From 2a96c2e513df48f2f1f595f8302bf6266c32a54f Mon Sep 17 00:00:00 2001 From: Bernard Kwok Date: Wed, 23 Jun 2021 16:30:22 -0400 Subject: [PATCH] v1.38.1_adsk Release (#1241) * Update changelog for recent work * Optimizations to shader translation and baking * Update changelog for recent work * Simplify material examples - Remove colorspace assignments on scalar images (only color values and images are affected by colorspace assignments). - Remove input bindings to default values. * Fixes. * Update JS bindings. * Unused var fix. * Add in new baking tests and bump to 1.38.1. Co-authored-by: Jonathan Stone --- CHANGELOG.md | 52 ++++- CMakeLists.txt | 2 +- .../standard_surface_brass_tiled.mtlx | 2 +- .../standard_surface_wood_tiled.mtlx | 2 +- .../usd_preview_surface_brass_tiled.mtlx | 4 +- .../usd_preview_surface_gold.mtlx | 2 - .../usd_preview_surface_plastic.mtlx | 4 - resources/Materials/TestSuite/_options.mtlx | 65 +++++++ .../JsMaterialXCore/JsMaterial.cpp | 6 +- source/MaterialXCore/Material.cpp | 148 +++++++-------- source/MaterialXCore/Material.h | 25 +-- source/MaterialXGenOgsXml/OgsFragment.cpp | 2 +- .../MaterialXGenShader/ShaderTranslator.cpp | 71 +++---- source/MaterialXGenShader/ShaderTranslator.h | 6 +- source/MaterialXGenShader/Util.cpp | 2 +- source/MaterialXRender/Image.cpp | 30 ++- source/MaterialXRender/Image.h | 6 + source/MaterialXRender/ImageHandler.cpp | 35 +++- source/MaterialXRender/ImageHandler.h | 21 ++- source/MaterialXRender/ShaderRenderer.cpp | 32 +--- source/MaterialXRender/ShaderRenderer.h | 66 +++---- source/MaterialXRenderGlsl/GLFramebuffer.cpp | 9 +- source/MaterialXRenderGlsl/GLFramebuffer.h | 6 +- .../MaterialXRenderGlsl/GLTextureHandler.cpp | 13 +- source/MaterialXRenderGlsl/GLTextureHandler.h | 6 +- source/MaterialXRenderGlsl/GlslRenderer.cpp | 43 +---- source/MaterialXRenderGlsl/GlslRenderer.h | 8 +- source/MaterialXRenderGlsl/TextureBaker.cpp | 177 +++++++++--------- source/MaterialXRenderGlsl/TextureBaker.h | 36 ++-- source/MaterialXRenderOsl/OslRenderer.cpp | 14 +- source/MaterialXRenderOsl/OslRenderer.h | 11 +- .../MaterialXGenShader/GenShaderUtil.cpp | 6 +- .../MaterialXRender/RenderUtil.cpp | 3 +- .../MaterialXRenderGlsl/RenderGlsl.cpp | 4 +- source/MaterialXView/Editor.cpp | 25 +-- source/MaterialXView/Viewer.cpp | 137 +++++++++----- source/MaterialXView/Viewer.h | 10 +- .../PyMaterialX/PyMaterialXRender/PyImage.cpp | 2 + .../PyMaterialXRender/PyImageHandler.cpp | 6 +- .../PyMaterialXRender/PyShaderRenderer.cpp | 77 +------- .../PyGLTextureHandler.cpp | 6 +- .../PyMaterialXRenderGlsl/PyGlslRenderer.cpp | 4 +- .../PyMaterialXRenderGlsl/PyTextureBaker.cpp | 2 - .../PyMaterialXRenderOsl/PyOslRenderer.cpp | 1 - 44 files changed, 611 insertions(+), 578 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1152bc0ec2..d321d235fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,15 +1,47 @@ # Change Log -## [1.38.1] Development +## [1.38.2_adsk] - Development -#### Added - - Unlit surface shader definitions - - Look / Lookgroup management (in progress) +- Javascript bindings for MaterialXCore, MaterialXFormat, MaterialXGenShader (wip) +- OCIO-v2 support (wip) +- SPIR-V support (wip) +- Token support (wip) -#### Changed: - - Runtime "single stage" data model / refactoring (in progress) - - Fix for Arnold conductor_bsdf implemetnation signature. - - Patch to allow "context" keyword for shaderrefs during upgrade path. Unsupported and will be removed. +## [1.38.1_adsk] - 2021-06-21 + +### Added +- Add support for local for numierc Value types. +- Addition of new unlit shader surface shader to standard pbr library. +- Add in new baking option to allow for baking to image using non-normalized texture coordinates. Required sometime for real-world unit mapping. +- Allow for "gamma" shader to be used instead of sRGB buffers. This allows for some color output transformation without relying on hardware SRGB buffers which are not support in MESA configurations. Gamma control is part of MaterialXView. +- Enable arm64 for macOS Xcode 12 CI build. + +### Changed +- Enhance hardware transparency check including handling when transparency is defined by an graph definition. +- Add token resolving to generic string resolvers. Fix code generation to use resolved values. +- Fixes so that derived targets can use units and color management properly. (Was not finding base class targets) +- Add requiresLighting() to shader generators to allow derived class to use this logic for custom direct lighting. +- Fixes to node definition "flattening" to take into account upstream nodegraphs, baking from surface shaders and handling baking when multiple children within a nodegraph are flattened. +- Minor MDL fix for texture pathing. +- Update unit test configuration to have more modular sets of options texture baing and wedging. + +## [1.38.1] - 2021-06-18 + +### Added +- Added support for shared library builds on Windows. +- Added support for 16-bit unsigned integer images in MaterialXRender. +- Added support for compound nodegraphs with user interfaces in shader and UI generation. +- Added headers for newly proposed MaterialX closures in OSL. +- Added a shader translation command to the viewer, assigned to the 'T' hotkey. + +### Changed +- Improved the memory efficiency of texture baking operations. +- Improved the compatibility of generated MDL code with Omniverse. +- Refactored image resolution logic into new methods ImageHandler\:\:getReferencedImages and MaterialX\:\:getMaxDimensions. +- Moved the viewer hotkey for GLSL code generation from 'S' to 'G' for consistency with other languages. + +### Fixed +- Fixed the Color3.asTuple and Color4.asTuple methods in Python ## [1.38.0_adsk](https://github.com/autodesk-forks/MaterialX/releases/tag/v1.38_adsk) - 2021-03-12 : Autodesk 1.38 release @@ -21,7 +53,7 @@ Updated the MaterialX library to the v1.38 specification. See the [v1.38 changelist](http://www.materialx.org/assets/MaterialX.v1.38.Changelist.pdf) for full details. -#### Added +### Added - Added support for the generalized 'layer' node in Physically Based Shading. - Added user controls for texture baking and wedge rendering in the [MaterialX Viewer](https://github.com/materialx/MaterialX/blob/main/documents/DeveloperGuide/Viewer.md). - Added support for Nvidia's Material Definition Language (MDL) in MaterialX code generation. @@ -30,7 +62,7 @@ Updated the MaterialX library to the v1.38 specification. See the [v1.38 change - Added viewer rendering to cloud-based tests in GitHub Actions. - Added support for Xcode 12. -#### Changed +### Changed - Updated the set of standard nodes to match the v1.38 specification, including significant improvements to the [Physically Based Shading](http://www.materialx.org/assets/MaterialX.v1.38.PBRSpec.pdf) nodes. - Replaced specialized Material elements with material nodes, allowing more flexible material definitions and more consistent traversal. - Unified the Input and Parameter element classes, simplifying the MaterialX API and client code. diff --git a/CMakeLists.txt b/CMakeLists.txt index 585ea6a6b9..2ee659a649 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -21,7 +21,7 @@ endif() project(MaterialX) set(MATERIALX_MAJOR_VERSION 1) set(MATERIALX_MINOR_VERSION 38) -set(MATERIALX_BUILD_VERSION 0) +set(MATERIALX_BUILD_VERSION 1) set(MATERIALX_LIBRARY_VERSION ${MATERIALX_MAJOR_VERSION}.${MATERIALX_MINOR_VERSION}.${MATERIALX_BUILD_VERSION}) cmake_minimum_required(VERSION 3.1) diff --git a/resources/Materials/Examples/StandardSurface/standard_surface_brass_tiled.mtlx b/resources/Materials/Examples/StandardSurface/standard_surface_brass_tiled.mtlx index 4c41e7747d..bc59bf172d 100644 --- a/resources/Materials/Examples/StandardSurface/standard_surface_brass_tiled.mtlx +++ b/resources/Materials/Examples/StandardSurface/standard_surface_brass_tiled.mtlx @@ -6,7 +6,7 @@ - + diff --git a/resources/Materials/Examples/StandardSurface/standard_surface_wood_tiled.mtlx b/resources/Materials/Examples/StandardSurface/standard_surface_wood_tiled.mtlx index d69e65ea9c..c030aa94ee 100644 --- a/resources/Materials/Examples/StandardSurface/standard_surface_wood_tiled.mtlx +++ b/resources/Materials/Examples/StandardSurface/standard_surface_wood_tiled.mtlx @@ -6,7 +6,7 @@ - + diff --git a/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_brass_tiled.mtlx b/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_brass_tiled.mtlx index 9613a1a5b8..59562e4ad0 100644 --- a/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_brass_tiled.mtlx +++ b/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_brass_tiled.mtlx @@ -6,7 +6,7 @@ - + @@ -14,8 +14,6 @@ - - diff --git a/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_gold.mtlx b/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_gold.mtlx index 5b119c0e1d..ecaf9914d3 100644 --- a/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_gold.mtlx +++ b/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_gold.mtlx @@ -2,8 +2,6 @@ - - diff --git a/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_plastic.mtlx b/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_plastic.mtlx index de2b3abe58..b6b4a8c4ae 100644 --- a/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_plastic.mtlx +++ b/resources/Materials/Examples/UsdPreviewSurface/usd_preview_surface_plastic.mtlx @@ -2,11 +2,7 @@ - - - - diff --git a/resources/Materials/TestSuite/_options.mtlx b/resources/Materials/TestSuite/_options.mtlx index 042f13b709..997763d0a4 100644 --- a/resources/Materials/TestSuite/_options.mtlx +++ b/resources/Materials/TestSuite/_options.mtlx @@ -146,5 +146,70 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/JsMaterialX/JsMaterialXCore/JsMaterial.cpp b/source/JsMaterialX/JsMaterialXCore/JsMaterial.cpp index d33444505e..68dc2e47b4 100644 --- a/source/JsMaterialX/JsMaterialXCore/JsMaterial.cpp +++ b/source/JsMaterialX/JsMaterialXCore/JsMaterial.cpp @@ -19,7 +19,7 @@ namespace mx = MaterialX; EMSCRIPTEN_BINDINGS(material) { ems::function("getShaderNodes", ems::optional_override([](mx::NodePtr materialNode) { - std::unordered_set set = mx::getShaderNodes(materialNode); + std::vector set = mx::getShaderNodes(materialNode); // Put all elements into an std::vector std::vector vec; vec.insert(vec.end(), set.begin(), set.end()); @@ -27,7 +27,7 @@ EMSCRIPTEN_BINDINGS(material) })); ems::function("getShaderNodes", ems::optional_override([](mx::NodePtr materialNode, const std::string& nodeType) { - std::unordered_set set = mx::getShaderNodes(materialNode, nodeType); + std::vector set = mx::getShaderNodes(materialNode, nodeType); // Put all elements into an std::vector std::vector vec; vec.insert(vec.end(), set.begin(), set.end()); @@ -36,7 +36,7 @@ EMSCRIPTEN_BINDINGS(material) ems::function("getShaderNodes", ems::optional_override([](mx::NodePtr materialNode, const std::string& nodeType, const std::string& target) { - std::unordered_set set = mx::getShaderNodes(materialNode, nodeType, target); + std::vector set = mx::getShaderNodes(materialNode, nodeType, target); // Put all elements into an std::vector std::vector vec; vec.insert(vec.end(), set.begin(), set.end()); diff --git a/source/MaterialXCore/Material.cpp b/source/MaterialXCore/Material.cpp index 37309b46bf..faf08e7b46 100644 --- a/source/MaterialXCore/Material.cpp +++ b/source/MaterialXCore/Material.cpp @@ -8,17 +8,71 @@ namespace MaterialX { -std::unordered_set getShaderNodes(NodePtr materialNode, const string& nodeType, const string& target) +vector getShaderNodes(NodePtr materialNode, const string& nodeType, const string& target) { - ElementPtr parent = materialNode->getParent(); - if (!parent) + vector shaderNodeVec; + std::set shaderNodeSet; + + vector inputs = materialNode->getActiveInputs(); + for (InputPtr input : inputs) { - throw Exception("Could not find a parent for material node '" + (materialNode ? materialNode->getNamePath() : EMPTY_STRING) + "'"); - } + // Scan for a node directly connected to the input. + // Note that this will handle traversing through interfacename associations. + NodePtr shaderNode = input->getConnectedNode(); + if (shaderNode && !shaderNodeSet.count(shaderNode)) + { + if (!nodeType.empty() && shaderNode->getType() != nodeType) + { + continue; + } + + if (!target.empty()) + { + NodeDefPtr nodeDef = shaderNode->getNodeDef(target); + if (!nodeDef) + { + continue; + } + } - std::unordered_set shaderNodes; + shaderNodeVec.push_back(shaderNode); + shaderNodeSet.insert(shaderNode); + } + else if (input->hasNodeGraphString()) + { + // Check upstream nodegraph connected to the input. + // If no explicit output name given then scan all outputs on the nodegraph. + ElementPtr parent = materialNode->getParent(); + NodeGraphPtr nodeGraph = parent->getChildOfType(input->getNodeGraphString()); + if (!nodeGraph) + { + continue; + } + vector outputs; + if (input->hasOutputString()) + { + outputs.push_back(nodeGraph->getOutput(input->getOutputString())); + } + else + { + outputs = nodeGraph->getOutputs(); + } + for (OutputPtr output : outputs) + { + NodePtr upstreamNode = output->getConnectedNode(); + if (upstreamNode && !shaderNodeSet.count(upstreamNode)) + { + if (!target.empty() && !upstreamNode->getNodeDef(target)) + { + continue; + } + shaderNodeVec.push_back(upstreamNode); + shaderNodeSet.insert(upstreamNode); + } + } + } + } - std::vector inputs = materialNode->getActiveInputs(); if (inputs.empty()) { // Try to find material nodes in the implementation graph if any. @@ -30,7 +84,7 @@ std::unordered_set getShaderNodes(NodePtr materialNode, const string& n if (impl->isA()) { NodeGraphPtr implGraph = impl->asA(); - for (auto defOutput : materialNodeDef->getOutputs()) + for (OutputPtr defOutput : materialNodeDef->getOutputs()) { if (defOutput->getType() == MATERIAL_TYPE_STRING) { @@ -46,10 +100,13 @@ std::unordered_set getShaderNodes(NodePtr materialNode, const string& n NodePtr upstreamNode = upstreamElem->asA(); if (upstreamNode && upstreamNode->getType() == MATERIAL_TYPE_STRING) { - std::unordered_set newShaderNodes = getShaderNodes(upstreamNode, nodeType, target); - if (!newShaderNodes.empty()) + for (NodePtr shaderNode : getShaderNodes(upstreamNode, nodeType, target)) { - shaderNodes.insert(newShaderNodes.begin(), newShaderNodes.end()); + if (!shaderNodeSet.count(shaderNode)) + { + shaderNodeVec.push_back(shaderNode); + shaderNodeSet.insert(shaderNode); + } } } } @@ -59,75 +116,10 @@ std::unordered_set getShaderNodes(NodePtr materialNode, const string& n } } - for (const InputPtr& input : inputs) - { - // Scan for a node directly connected to the input. - // Note that this will handle traversing through interfacename associations. - // - NodePtr shaderNode = input->getConnectedNode(); - if (shaderNode) - { - if (!nodeType.empty() && shaderNode->getType() != nodeType) - { - continue; - } - - if (!target.empty()) - { - NodeDefPtr nodeDef = shaderNode->getNodeDef(target); - if (!nodeDef) - { - continue; - } - } - shaderNodes.insert(shaderNode); - } - - // Check upstream nodegraph connected to the input. - // If no explicit output name given then scan all outputs on the nodegraph. - // - else - { - const string& inputGraph = input->getNodeGraphString(); - if (!inputGraph.empty()) - { - NodeGraphPtr nodeGraph = parent->getChildOfType(inputGraph); - if (nodeGraph) - { - const string& nodeGraphOutput = input->getOutputString(); - std::vector outputs; - if (!nodeGraphOutput.empty()) - { - outputs.push_back(nodeGraph->getOutput(nodeGraphOutput)); - } - else - { - outputs = nodeGraph->getOutputs(); - } - for (OutputPtr output : outputs) - { - NodePtr upstreamNode = output->getConnectedNode(); - if (upstreamNode) - { - if (!target.empty()) - { - NodeDefPtr nodeDef = upstreamNode->getNodeDef(target); - if (!nodeDef) - { - continue; - } - } - shaderNodes.insert(upstreamNode); - } - } - } - } - } - } - return shaderNodes; + return shaderNodeVec; } -vector getConnectedOutputs(const NodePtr& node) +vector getConnectedOutputs(NodePtr node) { vector outputVec; std::set outputSet; diff --git a/source/MaterialXCore/Material.h b/source/MaterialXCore/Material.h index 0d5a1162d4..227cb3d9f7 100644 --- a/source/MaterialXCore/Material.h +++ b/source/MaterialXCore/Material.h @@ -7,32 +7,27 @@ #define MATERIALX_MATERIAL_H /// @file -/// Material element subclasses +/// Material node helper functions #include -#include #include -#include - -#include namespace MaterialX { -/// Return a vector of all nodes connected to a Material node's inputs. The default behavior -/// is to return connected surface shader nodes. -/// @param materialNode Node to examine. -/// @param nodeType Type of node to return. If an empty string is specified then -/// all node types are returned. The default argument value is to return surface shaders. -/// @param target Target attribute filter for nodes to return. The default argument value is an empty string -/// indicating to include nodes which match any target. -MX_CORE_API std::unordered_set getShaderNodes(NodePtr materialNode, +/// Return a vector of all shader nodes connected to the given material node's inputs, +/// filtered by the given shader type and target. By default, all surface shader nodes +/// are returned. +/// @param materialNode The node to examine. +/// @param nodeType THe shader node type to return. Defaults to the surface shader type. +/// @param target An optional target name, which will be used to filter the returned nodes. +MX_CORE_API vector getShaderNodes(NodePtr materialNode, const string& nodeType = SURFACE_SHADER_TYPE_STRING, const string& target = EMPTY_STRING); -/// Return a vector of all outputs that this nodes inputs are connected to. -MX_CORE_API vector getConnectedOutputs(const NodePtr& node); +/// Return a vector of all outputs connected to the given node's inputs. +MX_CORE_API vector getConnectedOutputs(NodePtr node); } // namespace MaterialX diff --git a/source/MaterialXGenOgsXml/OgsFragment.cpp b/source/MaterialXGenOgsXml/OgsFragment.cpp index 3a3c084b39..47adf7154e 100644 --- a/source/MaterialXGenOgsXml/OgsFragment.cpp +++ b/source/MaterialXGenOgsXml/OgsFragment.cpp @@ -40,7 +40,7 @@ class GlslGeneratorWrapperBase mx::NodePtr outputNode = element->asA(); if (outputNode->getType() == mx::MATERIAL_TYPE_STRING) { - std::unordered_set shaderNodes = + std::vector shaderNodes = mx::getShaderNodes(outputNode, mx::SURFACE_SHADER_TYPE_STRING); if (!shaderNodes.empty()) { diff --git a/source/MaterialXGenShader/ShaderTranslator.cpp b/source/MaterialXGenShader/ShaderTranslator.cpp index a9e9edb5f4..ba2d17c754 100644 --- a/source/MaterialXGenShader/ShaderTranslator.cpp +++ b/source/MaterialXGenShader/ShaderTranslator.cpp @@ -6,7 +6,6 @@ #include #include -#include namespace MaterialX { @@ -15,10 +14,6 @@ namespace MaterialX // ShaderTranslator methods // -ShaderTranslator::ShaderTranslator() -{ -} - void ShaderTranslator::connectTranslationInputs(NodePtr shader, NodeDefPtr translationNodeDef) { vector origInputs = shader->getInputs(); @@ -27,34 +22,45 @@ void ShaderTranslator::connectTranslationInputs(NodePtr shader, NodeDefPtr trans { if (translationNodeDef->getInput(shaderInput->getName())) { - OutputPtr output = shaderInput->getConnectedOutput(); - if (output) + InputPtr input = _translationNode->addInput(shaderInput->getName(), shaderInput->getType()); + + OutputPtr connectedOutput = shaderInput->getConnectedOutput(); + if (connectedOutput) { - InputPtr input = _translationNode->addInput(shaderInput->getName(), shaderInput->getType()); - input->setConnectedNode(_graph->getNode(output->getNodeName())); - if (!shaderInput->getColorSpace().empty()) + NodePtr connectedNode = connectedOutput->getConnectedNode(); + + // Nodes with world-space outputs are skipped, with translation being applied to + // the node directly upstream. + NodePtr worldSpaceNode = connectsToWorldSpaceNode(connectedOutput); + if (worldSpaceNode) { - input->setColorSpace(shaderInput->getColorSpace()); + NodePtr upstreamNode = worldSpaceNode->getConnectedNode("in"); + if (upstreamNode) + { + connectedNode = upstreamNode; + } } - origOutputs.insert(output); + + input->setConnectedNode(connectedNode); + origOutputs.insert(connectedOutput); } - else if (!shaderInput->getValueString().empty()) + else if (shaderInput->hasValueString()) { - InputPtr input = _translationNode->addInput(shaderInput->getName(), shaderInput->getType()); input->setValueString(shaderInput->getValueString()); - if (!shaderInput->getColorSpace().empty()) - { - input->setColorSpace(shaderInput->getColorSpace()); - } - if (!shaderInput->getUnit().empty()) - { - input->setUnit(shaderInput->getUnit()); - input->setUnitType(shaderInput->getUnitType()); - } } else { - throw Exception("No associated output with " + shaderInput->getName()); + throw Exception("Shader input has no associated output or value " + shaderInput->getName()); + } + + if (shaderInput->hasColorSpace()) + { + input->setColorSpace(shaderInput->getColorSpace()); + } + if (shaderInput->hasUnit()) + { + input->setUnit(shaderInput->getUnit()); + input->setUnitType(shaderInput->getUnitType()); } } } @@ -104,16 +110,14 @@ void ShaderTranslator::connectTranslationOutputs(NodePtr shader) if (nodeInput && nodeInput->hasInterfaceName()) { InputPtr interfaceInput = _translationNode->getInput(nodeInput->getInterfaceName()); - if (interfaceInput) + NodePtr sourceNode = interfaceInput ? interfaceInput->getConnectedNode() : nullptr; + if (!sourceNode) { - NodePtr sourceNode = interfaceInput->getConnectedNode(); - if (sourceNode) - { - translatedStreamNode = _graph->addNode(worldSpaceNode->getCategory(), worldSpaceNode->getName(), worldSpaceNode->getType()); - translatedStreamNode->setConnectedNode("in", sourceNode); - translatedStreamOutput = EMPTY_STRING; - } + continue; } + translatedStreamNode = _graph->addNode(worldSpaceNode->getCategory(), worldSpaceNode->getName(), worldSpaceNode->getType()); + translatedStreamNode->setConnectedNode("in", sourceNode); + translatedStreamOutput = EMPTY_STRING; } } @@ -193,8 +197,7 @@ void ShaderTranslator::translateAllMaterials(DocumentPtr doc, string destCategor { continue; } - std::unordered_set shaderNodes = getShaderNodes(materialNode); - for (auto shaderNode : shaderNodes) + for (NodePtr shaderNode : getShaderNodes(materialNode)) { translateShader(shaderNode, destCategory); } diff --git a/source/MaterialXGenShader/ShaderTranslator.h b/source/MaterialXGenShader/ShaderTranslator.h index 5cea52ab19..206de447f4 100644 --- a/source/MaterialXGenShader/ShaderTranslator.h +++ b/source/MaterialXGenShader/ShaderTranslator.h @@ -7,7 +7,7 @@ #define MATERIALX_SHADERTRANSLATOR_H #include -#include + #include #include @@ -26,7 +26,7 @@ class MX_GENSHADER_API ShaderTranslator return ShaderTranslatorPtr(new ShaderTranslator()); } - /// Translate a shader to the destination shading model. + /// Translate a shader node to the destination shading model. void translateShader(NodePtr shader, const string& destCategory); /// Translate each material in the input document to the destination @@ -34,7 +34,7 @@ class MX_GENSHADER_API ShaderTranslator void translateAllMaterials(DocumentPtr doc, string destShader); protected: - ShaderTranslator(); + ShaderTranslator() { } // Connect translation node inputs from the original shader void connectTranslationInputs(NodePtr shader, NodeDefPtr translationNodeDef); diff --git a/source/MaterialXGenShader/Util.cpp b/source/MaterialXGenShader/Util.cpp index 1c84058ba9..40431c49f7 100644 --- a/source/MaterialXGenShader/Util.cpp +++ b/source/MaterialXGenShader/Util.cpp @@ -346,7 +346,7 @@ void findRenderableMaterialNodes(ConstDocumentPtr doc, { // Scan for any upstream shader outputs and put them on the "processed" list // if we don't want to consider them for rendering. - std::unordered_set shaderNodes = getShaderNodes(material); + vector shaderNodes = getShaderNodes(material); if (!shaderNodes.empty()) { // Push the material node only once if any shader nodes are found diff --git a/source/MaterialXRender/Image.cpp b/source/MaterialXRender/Image.cpp index 8d074e3112..2dee9f3630 100644 --- a/source/MaterialXRender/Image.cpp +++ b/source/MaterialXRender/Image.cpp @@ -22,13 +22,7 @@ ImagePtr createUniformImage(unsigned int width, unsigned int height, unsigned in { ImagePtr image = Image::create(width, height, channelCount, baseType); image->createResourceBuffer(); - for (unsigned int y = 0; y < image->getHeight(); y++) - { - for (unsigned int x = 0; x < image->getWidth(); x++) - { - image->setTexelColor(x, y, color); - } - } + image->setUniformColor(color); return image; } @@ -79,6 +73,17 @@ ImagePtr createImageStrip(const vector& imageVec) return imageStrip; } +std::pair getMaxDimensions(const vector& imageVec) +{ + std::pair maxSize(0, 0); + for (ImagePtr image : imageVec) + { + maxSize.first = std::max(maxSize.first, image->getWidth()); + maxSize.second = std::max(maxSize.second, image->getHeight()); + } + return maxSize; +} + // // Image methods // @@ -328,6 +333,17 @@ bool Image::isUniformColor(Color4* uniformColor) return true; } +void Image::setUniformColor(const Color4& color) +{ + for (unsigned int y = 0; y < getHeight(); y++) + { + for (unsigned int x = 0; x < getWidth(); x++) + { + setTexelColor(x, y, color); + } + } +} + ImagePtr Image::applyBoxBlur() { ImagePtr blurImage = Image::create(getWidth(), getHeight(), getChannelCount(), getBaseType()); diff --git a/source/MaterialXRender/Image.h b/source/MaterialXRender/Image.h index a8bb83feb6..0a0f71c618 100644 --- a/source/MaterialXRender/Image.h +++ b/source/MaterialXRender/Image.h @@ -124,6 +124,9 @@ class MX_RENDER_API Image /// @name Image Processing /// @{ + /// Set all texels of this image to a uniform color. + void setUniformColor(const Color4& color); + /// Apply a 3x3 box blur to this image, returning a new blurred image. ImagePtr applyBoxBlur(); @@ -206,6 +209,9 @@ MX_RENDER_API ImagePtr createUniformImage(unsigned int width, unsigned int heigh /// Create a horizontal image strip from a vector of images with identical resolutions and formats. MX_RENDER_API ImagePtr createImageStrip(const vector& imageVec); +/// Compute the maximum width and height of all images in the given vector. +MX_RENDER_API std::pair getMaxDimensions(const vector& imageVec); + } // namespace MaterialX #endif diff --git a/source/MaterialXRender/ImageHandler.cpp b/source/MaterialXRender/ImageHandler.cpp index 4fa3007055..f7664062a7 100644 --- a/source/MaterialXRender/ImageHandler.cpp +++ b/source/MaterialXRender/ImageHandler.cpp @@ -93,7 +93,7 @@ bool ImageHandler::saveImage(const FilePath& filePath, return false; } - FilePath foundFilePath = _searchPath.find(filePath); + FilePath foundFilePath = _searchPath.find(filePath); if (foundFilePath.isEmpty()) { return false; @@ -175,6 +175,30 @@ void ImageHandler::releaseRenderResources(ImagePtr) { } +ImageVec ImageHandler::getReferencedImages(DocumentPtr doc) +{ + ImageVec imageVec; + for (ElementPtr elem : doc->traverseTree()) + { + if (elem->getActiveSourceUri() != doc->getSourceUri()) + { + continue; + } + + NodePtr node = elem->asA(); + InputPtr file = node ? node->getInput("file") : nullptr; + if (file) + { + ImagePtr image = acquireImage(file->getResolvedValueString()); + if (image && image != _invalidImage) + { + imageVec.push_back(image); + } + } + } + return imageVec; +} + ImagePtr ImageHandler::loadImage(const FilePath& filePath) { string extension = stringToLower(filePath.getExtension()); @@ -247,15 +271,6 @@ ImagePtr ImageHandler::getCachedImage(const FilePath& filePath) return nullptr; } -void ImageHandler::clearImageCache() -{ - for (auto iter : _imageCache) - { - releaseRenderResources(iter.second); - } - _imageCache.clear(); -} - // // ImageSamplingProperties methods // diff --git a/source/MaterialXRender/ImageHandler.h b/source/MaterialXRender/ImageHandler.h index 068495653e..9956ab5d63 100644 --- a/source/MaterialXRender/ImageHandler.h +++ b/source/MaterialXRender/ImageHandler.h @@ -14,7 +14,7 @@ #include -#include +#include namespace MaterialX { @@ -214,12 +214,17 @@ class MX_RENDER_API ImageHandler /// Create rendering resources for the given image. virtual bool createRenderResources(ImagePtr image, bool generateMipMaps); - /// Release rendering resources for the given image. - virtual void releaseRenderResources(ImagePtr image); + /// Release rendering resources for the given image, or for all cached images + /// if no image pointer is specified. + virtual void releaseRenderResources(ImagePtr image = nullptr); - /// Clear the contents of the image cache, first releasing any - /// render resources associated with each image. - void clearImageCache(); + /// Clear the contents of the image cache, first releasing any render + /// resources associated with cached images. + void clearImageCache() + { + releaseRenderResources(); + _imageCache.clear(); + } /// Return a fallback image with zeroes in all channels. ImagePtr getZeroImage() const @@ -234,6 +239,10 @@ class MX_RENDER_API ImageHandler return _invalidImage; } + /// Acquire all images referenced by the given document, and return the + /// images in a vector. + ImageVec getReferencedImages(DocumentPtr doc); + protected: // Protected constructor. ImageHandler(ImageLoaderPtr imageLoader); diff --git a/source/MaterialXRender/ShaderRenderer.cpp b/source/MaterialXRender/ShaderRenderer.cpp index 8dc8c0a146..798a8c8dd5 100644 --- a/source/MaterialXRender/ShaderRenderer.cpp +++ b/source/MaterialXRender/ShaderRenderer.cpp @@ -4,38 +4,20 @@ // #include -#include namespace MaterialX { -ImageVec ShaderRenderer::getReferencedImages(const ShaderPtr& shader) +void ShaderRenderer::createProgram(ShaderPtr) { - if (!shader && !_imageHandler) - { - return {}; - } - - ImageVec imageList; +} - // Prefetch all required images for Public Uniforms and query their dimensions. - // Since Images are cached by ImageHandler, they will be reused during bindTextures - const ShaderStage& stage = shader->getStage(Stage::PIXEL); - const VariableBlock& block = stage.getUniformBlock(HW::PUBLIC_UNIFORMS); - for (const auto& uniform : block.getVariableOrder()) - { - if (uniform->getType() == Type::FILENAME) - { - const string fileName(uniform->getValue()->getValueString()); - ImagePtr image = _imageHandler->acquireImage(fileName); - if (image) - { - imageList.push_back(image); - } - } - } +void ShaderRenderer::createProgram(const StageMap&) +{ +} - return imageList; +void ShaderRenderer::setSize(unsigned int, unsigned int) +{ } } // namespace MaterialX diff --git a/source/MaterialXRender/ShaderRenderer.h b/source/MaterialXRender/ShaderRenderer.h index ead95811b2..69cb6bda1c 100644 --- a/source/MaterialXRender/ShaderRenderer.h +++ b/source/MaterialXRender/ShaderRenderer.h @@ -24,7 +24,7 @@ namespace MaterialX using ShaderRendererPtr = std::shared_ptr; /// @class ShaderRenderer -/// Helper class for rendering generated shader code to produce images. +/// Base class for renderers that generate shader code to produce images. class MX_RENDER_API ShaderRenderer { public: @@ -37,53 +37,52 @@ class MX_RENDER_API ShaderRenderer /// @name Setup /// @{ - /// Renderer initialization - virtual void initialize() = 0; + /// Initialize the renderer. + virtual void initialize() { } - /// Set image handler to use for image load and save - /// @param imageHandler Handler used to save image + /// Set the image handler used by this renderer for image I/O. void setImageHandler(ImageHandlerPtr imageHandler) { _imageHandler = imageHandler; } - /// Get image handler - /// @return Shared pointer to an image handler + /// Return the image handler. ImageHandlerPtr getImageHandler() const { return _imageHandler; } - /// Set light handler to use for light bindings - /// @param lightHandler Handler used for lights + /// Set the light handler used by this renderer for light bindings. void setLightHandler(LightHandlerPtr lightHandler) { _lightHandler = lightHandler; } - /// Get light handler - /// @return Shared pointer to a light handler + /// Return the light handler. LightHandlerPtr getLightHandler() const { return _lightHandler; } - /// Get geometry handler - /// @return Reference to a geometry handler + /// Set the geometry handler. + void setGeometryHandler(GeometryHandlerPtr geometryHandler) + { + _geometryHandler = geometryHandler; + } + + /// Return the geometry handler. GeometryHandlerPtr getGeometryHandler() const { return _geometryHandler; } - /// Set viewing utilities handler. - /// @param viewHandler Handler to use + /// Set the view handler. void setViewHandler(ViewHandlerPtr viewHandler) { _viewHandler = viewHandler; } - /// Get viewing utilities handler - /// @return Shared pointer to a view utilities handler + /// Return the view handler. ViewHandlerPtr getViewHandler() const { return _viewHandler; @@ -93,40 +92,35 @@ class MX_RENDER_API ShaderRenderer /// @name Rendering /// @{ - /// Create program based on an input shader - /// @param shader Input Shader - virtual void createProgram(ShaderPtr shader) = 0; + /// Create program based on an input shader. + virtual void createProgram(ShaderPtr shader); /// Create program based on shader stage source code. /// @param stages Map of name and source code for the shader stages. - virtual void createProgram(const StageMap& stages) = 0; + virtual void createProgram(const StageMap& stages); - /// Validate inputs for the program - virtual void validateInputs() = 0; + /// Validate inputs for the program. + virtual void validateInputs() { } - /// Set the size of the rendered image - virtual void setSize(unsigned int /*width*/, unsigned int /*height*/) = 0; + /// Set the size of the rendered image. + virtual void setSize(unsigned int width, unsigned int height); - /// Render the current program to produce an image - virtual void render() = 0; + /// Render the current program to produce an image. + virtual void render() { } /// @} /// @name Utilities /// @{ - /// Capture the current contents of the off-screen hardware buffer as an image. - virtual ImagePtr captureImage() = 0; - - /// Save the current contents of the off-screen hardware buffer to disk. - virtual void saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip) = 0; - - /// Load images referenced by shader program and return list of images loaded - virtual ImageVec getReferencedImages (const ShaderPtr& shader); + /// Capture the current rendered output as an image. + virtual ImagePtr captureImage(ImagePtr image = nullptr) + { + return nullptr; + } /// @} protected: - // Protected constructor ShaderRenderer() : _width(0), _height(0), diff --git a/source/MaterialXRenderGlsl/GLFramebuffer.cpp b/source/MaterialXRenderGlsl/GLFramebuffer.cpp index 45aded6a61..855f38d3d1 100644 --- a/source/MaterialXRenderGlsl/GLFramebuffer.cpp +++ b/source/MaterialXRenderGlsl/GLFramebuffer.cpp @@ -185,10 +185,13 @@ void GLFramebuffer::unbind() glDrawBuffer(GL_NONE); } -ImagePtr GLFramebuffer::createColorImage() +ImagePtr GLFramebuffer::getColorImage(ImagePtr image) { - ImagePtr image = Image::create(_width, _height, _channelCount, _baseType); - image->createResourceBuffer(); + if (!image) + { + image = Image::create(_width, _height, _channelCount, _baseType); + image->createResourceBuffer(); + } int glType, glFormat, glInternalFormat; GLTextureHandler::mapTextureFormatToGL(_baseType, _channelCount, false, glType, glFormat, glInternalFormat); diff --git a/source/MaterialXRenderGlsl/GLFramebuffer.h b/source/MaterialXRenderGlsl/GLFramebuffer.h index 763c7ce2b4..586e83de6b 100644 --- a/source/MaterialXRenderGlsl/GLFramebuffer.h +++ b/source/MaterialXRenderGlsl/GLFramebuffer.h @@ -66,8 +66,10 @@ class MX_RENDERGLSL_API GLFramebuffer return _depthTexture; } - /// Create an image from our color texture. - ImagePtr createColorImage(); + /// Return the color data of this framebuffer as an image. + /// If an input image is provided, it will be used to store the color data; + /// otherwise a new image of the required format will be created. + ImagePtr getColorImage(ImagePtr image = nullptr); /// Blit our color texture to the back buffer. void blit(); diff --git a/source/MaterialXRenderGlsl/GLTextureHandler.cpp b/source/MaterialXRenderGlsl/GLTextureHandler.cpp index 2d4f63a0b5..a020ae1f3a 100644 --- a/source/MaterialXRenderGlsl/GLTextureHandler.cpp +++ b/source/MaterialXRenderGlsl/GLTextureHandler.cpp @@ -133,7 +133,18 @@ bool GLTextureHandler::createRenderResources(ImagePtr image, bool generateMipMap void GLTextureHandler::releaseRenderResources(ImagePtr image) { - if (!image || image->getResourceId() == GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID) + if (!image) + { + for (auto iter : _imageCache) + { + if (iter.second) + { + releaseRenderResources(iter.second); + } + } + return; + } + if (image->getResourceId() == GlslProgram::UNDEFINED_OPENGL_RESOURCE_ID) { return; } diff --git a/source/MaterialXRenderGlsl/GLTextureHandler.h b/source/MaterialXRenderGlsl/GLTextureHandler.h index d0a8332562..360cff695f 100644 --- a/source/MaterialXRenderGlsl/GLTextureHandler.h +++ b/source/MaterialXRenderGlsl/GLTextureHandler.h @@ -40,8 +40,9 @@ class MX_RENDERGLSL_API GLTextureHandler : public ImageHandler /// Create rendering resources for the given image. bool createRenderResources(ImagePtr image, bool generateMipMaps) override; - // Release rendering resources for the given image. - void releaseRenderResources(ImagePtr image) override; + /// Release rendering resources for the given image, or for all cached images + /// if no image pointer is specified. + void releaseRenderResources(ImagePtr image = nullptr) override; /// Return the bound texture location for a given resource int getBoundTextureLocation(unsigned int resourceId); @@ -52,6 +53,7 @@ class MX_RENDERGLSL_API GLTextureHandler : public ImageHandler /// Utility to map a filter type enumeration to an OpenGL filter type static int mapFilterTypeToGL(ImageSamplingProperties::FilterType filterTypeEnum, bool enableMipmaps); + /// Utility to map generic texture properties to OpenGL texture formats. static void mapTextureFormatToGL(Image::BaseType baseType, unsigned int channelCount, bool srgb, int& glType, int& glFormat, int& glInternalFormat); diff --git a/source/MaterialXRenderGlsl/GlslRenderer.cpp b/source/MaterialXRenderGlsl/GlslRenderer.cpp index d21106369e..81745b54b3 100644 --- a/source/MaterialXRenderGlsl/GlslRenderer.cpp +++ b/source/MaterialXRenderGlsl/GlslRenderer.cpp @@ -181,7 +181,7 @@ void GlslRenderer::setSize(unsigned int width, unsigned int height) } else { - _frameBuffer = GLFramebuffer::create(width, height, 4, Image::BaseType::UINT8); + _frameBuffer = GLFramebuffer::create(width, height, 4, _baseType); } _width = width; _height = height; @@ -290,7 +290,7 @@ void GlslRenderer::render() _frameBuffer->unbind(); } -ImagePtr GlslRenderer::captureImage() +ImagePtr GlslRenderer::captureImage(ImagePtr image) { StringVec errors; const string errorType("GLSL image capture error."); @@ -301,44 +301,7 @@ ImagePtr GlslRenderer::captureImage() throw ExceptionShaderRenderError(errorType, errors); } - return _frameBuffer->createColorImage(); -} - -void GlslRenderer::saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip) -{ - StringVec errors; - const string errorType("GLSL image save error."); - - if (!_imageHandler->saveImage(filePath, image, verticalFlip)) - { - errors.push_back("Failed to save to file:" + filePath.asString()); - throw ExceptionShaderRenderError(errorType, errors); - } -} - -ImageVec GlslRenderer::getReferencedImages(const ShaderPtr& /*shader*/) -{ - ImageVec imageList; - const GlslProgram::InputMap& uniformList = _program->getUniformsList(); - for (const auto& uniform : uniformList) - { - GLenum uniformType = uniform.second->gltype; - GLint uniformLocation = uniform.second->location; - if (uniformLocation >= 0 && uniformType >= GL_SAMPLER_1D && uniformType <= GL_SAMPLER_CUBE) - { - const string fileName(uniform.second->value ? uniform.second->value->getValueString() : ""); - if (fileName != HW::ENV_RADIANCE && - fileName != HW::ENV_IRRADIANCE) - { - ImagePtr image = _imageHandler->acquireImage(fileName); - if (image) - { - imageList.push_back(image); - } - } - } - } - return imageList; + return _frameBuffer->getColorImage(image); } void GlslRenderer::drawScreenSpaceQuad(const Vector2& uvMin, const Vector2& uvMax) diff --git a/source/MaterialXRenderGlsl/GlslRenderer.h b/source/MaterialXRenderGlsl/GlslRenderer.h index 03ea986549..9fefc84163 100644 --- a/source/MaterialXRenderGlsl/GlslRenderer.h +++ b/source/MaterialXRenderGlsl/GlslRenderer.h @@ -86,13 +86,7 @@ class MX_RENDERGLSL_API GlslRenderer : public ShaderRenderer /// @{ /// Capture the current contents of the off-screen hardware buffer as an image. - ImagePtr captureImage() override; - - /// Save the current contents of the off-screen hardware buffer to disk. - void saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip) override; - - /// Load images referenced by shader program and return list of images loaded - ImageVec getReferencedImages(const ShaderPtr& shader) override; + ImagePtr captureImage(ImagePtr image = nullptr) override; /// Return the GL frame buffer. GLFrameBufferPtr getFrameBuffer() const diff --git a/source/MaterialXRenderGlsl/TextureBaker.cpp b/source/MaterialXRenderGlsl/TextureBaker.cpp index 68a4967fa9..a3bb2c9592 100644 --- a/source/MaterialXRenderGlsl/TextureBaker.cpp +++ b/source/MaterialXRenderGlsl/TextureBaker.cpp @@ -65,7 +65,6 @@ TextureBaker::TextureBaker(unsigned int width, unsigned int height, Image::BaseT _bakedGraphName("NG_baked"), _bakedGeomInfoName("GI_baked"), _outputStream(&std::cout), - _autoTextureResolution(false), _hashImageNames(false), _generator(GlslShaderGenerator::create()) { @@ -90,7 +89,19 @@ TextureBaker::TextureBaker(unsigned int width, unsigned int height, Image::BaseT #endif _colorSpace = LIN_REC709; } + + // Initialize our base renderer. initialize(); + + // Initialize our image handler. + _imageHandler = GLTextureHandler::create(StbImageLoader::create()); +#if MATERIALX_BUILD_OIIO + _imageHandler->addLoader(OiioImageLoader::create()); +#endif + + // Create our dedicated frame capture image. + _frameCaptureImage = Image::create(width, height, 4, baseType); + _frameCaptureImage->createResourceBuffer(); } FilePath TextureBaker::generateTextureFilename(OutputPtr output, const string& shaderName, const string& udim) @@ -109,6 +120,25 @@ FilePath TextureBaker::generateTextureFilename(OutputPtr output, const string& s return FilePath(bakedImageName + "." + _extension); } +bool TextureBaker::writeBakedImage(const BakedImage& baked, ImagePtr image) +{ + if (!_imageHandler->saveImage(baked.filename, image, true)) + { + if (_outputStream) + { + *_outputStream << "Failed to write baked image: " << baked.filename.asString() << std::endl; + } + return false; + } + + if (_outputStream) + { + *_outputStream << "Wrote baked image: " << baked.filename.asString() << std::endl; + } + + return true; +} + void TextureBaker::bakeShaderInputs(NodePtr material, NodePtr shader, GenContext& context, const string& udim) { _material = material; @@ -152,48 +182,34 @@ void TextureBaker::bakeGraphOutput(OutputPtr output, GenContext& context, const ShaderPtr shader = _generator->generate("BakingShader", output, context); createProgram(shader); - if (_autoTextureResolution) - { - GlslProgramPtr program = getProgram(); - GLFrameBufferPtr framebuffer = getFrameBuffer(); - unsigned int bakedTextureHeight = 0; - unsigned int bakedTextureWidth = 0; - bool requiresResize = false; - - // Prefetch all required images and query their dimensions. - // Since Images are cached by ImageHandler, they will be reused during bindTextures - ImageVec imageList = getReferencedImages(shader); - for (const auto& image : imageList) - { - const unsigned int imageHeight = image->getHeight(); - const unsigned int imageWidth = image->getWidth(); - bakedTextureHeight = imageHeight > bakedTextureHeight ? imageHeight : bakedTextureHeight; - bakedTextureWidth = imageWidth > bakedTextureWidth ? imageWidth : bakedTextureWidth; - requiresResize = true; - } - - if (requiresResize) - { - framebuffer->resize(bakedTextureWidth, bakedTextureHeight); - } - else - { - // Ensure that original size is restored. - framebuffer->resize(_width, _height); - } - } - bool encodeSrgb = _colorSpace == SRGB_TEXTURE && (output->getType() == "color3" || output->getType() == "color4"); getFrameBuffer()->setEncodeSrgb(encodeSrgb); + // Render and capture the requested image. auto textureSpaceRange = getTextureSpace(); renderTextureSpace(textureSpaceRange.first, textureSpaceRange.second); + captureImage(_frameCaptureImage); + // Construct a baked image record. BakedImage baked; - baked.image = captureImage(); baked.filename = texturefilepath; + if (_averageImages) + { + baked.uniformColor = _frameCaptureImage->getAverageColor(); + baked.isUniform = true; + } + else if (_frameCaptureImage->isUniformColor(&baked.uniformColor)) + { + baked.isUniform = true; + } _bakedImageMap[output].push_back(baked); + + // Write non-uniform images to disk. + if (!baked.isUniform) + { + writeBakedImage(baked, _frameCaptureImage); + } } void TextureBaker::optimizeBakedTextures(NodePtr shader) @@ -203,30 +219,19 @@ void TextureBaker::optimizeBakedTextures(NodePtr shader) return; } - // Check for uniform images. + // Check for fully uniform outputs. for (auto& pair : _bakedImageMap) { bool outputIsUniform = true; for (BakedImage& baked : pair.second) { - if (_averageImages) - { - baked.uniformColor = baked.image->getAverageColor(); - baked.isUniform = true; - } - else if (baked.image->isUniformColor(&baked.uniformColor)) - { - baked.image = createUniformImage(4, 4, baked.image->getChannelCount(), baked.image->getBaseType(), baked.uniformColor); - baked.isUniform = true; - } - else + if (!baked.isUniform || baked.uniformColor != pair.second[0].uniformColor) { outputIsUniform = false; + continue; } } - - // Add a constant for each uniform if optimizing out uniforms - if (outputIsUniform && _optimizeConstants) + if (outputIsUniform) { BakedConstant bakedConstant; bakedConstant.color = pair.second[0].uniformColor; @@ -234,12 +239,6 @@ void TextureBaker::optimizeBakedTextures(NodePtr shader) } } - // Remove uniform outputs from the baked image map. - for (auto& pair : _bakedConstantMap) - { - _bakedImageMap.erase(pair.first); - } - // Check for uniform outputs at their default values. NodeDefPtr shaderNodeDef = shader->getNodeDef(); if (shaderNodeDef) @@ -254,7 +253,8 @@ void TextureBaker::optimizeBakedTextures(NodePtr shader) { Color4 uniformColor = _bakedConstantMap[output].color; string uniformColorString = getValueStringFromColor(uniformColor, input->getType()); - if (uniformColorString == input->getValueString()) + string defaultValueString = input->hasValue() ? input->getValue()->getValueString() : EMPTY_STRING; + if (uniformColorString == defaultValueString) { _bakedConstantMap[output].isDefault = true; } @@ -262,6 +262,15 @@ void TextureBaker::optimizeBakedTextures(NodePtr shader) } } } + + // Remove baked images that have been replaced by constant values. + for (auto& pair : _bakedConstantMap) + { + if (pair.second.isDefault || _optimizeConstants || _averageImages) + { + _bakedImageMap.erase(pair.first); + } + } } DocumentPtr TextureBaker::bakeMaterial(NodePtr shader, const StringVec& udimSet) @@ -319,12 +328,21 @@ DocumentPtr TextureBaker::bakeMaterial(NodePtr shader, const StringVec& udimSet) // Create and connect inputs on the new shader node. for (ValueElementPtr valueElem : shader->getChildrenOfType()) { - // Get source and destination inputs + // Get the source input and its connected output. InputPtr sourceInput = valueElem->asA(); if (!sourceInput) { continue; } + OutputPtr output = sourceInput->getConnectedOutput(); + + // Skip uniform outputs at their default values. + if (output && _bakedConstantMap.count(output) && _bakedConstantMap[output].isDefault) + { + continue; + } + + // Find or create the baked input. const std::string& sourceName = sourceInput->getName(); const std::string& sourceType = sourceInput->getType(); InputPtr bakedInput = bakedShader->getInput(sourceName); @@ -333,15 +351,9 @@ DocumentPtr TextureBaker::bakeMaterial(NodePtr shader, const StringVec& udimSet) bakedInput = bakedShader->addInput(sourceName, sourceType); } - OutputPtr output = sourceInput->getConnectedOutput(); + // Assign image or constant data to the baked input. if (output) { - // Skip uniform outputs at their default values. - if (_bakedConstantMap.count(output) && _bakedConstantMap[output].isDefault) - { - continue; - } - // Store a constant value for uniform outputs. if (_optimizeConstants && _bakedConstantMap.count(output)) { @@ -392,27 +404,16 @@ DocumentPtr TextureBaker::bakeMaterial(NodePtr shader, const StringVec& udimSet) } } - // Write referenced baked images. - bool bakingSuccessful = true; + // Generate uniform images and write to disk. + ImagePtr uniformImage = createUniformImage(4, 4, 4, _baseType, Color4()); for (const auto& pair : _bakedImageMap) { - if (_optimizeConstants && _bakedConstantMap.count(pair.first)) - { - continue; - } for (const BakedImage& baked : pair.second) { - if (!_imageHandler->saveImage(baked.filename, baked.image, true)) + if (baked.isUniform) { - bakingSuccessful = false; - if (_outputStream) - { - *_outputStream << "Failed to write baked image: " << baked.filename.asString() << std::endl; - } - } - else if (_outputStream) - { - *_outputStream << "Wrote baked image: " << baked.filename.asString() << std::endl; + uniformImage->setUniformColor(baked.uniformColor); + writeBakedImage(baked, uniformImage); } } } @@ -424,15 +425,12 @@ DocumentPtr TextureBaker::bakeMaterial(NodePtr shader, const StringVec& udimSet) _material = nullptr; // Return the baked document on success. - return bakingSuccessful ? bakedTextureDoc : nullptr; + return bakedTextureDoc; } BakedDocumentVec TextureBaker::createBakeDocuments(DocumentPtr doc, const FileSearchPath& searchPath) { GenContext genContext(_generator); - genContext.getOptions().hwSpecularEnvironmentMethod = SPECULAR_ENVIRONMENT_FIS; - genContext.getOptions().hwDirectionalAlbedoMethod = DIRECTIONAL_ALBEDO_TABLE; - genContext.getOptions().hwShadowMap = true; genContext.getOptions().targetColorSpaceOverride = LIN_REC709; genContext.getOptions().fileTextureVerticalFlip = true; genContext.getOptions().targetDistanceUnit = _distanceUnit; @@ -449,10 +447,6 @@ BakedDocumentVec TextureBaker::createBakeDocuments(DocumentPtr doc, const FileSe } genContext.getShaderGenerator().setColorManagementSystem(cms); StringResolverPtr resolver = StringResolver::create(); - ImageHandlerPtr imageHandler = GLTextureHandler::create(StbImageLoader::create()); -#if MATERIALX_BUILD_OIIO - imageHandler->addLoader(OiioImageLoader::create()); -#endif StringVec renderablePaths = getRenderablePaths(doc); BakedDocumentVec bakedDocuments; @@ -465,8 +459,8 @@ BakedDocumentVec TextureBaker::createBakeDocuments(DocumentPtr doc, const FileSe } NodePtr materialNode = elem->asA(); - std::unordered_set shaderNodes = getShaderNodes(materialNode); - NodePtr shaderNode = shaderNodes.empty() ? nullptr : *shaderNodes.begin(); + vector shaderNodes = getShaderNodes(materialNode); + NodePtr shaderNode = shaderNodes.empty() ? nullptr : shaderNodes[0]; if (!shaderNode) { continue; @@ -498,10 +492,9 @@ BakedDocumentVec TextureBaker::createBakeDocuments(DocumentPtr doc, const FileSe { continue; } - imageHandler->setSearchPath(searchPath); + _imageHandler->setSearchPath(searchPath); resolver->setUdimString(tag); - imageHandler->setFilenameResolver(resolver); - setImageHandler(imageHandler); + _imageHandler->setFilenameResolver(resolver); bakeShaderInputs(materialNode, shaderNode, genContext, tag); } diff --git a/source/MaterialXRenderGlsl/TextureBaker.h b/source/MaterialXRenderGlsl/TextureBaker.h index 79741a5a2b..a06450c3a6 100644 --- a/source/MaterialXRenderGlsl/TextureBaker.h +++ b/source/MaterialXRenderGlsl/TextureBaker.h @@ -161,20 +161,6 @@ class MX_RENDERGLSL_API TextureBaker : public GlslRenderer return _outputStream; } - /// Set baked texture resolution automatically. Defaults to false. - /// If any images are found upstream from a shader input, then the output baked texture is the largest image resolution. - /// If no images are found, then the fixed resolution of the baker is used. - void setAutoTextureResolution(bool enable) - { - _autoTextureResolution = enable; - } - - /// Return whether automatic baked texture resolution is set. - bool getAutoTextureResolution() const - { - return _autoTextureResolution; - } - /// Set whether to create a short name for baked images by hashing the baked image filenames /// This is useful for file systems which may have a maximum limit on filename size. /// By default names are not hashed. @@ -224,20 +210,13 @@ class MX_RENDERGLSL_API TextureBaker : public GlslRenderer /// then the given output filename will be used as a template. void bakeAllMaterials(DocumentPtr doc, const FileSearchPath& searchPath, const FilePath& outputFileName); - protected: - TextureBaker(unsigned int width, unsigned int height, Image::BaseType baseType); - - // Generate a texture filename for the given graph output. - FilePath generateTextureFilename(OutputPtr output, const string& srName, const string& udim); - protected: class BakedImage { public: - ImagePtr image; - bool isUniform = false; - Color4 uniformColor; FilePath filename; + Color4 uniformColor; + bool isUniform = false; }; class BakedConstant { @@ -249,6 +228,15 @@ class MX_RENDERGLSL_API TextureBaker : public GlslRenderer using BakedImageMap = std::unordered_map; using BakedConstantMap = std::unordered_map; + protected: + TextureBaker(unsigned int width, unsigned int height, Image::BaseType baseType); + + // Generate a texture filename for the given graph output. + FilePath generateTextureFilename(OutputPtr output, const string& srName, const string& udim); + + // Write a baked image to disk, returning true if the write was successful. + bool writeBakedImage(const BakedImage& baked, ImagePtr image); + protected: string _extension; string _colorSpace; @@ -260,12 +248,12 @@ class MX_RENDERGLSL_API TextureBaker : public GlslRenderer string _bakedGeomInfoName; FileSearchPath _codeSearchPath; std::ostream* _outputStream; - bool _autoTextureResolution; bool _hashImageNames; std::pair _textureSpace; ShaderGeneratorPtr _generator; ConstNodePtr _material; + ImagePtr _frameCaptureImage; BakedImageMap _bakedImageMap; BakedConstantMap _bakedConstantMap; diff --git a/source/MaterialXRenderOsl/OslRenderer.cpp b/source/MaterialXRenderOsl/OslRenderer.cpp index 58a2cc99a1..e08a95f73d 100644 --- a/source/MaterialXRenderOsl/OslRenderer.cpp +++ b/source/MaterialXRenderOsl/OslRenderer.cpp @@ -414,7 +414,7 @@ void OslRenderer::render() } } -ImagePtr OslRenderer::captureImage() +ImagePtr OslRenderer::captureImage(ImagePtr) { // As rendering goes to disk need to read the image back from disk StringVec errors; @@ -436,16 +436,4 @@ ImagePtr OslRenderer::captureImage() return returnImage; } -void OslRenderer::saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip) -{ - StringVec errors; - const string errorType("GLSL image save error."); - - if (!_imageHandler->saveImage(filePath, image, verticalFlip)) - { - errors.push_back("Failed to save to file: " + filePath.asString()); - throw ExceptionShaderRenderError(errorType, errors); - } -} - } // namespace MaterialX diff --git a/source/MaterialXRenderOsl/OslRenderer.h b/source/MaterialXRenderOsl/OslRenderer.h index 486a5620d6..35cea74c35 100644 --- a/source/MaterialXRenderOsl/OslRenderer.h +++ b/source/MaterialXRenderOsl/OslRenderer.h @@ -91,15 +91,8 @@ class MX_RENDEROSL_API OslRenderer : public ShaderRenderer /// @name Utilities /// @{ - /// Capture the current contents of rendering to an image. Note that this method - /// does not perform any action as render() produces images as part of it's - /// execution. - ImagePtr captureImage() override; - - /// Save the current contents of rendering to disk. Note that this method - /// does not perform any action as render() produces images as part if it's - /// execution. - void saveImage(const FilePath& filePath, ConstImagePtr image, bool verticalFlip) override; + /// Capture the current rendered output as an image. + ImagePtr captureImage(ImagePtr image = nullptr) override; /// @} /// @name Compilation settings diff --git a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp index 611a9f5ca6..8d12da3f5f 100644 --- a/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp +++ b/source/MaterialXTest/MaterialXGenShader/GenShaderUtil.cpp @@ -803,11 +803,11 @@ void ShaderGeneratorTester::validate(const mx::GenOptions& generateOptions, cons // Handle material node checking. For now only check first surface shader if any if (outputNode && outputNode->getType() == mx::MATERIAL_TYPE_STRING) { - std::unordered_set shaderNodes = getShaderNodes(outputNode, mx::SURFACE_SHADER_TYPE_STRING); + std::vector shaderNodes = getShaderNodes(outputNode); if (!shaderNodes.empty()) { - nodeDef = (*shaderNodes.begin())->getNodeDef(); - targetElement = *shaderNodes.begin(); + nodeDef = shaderNodes[0]->getNodeDef(); + targetElement = shaderNodes[0]; } } diff --git a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp index b26d74d34b..853ffc4e5d 100644 --- a/source/MaterialXTest/MaterialXRender/RenderUtil.cpp +++ b/source/MaterialXTest/MaterialXRender/RenderUtil.cpp @@ -381,8 +381,7 @@ bool ShaderRenderTester::validate(const mx::FilePathVec& testRootPaths, const mx // Get connected shader nodes if a material node. if (outputNode && outputNode->getType() == mx::MATERIAL_TYPE_STRING) { - std::unordered_set shaderNodes = getShaderNodes(outputNode); - for (auto node : shaderNodes) + for (mx::NodePtr node : getShaderNodes(outputNode)) { mx::NodeDefPtr nodeDef = node->getNodeDef(); if (nodeDef) diff --git a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp index 7e830caf1d..0965536d0f 100644 --- a/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp +++ b/source/MaterialXTest/MaterialXRenderGlsl/RenderGlsl.cpp @@ -664,7 +664,7 @@ bool GlslShaderRenderTester::runRenderer(const std::string& shaderName, mx::ImagePtr image = _renderer->captureImage(); if (image) { - _renderer->saveImage(fileName, image, true); + _renderer->getImageHandler()->saveImage(fileName, image, true); if (imageVec) { imageVec->push_back(image); @@ -709,12 +709,12 @@ void GlslShaderRenderTester::runBake(mx::DocumentPtr doc, const mx::FileSearchPa const unsigned int bakeWidth = std::max(bakeOptions.resolution, (unsigned int) 2); const unsigned int bakeHeight = std::max(bakeOptions.resolution, (unsigned int) 2); + mx::ImageVec imageVec = _renderer->getImageHandler()->getReferencedImages(doc); mx::Image::BaseType baseType = bakeOptions.hdr ? mx::Image::BaseType::FLOAT : mx::Image::BaseType::UINT8; mx::TextureBakerPtr baker = mx::TextureBaker::create(bakeWidth, bakeHeight, baseType); baker->setupUnitSystem(doc); baker->setImageHandler(_renderer->getImageHandler()); baker->setOptimizeConstants(true); - baker->setAutoTextureResolution(true); baker->setHashImageNames(true); baker->setCodeSearchPath(codeSearchPath); baker->setTextureSpace(bakeOptions.uvmin, bakeOptions.uvmax); diff --git a/source/MaterialXView/Editor.cpp b/source/MaterialXView/Editor.cpp index 2f5a528d3f..5d3f7e00aa 100644 --- a/source/MaterialXView/Editor.cpp +++ b/source/MaterialXView/Editor.cpp @@ -655,23 +655,26 @@ void PropertyEditor::updateContents(Viewer* viewer) create(*viewer); MaterialPtr material = viewer->getSelectedMaterial(); - if (!material) + mx::TypedElementPtr elem = material ? material->getElement() : nullptr; + if (!material || !elem) { return; } // Shading model display - mx::TypedElementPtr elem = material->getElement(); - std::string shaderName = elem ? elem->getCategory() : mx::EMPTY_STRING; - if (elem->isA() && !shaderName.empty() && shaderName != "surface") + if (elem->isA()) { - ng::Widget* twoColumns = new ng::Widget(_container); - twoColumns->setLayout(_gridLayout2); - ng::Label* modelLabel = new ng::Label(twoColumns, "Shading Model"); - modelLabel->setFontSize(20); - modelLabel->setFont("sans-bold"); - ng::Label* nameLabel = new ng::Label(twoColumns, shaderName); - nameLabel->setFontSize(20); + std::string shaderName = elem->getCategory(); + if (!shaderName.empty() && shaderName != "surface") + { + ng::Widget* twoColumns = new ng::Widget(_container); + twoColumns->setLayout(_gridLayout2); + ng::Label* modelLabel = new ng::Label(twoColumns, "Shading Model"); + modelLabel->setFontSize(20); + modelLabel->setFont("sans-bold"); + ng::Label* nameLabel = new ng::Label(twoColumns, shaderName); + nameLabel->setFontSize(20); + } } bool addedItems = false; diff --git a/source/MaterialXView/Viewer.cpp b/source/MaterialXView/Viewer.cpp index 5525e3a395..a760a067fa 100644 --- a/source/MaterialXView/Viewer.cpp +++ b/source/MaterialXView/Viewer.cpp @@ -11,6 +11,7 @@ #include #include +#include #include #include @@ -43,10 +44,6 @@ const int ALBEDO_TABLE_SIZE = 64; const int IRRADIANCE_MAP_WIDTH = 256; const int IRRADIANCE_MAP_HEIGHT = 128; -const int MIN_TEXTURE_RES = 256; -const int MAX_TEXTURE_RES = 8192; -const int DEFAULT_TEXTURE_RES = 1024; - const std::string DIR_LIGHT_NODE_CATEGORY = "directional_light"; const std::string IRRADIANCE_MAP_FOLDER = "irradiance"; @@ -247,6 +244,7 @@ Viewer::Viewer(const std::string& materialFilename, _outlineSelection(false), _envSampleCount(DEFAULT_ENV_SAMPLE_COUNT), _drawEnvironment(false), + _targetShader("standard_surface"), _captureRequested(false), _exitRequested(false), _wedgeRequested(false), @@ -258,7 +256,6 @@ Viewer::Viewer(const std::string& materialFilename, _bakeHdr(false), _bakeAverage(false), _bakeOptimize(true), - _bakeTextureRes(DEFAULT_TEXTURE_RES), _bakeRequested(false) { // Set the requested background color. @@ -584,6 +581,18 @@ mx::FilePath Viewer::getBaseOutputPath() return baseFilename; } +mx::ElementPredicate Viewer::getElementPredicate() +{ + return [this](mx::ConstElementPtr elem) + { + if (elem->hasSourceUri()) + { + return (_xincludeFiles.count(elem->getSourceUri()) == 0); + } + return true; + }; +} + void Viewer::createLoadMeshInterface(Widget* parent, const std::string& label) { ng::Button* meshButton = new ng::Button(parent, label); @@ -668,18 +677,8 @@ void Viewer::createSaveMaterialsInterface(Widget* parent, const std::string& lab filename.addExtension(mx::MTLX_EXTENSION); } - // Add element predicate to prune out writing elements from included files - auto skipXincludes = [this](mx::ConstElementPtr elem) - { - if (elem->hasSourceUri()) - { - return (_xincludeFiles.count(elem->getSourceUri()) == 0); - } - return true; - }; mx::XmlWriteOptions writeOptions; - writeOptions.writeXIncludeEnable = true; - writeOptions.elementPredicate = skipXincludes; + writeOptions.elementPredicate = getElementPredicate(); mx::writeToXmlFile(material->getDocument(), filename, &writeOptions); // Update material file name @@ -942,6 +941,22 @@ void Viewer::createAdvancedSettings(Widget* parent) _envSampleCount = MIN_ENV_SAMPLES * (int) std::pow(4, index); }); + ng::Label* translationLabel = new ng::Label(advancedPopup, "Translation Options (T)"); + translationLabel->setFontSize(20); + translationLabel->setFont("sans-bold"); + + ng::Widget* targetShaderGroup = new ng::Widget(advancedPopup); + targetShaderGroup->setLayout(new ng::BoxLayout(ng::Orientation::Horizontal)); + new ng::Label(targetShaderGroup, "Target Shader"); + ng::TextBox* targetShaderBox = new ng::TextBox(targetShaderGroup, _targetShader); + targetShaderBox->setCallback([this](const std::string& choice) + { + _targetShader = choice; + return true; + }); + targetShaderBox->setFontSize(16); + targetShaderBox->setEditable(true); + ng::Label* textureLabel = new ng::Label(advancedPopup, "Texture Baking Options (B)"); textureLabel->setFontSize(20); textureLabel->setFont("sans-bold"); @@ -960,31 +975,13 @@ void Viewer::createAdvancedSettings(Widget* parent) _bakeAverage = enable; }); - ng::CheckBox* bakeOptimized = new ng::CheckBox(advancedPopup, "Optimize Baked Materials"); + ng::CheckBox* bakeOptimized = new ng::CheckBox(advancedPopup, "Optimize Baked Constants"); bakeOptimized->setChecked(_bakeOptimize); bakeOptimized->setCallback([this](bool enable) { _bakeOptimize = enable; }); - Widget* textureResGroup = new Widget(advancedPopup); - textureResGroup->setLayout(new ng::BoxLayout(ng::Orientation::Horizontal)); - new ng::Label(textureResGroup, "Texture Res:"); - mx::StringVec textureResOptions; - for (int i = MIN_TEXTURE_RES; i <= MAX_TEXTURE_RES; i *= 2) - { - mProcessEvents = false; - textureResOptions.push_back(std::to_string(i)); - mProcessEvents = true; - } - ng::ComboBox* textureResBox = new ng::ComboBox(textureResGroup, textureResOptions); - textureResBox->setChevronIcon(-1); - textureResBox->setSelectedIndex((int)std::log2(DEFAULT_TEXTURE_RES / MIN_TEXTURE_RES)); - textureResBox->setCallback([this](int index) - { - _bakeTextureRes = MIN_TEXTURE_RES * (int) std::pow(2, index); - }); - ng::Label* wedgeLabel = new ng::Label(advancedPopup, "Wedge Render Options (W)"); wedgeLabel->setFontSize(20); wedgeLabel->setFont("sans-bold"); @@ -1225,10 +1222,10 @@ void Viewer::loadDocument(const mx::FilePath& filename, mx::DocumentPtr librarie mx::NodePtr node = elem->asA(); if (node && node->getType() == mx::MATERIAL_TYPE_STRING) { - std::unordered_set shaderNodes = getShaderNodes(node, mx::SURFACE_SHADER_TYPE_STRING); + std::vector shaderNodes = getShaderNodes(node); if (!shaderNodes.empty()) { - renderableElem = *shaderNodes.begin(); + renderableElem = shaderNodes[0]; } materialNodes.push_back(node); } @@ -1544,6 +1541,30 @@ void Viewer::saveDotFiles() } } +mx::DocumentPtr Viewer::translateMaterial() +{ + MaterialPtr material = getSelectedMaterial(); + mx::DocumentPtr doc = material ? material->getDocument() : nullptr; + if (!doc) + { + return nullptr; + } + + mx::DocumentPtr translatedDoc = doc->copy(); + mx::ShaderTranslatorPtr translator = mx::ShaderTranslator::create(); + try + { + translator->translateAllMaterials(translatedDoc, _targetShader); + } + catch (std::exception& e) + { + new ng::MessageDialog(this, ng::MessageDialog::Type::Warning, "Failed to translate material", e.what()); + return nullptr; + } + + return translatedDoc; +} + void Viewer::initContext(mx::GenContext& context) { // Initialize search paths. @@ -1732,6 +1753,25 @@ bool Viewer::keyboardEvent(int key, int scancode, int action, int modifiers) } } + // Request shader translation for the current material. + if (key == GLFW_KEY_T && action == GLFW_PRESS) + { + mx::DocumentPtr translatedDoc = translateMaterial(); + if (translatedDoc) + { + mx::FilePath translatedFilename = getBaseOutputPath(); + translatedFilename = translatedFilename.asString() + "_" + _targetShader; + translatedFilename.addExtension(mx::MTLX_EXTENSION); + + mx::XmlWriteOptions writeOptions; + writeOptions.elementPredicate = getElementPredicate(); + mx::writeToXmlFile(translatedDoc, translatedFilename, &writeOptions); + + new ng::MessageDialog(this, ng::MessageDialog::Type::Information, "Saved translated material: ", translatedFilename); + } + return true; + } + // Request a bake of the current material. if (key == GLFW_KEY_B && action == GLFW_PRESS) { @@ -2072,21 +2112,31 @@ mx::ImagePtr Viewer::renderWedge() void Viewer::bakeTextures() { MaterialPtr material = getSelectedMaterial(); - mx::DocumentPtr doc = material->getDocument(); + mx::DocumentPtr doc = material ? material->getDocument() : nullptr; if (!doc) { return; } { + // Compute baking resolution. + mx::ImageVec imageVec = _imageHandler->getReferencedImages(doc); + auto maxImageSize = mx::getMaxDimensions(imageVec); + unsigned int bakeWidth = std::max(maxImageSize.first, (unsigned int) 4); + unsigned int bakeHeight = std::max(maxImageSize.second, (unsigned int) 4); + // Construct a texture baker. mx::Image::BaseType baseType = _bakeHdr ? mx::Image::BaseType::FLOAT : mx::Image::BaseType::UINT8; - mx::TextureBakerPtr baker = mx::TextureBaker::create(_bakeTextureRes, _bakeTextureRes, baseType); + mx::TextureBakerPtr baker = mx::TextureBaker::create(bakeWidth, bakeHeight, baseType); baker->setupUnitSystem(_stdLib); baker->setDistanceUnit(_genContext.getOptions().targetDistanceUnit); baker->setAverageImages(_bakeAverage); baker->setOptimizeConstants(_bakeOptimize); + // Assign our existing image handler, releasing any existing render resources for cached images. + _imageHandler->releaseRenderResources(); + baker->setImageHandler(_imageHandler); + // Extend the image search path to include the source material folder. mx::FilePath materialFilename = mx::FilePath(doc->getSourceUri()); mx::FileSearchPath materialSearchPath = _searchPath; @@ -2101,6 +2151,9 @@ void Viewer::bakeTextures() { std::cerr << "Error in texture baking: " << e.what() << std::endl; } + + // Release any render resources generated by the baking process. + _imageHandler->releaseRenderResources(); } // After the baker has been destructed, restore state for scene rendering. @@ -2414,7 +2467,7 @@ void Viewer::updateShadowMap() mx::MeshPartitionPtr geom = assignment.first; _shadowMaterial->drawPartition(geom); } - _shadowMap = framebuffer->createColorImage(); + _shadowMap = framebuffer->getColorImage(); // Apply Gaussian blurring. for (unsigned int i = 0; i < _shadowSoftness; i++) @@ -2433,7 +2486,7 @@ void Viewer::updateShadowMap() glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT); renderScreenSpaceQuad(_shadowBlurMaterial); _imageHandler->releaseRenderResources(_shadowMap); - _shadowMap = framebuffer->createColorImage(); + _shadowMap = framebuffer->getColorImage(); } // Restore state for scene rendering. @@ -2487,7 +2540,7 @@ void Viewer::updateAlbedoTable() // Store albedo table image. _imageHandler->releaseRenderResources(_lightHandler->getAlbedoTable()); - _lightHandler->setAlbedoTable(framebuffer->createColorImage()); + _lightHandler->setAlbedoTable(framebuffer->getColorImage()); if (_saveGeneratedLights) { _imageHandler->saveImage("AlbedoTable.exr", _lightHandler->getAlbedoTable()); diff --git a/source/MaterialXView/Viewer.h b/source/MaterialXView/Viewer.h index 2dfe8fdfff..6df65c682d 100644 --- a/source/MaterialXView/Viewer.h +++ b/source/MaterialXView/Viewer.h @@ -169,6 +169,9 @@ class Viewer : public ng::Screen void loadShaderSource(); void saveDotFiles(); + // Translate the current material to the target shading model. + mx::DocumentPtr translateMaterial(); + // Assign the given material to the given geometry, or remove any // existing assignment if the given material is nullptr. void assignMaterial(mx::MeshPartitionPtr geometry, MaterialPtr material); @@ -189,6 +192,9 @@ class Viewer : public ng::Screen // Generate a base output filepath for data derived from the current material. mx::FilePath getBaseOutputPath(); + // Return an element predicate for documents written from the viewer. + mx::ElementPredicate getElementPredicate(); + void initCamera(); void updateViewHandlers(); void updateGeometrySelections(); @@ -348,6 +354,9 @@ class Viewer : public ng::Screen int _envSampleCount; bool _drawEnvironment; + // Shader translation + std::string _targetShader; + // Frame capture bool _captureRequested; mx::FilePath _captureFilename; @@ -366,7 +375,6 @@ class Viewer : public ng::Screen bool _bakeHdr; bool _bakeAverage; bool _bakeOptimize; - int _bakeTextureRes; bool _bakeRequested; mx::FilePath _bakeFilename; }; diff --git a/source/PyMaterialX/PyMaterialXRender/PyImage.cpp b/source/PyMaterialX/PyMaterialXRender/PyImage.cpp index 3a874e0413..bc04d4478b 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyImage.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyImage.cpp @@ -32,6 +32,7 @@ void bindPyImage(py::module& mod) .def("setTexelColor", &mx::Image::setTexelColor) .def("getTexelColor", &mx::Image::getTexelColor) .def("isUniformColor", &mx::Image::isUniformColor) + .def("setUniformColor", &mx::Image::setUniformColor) .def("applyBoxBlur", &mx::Image::applyBoxBlur) .def("applyGaussianBlur", &mx::Image::applyGaussianBlur) .def("splitByLuminance", &mx::Image::splitByLuminance) @@ -44,4 +45,5 @@ void bindPyImage(py::module& mod) mod.def("createUniformImage", &mx::createUniformImage); mod.def("createImageStrip", &mx::createImageStrip); + mod.def("getMaxDimensions", &mx::getMaxDimensions); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp b/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp index 5c6d864914..75e8134ca4 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyImageHandler.cpp @@ -50,8 +50,10 @@ void bindPyImageHandler(py::module& mod) .def("setFilenameResolver", &mx::ImageHandler::setFilenameResolver) .def("getFilenameResolver", &mx::ImageHandler::getFilenameResolver) .def("createRenderResources", &mx::ImageHandler::createRenderResources) - .def("releaseRenderResources", &mx::ImageHandler::releaseRenderResources) + .def("releaseRenderResources", &mx::ImageHandler::releaseRenderResources, + py::arg("image") = nullptr) .def("clearImageCache", &mx::ImageHandler::clearImageCache) .def("getZeroImage", &mx::ImageHandler::getZeroImage) - .def("getInvalidImage", &mx::ImageHandler::getInvalidImage); + .def("getInvalidImage", &mx::ImageHandler::getInvalidImage) + .def("getReferencedImages", &mx::ImageHandler::getReferencedImages); } diff --git a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp index 027074584b..e871f4d4f6 100644 --- a/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRender/PyShaderRenderer.cpp @@ -10,92 +10,23 @@ namespace py = pybind11; namespace mx = MaterialX; -class PyShaderRenderer : public mx::ShaderRenderer -{ - public: - PyShaderRenderer() : - mx::ShaderRenderer() - { - } - - void initialize() override - { - PYBIND11_OVERLOAD_PURE( - void, - mx::ShaderRenderer, - initialize - ); - } - - void createProgram(mx::ShaderPtr shader) override - { - PYBIND11_OVERLOAD_PURE( - void, - mx::ShaderRenderer, - createProgram, - shader - ); - } - - void createProgram(const StageMap& stages) override - { - PYBIND11_OVERLOAD_PURE( - void, - mx::ShaderRenderer, - createProgram, - stages - ); - } - - void validateInputs() override - { - PYBIND11_OVERLOAD_PURE( - void, - mx::ShaderRenderer, - validateInputs - ); - } - - void render() override - { - PYBIND11_OVERLOAD_PURE( - void, - mx::ShaderRenderer, - render - ); - } - - void saveImage(const mx::FilePath& filePath, mx::ConstImagePtr image, bool verticalFlip) override - { - PYBIND11_OVERLOAD_PURE( - void, - mx::ShaderRenderer, - saveImage, - filePath, - image, - verticalFlip - ); - } -}; - void bindPyShaderRenderer(py::module& mod) { - py::class_(mod, "ShaderRenderer") + py::class_(mod, "ShaderRenderer") .def("initialize", &mx::ShaderRenderer::initialize) - .def("setSize", &mx::ShaderRenderer::setSize) .def("setImageHandler", &mx::ShaderRenderer::setImageHandler) .def("getImageHandler", &mx::ShaderRenderer::getImageHandler) .def("setLightHandler", &mx::ShaderRenderer::setLightHandler) .def("getLightHandler", &mx::ShaderRenderer::getLightHandler) + .def("setGeometryHandler", &mx::ShaderRenderer::setGeometryHandler) .def("getGeometryHandler", &mx::ShaderRenderer::getGeometryHandler) .def("setViewHandler", &mx::ShaderRenderer::setViewHandler) .def("getViewHandler", &mx::ShaderRenderer::getViewHandler) .def("createProgram", static_cast(&mx::ShaderRenderer::createProgram)) .def("createProgram", static_cast(&mx::ShaderRenderer::createProgram)) .def("validateInputs", &mx::ShaderRenderer::validateInputs) - .def("render", &mx::ShaderRenderer::render) - .def("saveImage", &mx::ShaderRenderer::saveImage) - .def("getReferencedImages", &mx::ShaderRenderer::getReferencedImages); + .def("setSize", &mx::ShaderRenderer::setSize) + .def("render", &mx::ShaderRenderer::render); static py::exception pyExceptionShaderRenderError(mod, "ExceptionShaderRenderError"); diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp index 1bebf48bf5..a7d2ceb15a 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGLTextureHandler.cpp @@ -15,6 +15,8 @@ void bindPyGLTextureHandler(py::module& mod) py::class_(mod, "GLTextureHandler") .def_static("create", &mx::GLTextureHandler::create) .def("bindImage", &mx::GLTextureHandler::bindImage) - .def("mapAddressModeToGL", &mx::GLTextureHandler::mapAddressModeToGL) - .def("mapFilterTypeToGL", &mx::GLTextureHandler::mapFilterTypeToGL); + .def("unbindImage", &mx::GLTextureHandler::unbindImage) + .def("createRenderResources", &mx::GLTextureHandler::createRenderResources) + .def("releaseRenderResources", &mx::GLTextureHandler::releaseRenderResources, + py::arg("image") = nullptr); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp index 312e7c2073..72c6cd9757 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyGlslRenderer.cpp @@ -21,7 +21,5 @@ void bindPyGlslRenderer(py::module& mod) .def("render", &mx::GlslRenderer::render) .def("renderTextureSpace", &mx::GlslRenderer::renderTextureSpace) .def("captureImage", &mx::GlslRenderer::captureImage) - .def("saveImage", &mx::GlslRenderer::saveImage) - .def("getProgram", &mx::GlslRenderer::getProgram) - .def("getReferencedImages", &mx::GlslRenderer::getReferencedImages); + .def("getProgram", &mx::GlslRenderer::getProgram); } diff --git a/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp b/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp index 148c9ec0cb..ae5f8e81be 100644 --- a/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp +++ b/source/PyMaterialX/PyMaterialXRenderGlsl/PyTextureBaker.cpp @@ -31,8 +31,6 @@ void bindPyTextureBaker(py::module& mod) .def("getBakedGraphName", &mx::TextureBaker::getBakedGraphName) .def("setBakedGeomInfoName", &mx::TextureBaker::setBakedGeomInfoName) .def("getBakedGeomInfoName", &mx::TextureBaker::getBakedGeomInfoName) - .def("setAutoTextureResolution", &mx::TextureBaker::setAutoTextureResolution) - .def("getAutoTextureResolution", &mx::TextureBaker::getAutoTextureResolution) .def("setHashImageNames", &mx::TextureBaker::setHashImageNames) .def("getHashImageNames", &mx::TextureBaker::getHashImageNames) .def("setTextureSpace", &mx::TextureBaker::setTextureSpace) diff --git a/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp b/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp index 5fb94712a4..76ecee8ce9 100644 --- a/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp +++ b/source/PyMaterialX/PyMaterialXRenderOsl/PyOslRenderer.cpp @@ -21,7 +21,6 @@ void bindPyOslRenderer(py::module& mod) .def("validateInputs", &mx::OslRenderer::validateInputs) .def("render", &mx::OslRenderer::render) .def("captureImage", &mx::OslRenderer::captureImage) - .def("saveImage", &mx::OslRenderer::saveImage) .def("setOslCompilerExecutable", &mx::OslRenderer::setOslCompilerExecutable) .def("setOslIncludePath", &mx::OslRenderer::setOslIncludePath) .def("setOslOutputFilePath", &mx::OslRenderer::setOslOutputFilePath)