diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp b/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp index 94f435a56..5c8f476eb 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.cpp @@ -1,6 +1,7 @@ #include "ShaderGenUtil.h" #include "LobePruner.h" +#include "MaterialXCore/Types.h" #include @@ -56,17 +57,26 @@ const std::string& TopoNeutralGraph::getMaterialName() return kMaterialName; } -TopoNeutralGraph::TopoNeutralGraph(const mx::ElementPtr& material) { computeGraph(material); } +TopoNeutralGraph::TopoNeutralGraph(const mx::ElementPtr& material) { computeGraph(material, true); } TopoNeutralGraph::TopoNeutralGraph( const mx::ElementPtr& material, const LobePruner::Ptr& lobePruner) { _lobePruner = lobePruner; - computeGraph(material); + computeGraph(material, true); } -void TopoNeutralGraph::computeGraph(const mx::ElementPtr& material) +TopoNeutralGraph::TopoNeutralGraph( + const mx::ElementPtr& material, + const LobePruner::Ptr& lobePruner, + bool textured) +{ + _lobePruner = lobePruner; + computeGraph(material, textured); +} + +void TopoNeutralGraph::computeGraph(const mx::ElementPtr& material, bool textured) { if (!material) { throw mx::Exception("Invalid material element"); @@ -138,7 +148,25 @@ void TopoNeutralGraph::computeGraph(const mx::ElementPtr& material) if (!sourceInput) { continue; } - auto connectedNode = sourceInput->getConnectedNode(); + auto connectedNode = sourceInput->getConnectedNode(); + + // In textured mode we traverse everything, but in untextured mode we only traverse PBR + // level connections. + static const auto kPbrConnectionTypes = std::set + { +#if MX_COMBINED_VERSION >= 13807 + mx::BSDF_TYPE_STRING, mx::EDF_TYPE_STRING, mx::VDF_TYPE_STRING, +#else + "BSDF", "EDF", "VDF", +#endif + mx::SURFACE_SHADER_TYPE_STRING, mx::DISPLACEMENT_SHADER_TYPE_STRING, + mx::VOLUME_SHADER_TYPE_STRING + }; + + if (!textured && kPbrConnectionTypes.count(sourceInput->getType()) == 0) { + connectedNode = nullptr; + } + const std::string defaultGeomPropString = gatherDefaultGeomProp(*sourceInput); if (connectedNode) { auto destConnectedIt = _nodeMap.find(connectedNode->getNamePath()); diff --git a/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.h b/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.h index 5387bb374..bc9c9c002 100644 --- a/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.h +++ b/lib/mayaUsd/render/MaterialXGenOgsXml/ShaderGenUtil.h @@ -20,8 +20,34 @@ namespace ShaderGenUtil { class MAYAUSD_CORE_PUBLIC TopoNeutralGraph { public: + /*! Creates a barebones TopoNeutralGraph that will process the provided material and generate a + * topo neutral version of it. + * @param[in] material the material to process + */ explicit TopoNeutralGraph(const mx::ElementPtr& material); - explicit TopoNeutralGraph(const mx::ElementPtr& material, const LobePruner::Ptr& library); + + /*! Creates a TopoNeutralGraph that will process the provided material and generate a topo + * neutral version of it. It will also substitute lobe pruned categories if a LobePruner is + * provided. + * @param[in] material the material to process + * @param[in] lobePruner an instance of a LobePruner. These are usually singletons that + * accumulate pruned NodeDefs + */ + TopoNeutralGraph(const mx::ElementPtr& material, const LobePruner::Ptr& lobePruner); + + /*! Creates a TopoNeutralGraph that will process the provided material and generate a topo + * neutral version of it. It will also substitute lobe pruned categories if a LobePruner is + * provided. + * @param[in] material the material to process + * @param[in] lobePruner an instance of a LobePruner. These are usually singletons that + * accumulate pruned NodeDefs + * @param[in] textured is true if the full material is to be processed. When false, we will + * generate an untextured topo neutral material instead + */ + TopoNeutralGraph( + const mx::ElementPtr& material, + const LobePruner::Ptr& lobePruner, + bool textured); ~TopoNeutralGraph() = default; TopoNeutralGraph() = delete; @@ -57,7 +83,7 @@ class MAYAUSD_CORE_PUBLIC TopoNeutralGraph mx::NodeGraphPtr& getNodeGraph(); protected: - void computeGraph(const mx::ElementPtr& material); + void computeGraph(const mx::ElementPtr& material, bool textured); mx::NodePtr cloneNode(const mx::Node& node, mx::GraphElement& container); mx::OutputPtr findNodeGraphOutput(const mx::Input& input, const std::string& outputName); std::string gatherChannels(const mx::Input& input); diff --git a/lib/mayaUsdAPI/render.cpp b/lib/mayaUsdAPI/render.cpp index ca0f3dcfc..8022c28a7 100644 --- a/lib/mayaUsdAPI/render.cpp +++ b/lib/mayaUsdAPI/render.cpp @@ -169,6 +169,14 @@ struct TopoNeutralGraphImpl { } + TopoNeutralGraphImpl( + const MaterialX::ElementPtr& material, + const LobePruner::Ptr& lobePruner, + bool textured) + : _topoGraph(material, lobePruner->_imp->_lobePruner, textured) + { + } + MaterialXMaya::ShaderGenUtil::TopoNeutralGraph _topoGraph; }; @@ -184,6 +192,14 @@ TopoNeutralGraph::TopoNeutralGraph( { } +TopoNeutralGraph::TopoNeutralGraph( + const MaterialX::ElementPtr& material, + const LobePruner::Ptr& lobePruner, + bool textured) + : _imp(new TopoNeutralGraphImpl(material, lobePruner, textured)) +{ +} + // When using a pimpl you need to define the destructor here in the // .cpp so the compiler has access to the impl. TopoNeutralGraph::~TopoNeutralGraph() = default; diff --git a/lib/mayaUsdAPI/render.h b/lib/mayaUsdAPI/render.h index 77e70c897..b3f71699d 100644 --- a/lib/mayaUsdAPI/render.h +++ b/lib/mayaUsdAPI/render.h @@ -145,8 +145,34 @@ class MAYAUSD_API_PUBLIC LobePruner class MAYAUSD_API_PUBLIC TopoNeutralGraph { public: - TopoNeutralGraph(const MaterialX::ElementPtr&); - TopoNeutralGraph(const MaterialX::ElementPtr&, const LobePruner::Ptr& lobePruner); + /*! Creates a barebones TopoNeutralGraph that will process the provided material and generate a + * topo neutral version of it. + * @param[in] material the material to process + */ + TopoNeutralGraph(const MaterialX::ElementPtr& material); + + /*! Creates a TopoNeutralGraph that will process the provided material and generate a topo + * neutral version of it. It will also substitute lobe pruned categories if a LobePruner is + * provided. + * @param[in] material the material to process + * @param[in] lobePruner an instance of a LobePruner. These are usually singletons that + * accumulate pruned NodeDefs + */ + TopoNeutralGraph(const MaterialX::ElementPtr& material, const LobePruner::Ptr& lobePruner); + + /*! Creates a TopoNeutralGraph that will process the provided material and generate a topo + * neutral version of it. It will also substitute lobe pruned categories if a LobePruner is + * provided. + * @param[in] material the material to process + * @param[in] lobePruner an instance of a LobePruner. These are usually singletons that + * accumulate pruned NodeDefs + * @param[in] textured is true if the full material is to be processed. When false, we will + * generate an untextured topo neutral material instead + */ + TopoNeutralGraph( + const MaterialX::ElementPtr& material, + const LobePruner::Ptr& lobePruner, + bool textured); ~TopoNeutralGraph(); MaterialX::NodeGraphPtr nodeGraph(); diff --git a/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp b/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp index 35ecd56e6..6083f92ad 100644 --- a/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp +++ b/test/lib/mayaUsd/utils/test_ShaderGenUtils.cpp @@ -204,3 +204,51 @@ TEST(ShaderGenUtils, lobePruner) optimizedNodeId = lobePruner->getOptimizedNodeId(usdNode); ASSERT_TRUE(optimizedNodeId.IsEmpty()); } + +TEST(ShaderGenUtils, lobePrunedTopoGraph) +{ + namespace sgu = MaterialXMaya::ShaderGenUtil; + + auto testPath = mx::FilePath(MATERIALX_TEST_DATA); + + auto library = mx::createDocument(); + ASSERT_TRUE(library != nullptr); + auto searchPath = PXR_NS::HdMtlxSearchPaths(); + mx::loadLibraries({}, searchPath, library); + + auto doc = mx::createDocument(); + doc->importLibrary(library); + + auto lobePruner = sgu::LobePruner::create(); + lobePruner->setLibrary(doc); + + const mx::XmlReadOptions readOptions; + mx::readFromXmlFile(doc, testPath / "MultiConnect1_topo.mtlx", mx::EMPTY_STRING, &readOptions); + + const auto originalCategory = doc->getNode("N1")->getCategory(); + + auto topoNetwork + = MaterialXMaya::ShaderGenUtil::TopoNeutralGraph(doc->getNode("N0"), lobePruner, false); + + // In thory, we should have an empty NodeGraph since we are in untextured mode: + ASSERT_TRUE(topoNetwork.getNodeGraph()->getNodes().empty()); + + // Should have only 2 nodes: + ASSERT_EQ(topoNetwork.getDocument()->getNodes().size(), 2); + + // Surface should be optimized and fully unconnected: + const auto surface = topoNetwork.getDocument()->getNode("N1"); + + const auto surfaceCategory = surface->getCategory(); + + // Should begin with standard_surface + ASSERT_TRUE(surfaceCategory.rfind(originalCategory, 0) == 0); + + // But have a LobePruner optimization: + ASSERT_TRUE(surfaceCategory.size() > originalCategory.size()); + + // Which should be in the library of the LobePruner: + topoNetwork.getDocument()->importLibrary(doc); + const auto optNodeDef = surface->getNodeDef(); + ASSERT_TRUE(optNodeDef != nullptr); +}