diff --git a/javascript/MaterialXTest/document.spec.js b/javascript/MaterialXTest/document.spec.js index 9b18339aaf..cb1a16cb50 100644 --- a/javascript/MaterialXTest/document.spec.js +++ b/javascript/MaterialXTest/document.spec.js @@ -185,4 +185,100 @@ describe('Document', () => nodeGraph.delete(); doc.delete(); }); + + // Sample node graph for defintion tests + function create_sample_graph() + { + const doc = mx.createDocument(); + + // Create a node graph with a single image node and output. + const nodeGraph = doc.addNodeGraph('NG_wrapper'); + expect(doc.getNodeGraphs().length).to.equal(1); + const image = nodeGraph.addNode('image'); + const nodes = nodeGraph.getNodes(); + expect(nodes.length).to.equal(1); + expect(nodes[0]).to.eql(image); + + image.setInputValueString('file', 'image1.png', 'filename'); + const input = image.getInput('file'); + expect(input).to.not.be.null; + + const output = nodeGraph.addOutput(); + const outputs = nodeGraph.getOutputs(); + expect(outputs.length).to.equal(1); + expect(outputs[0]).to.eql(output); + + // Verify the graph is valid + expect(doc.validate()).to.be.true; + return { doc, nodeGraph }; + } + + it('Create NodeDef from NodeGraph with child implementation', () => + { + const { doc, nodeGraph } = create_sample_graph(); + + // Create DefinitionOptions with addImplementationAsChild set to true + const options = new mx.DefinitionOptions(); + options.addImplementationAsChild = true; + + // Create NodeDef from the NodeGraph with embedded implementation + const nodeDef = doc.addNodeDefFromGraph( + nodeGraph, + 'ND_wrapper', + 'texture', + 'NG_wrapper', + options + ); + + expect(nodeDef).to.exist; + + // Verify the implementation was created as a child of the nodedef + const impl = nodeDef.getImplementation(); + expect(impl).to.exist; + expect(impl.isANodeGraph()) + expect(impl.getName()).to.equal('NG_wrapper'); + + // Verify the implementation matches the original node graph + let diff_options = new mx.ElementEquivalenceOptions(); + let differences = {}; + options.performValueComparisons = false; + let result = impl.isEquivalent(nodeGraph, diff_options, differences); + expect(result).to.be.true; + + // Cleanup + diff_options.delete(); + options.delete(); + doc.delete(); + }); + + it('Create NodeDef from NodeGraph with referencing implementation', () => + { + const { doc, nodeGraph } = create_sample_graph(); + + // Create DefinitionOptions with addImplementationAsChild set to true + const options = new mx.DefinitionOptions(); + options.addImplementationAsChild = false + + const nodeDef2 = doc.addNodeDefFromGraph( + nodeGraph, + 'ND_wrapper_2', + 'texture', + 'NG_wrapper_2', + options + ); + + expect(nodeDef2).to.exist; + + // Verify the implementation was created as a referenced implementation + const impl2 = nodeDef2.getImplementation(); + expect(impl2).to.exist; + expect(impl2.isANodeGraph()) + expect(impl2.getName()).to.equal('NG_wrapper_2'); + const nodedef_string = impl2.getNodeDefString(); + expect(nodedef_string).to.equal('ND_wrapper_2'); + + // Cleanup + options.delete(); + doc.delete(); + }); }); diff --git a/resources/Materials/TestSuite/stdlib/definition/functional_nodedef.mtlx b/resources/Materials/TestSuite/stdlib/definition/functional_nodedef.mtlx new file mode 100644 index 0000000000..891c86c07e --- /dev/null +++ b/resources/Materials/TestSuite/stdlib/definition/functional_nodedef.mtlx @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/source/JsMaterialX/JsMaterialXCore/JsDocument.cpp b/source/JsMaterialX/JsMaterialXCore/JsDocument.cpp index 18750666b7..84898ce2a6 100644 --- a/source/JsMaterialX/JsMaterialXCore/JsDocument.cpp +++ b/source/JsMaterialX/JsMaterialXCore/JsDocument.cpp @@ -16,6 +16,11 @@ namespace mx = MaterialX; EMSCRIPTEN_BINDINGS(document) { ems::function("createDocument", &mx::createDocument); + + ems::class_("DefinitionOptions") + .constructor<>() + .property("addImplementationAsChild", &mx::DefinitionOptions::addImplementationAsChild); + ems::class_>("Document") .smart_ptr_constructor("Document", &std::make_shared) .smart_ptr>("Document") @@ -62,8 +67,8 @@ EMSCRIPTEN_BINDINGS(document) .function("getTypeDefs", &mx::Document::getTypeDefs) .function("removeTypeDef", &mx::Document::removeTypeDef) BIND_MEMBER_FUNC("addNodeDef", mx::Document, addNodeDef, 0, 3, stRef, stRef, stRef) - BIND_MEMBER_FUNC("addNodeDefFromGraph", mx::Document, addNodeDefFromGraph, 4, 4, mx::NodeGraphPtr, - const std::string&, const std::string&, const std::string&) + BIND_MEMBER_FUNC_RAW_PTR("addNodeDefFromGraph", mx::Document, addNodeDefFromGraph, 4, 5, mx::NodeGraphPtr, + const std::string&, const std::string&, const std::string&, mx::DefinitionOptions*) .function("getNodeDef", &mx::Document::getNodeDef) .function("getNodeDefs", &mx::Document::getNodeDefs) .function("removeNodeDef", &mx::Document::removeNodeDef) diff --git a/source/MaterialXCore/Definition.cpp b/source/MaterialXCore/Definition.cpp index d69b9265fe..76a3d3f41a 100644 --- a/source/MaterialXCore/Definition.cpp +++ b/source/MaterialXCore/Definition.cpp @@ -7,6 +7,8 @@ #include +#include + MATERIALX_NAMESPACE_BEGIN const string COLOR_SEMANTIC = "color"; @@ -156,6 +158,86 @@ ConstInterfaceElementPtr NodeDef::getDeclaration(const string&) const return getSelf()->asA(); } +StringVec NodeDef::getMatchingDefinitions() const +{ + StringVec result = { getName() }; + ElementPtr base = getInheritsFrom(); + while (base) + { + result.push_back(base->getName()); + base = base->getInheritsFrom(); + } + return result; +} + +bool NodeDef::hasSharedImplementation(const string& target) const +{ + bool hasSharedImplementations = false; + + StringVec definitionNames = getMatchingDefinitions(); + StringSet implementationNames; + for (const string& definitionName : definitionNames) + { + NodeDefPtr definition = getDocument()->getNodeDef(definitionName); + InterfaceElementPtr implementation = definition ? definition->getImplementation(target) : nullptr; + if (implementation) + { + const string& implementationName = implementation->getName(); + if (implementationNames.find(implementationName) != implementationNames.end()) + { + hasSharedImplementations = true; + break; + } + else + { + implementationNames.insert(implementationName); + } + } + } + return hasSharedImplementations; +} + +InterfaceElementPtr NodeDef::inlineImplementation(const string& target, bool requireExclusive) +{ + // Do not allow inlining if shared implementations exist and exclusivity is required. + if (requireExclusive && hasSharedImplementation(target)) + { + return nullptr; + } + + InterfaceElementPtr impl = getImplementation(target); + if (!impl) + { + return nullptr; + } + + // Firewall check to only support functional nodegraphs for now. + StringSet supportedCategories = { Implementation::NODE_GRAPH_ATTRIBUTE }; + if (supportedCategories.find(impl->getCategory()) == supportedCategories.end()) + { + return nullptr; + } + + string newImplName = impl->getName(); + newImplName = createValidChildName(newImplName); + ElementPtr newImpl= addChildOfCategory(impl->getCategory(), newImplName); + if (!newImpl) + { + return nullptr; + } + + InterfaceElementPtr newInterface = newImpl->asA(); + if (newInterface) + { + // Remove the definition back-reference otherwise the existing implementation will be + // used instead of the new child implementation. + impl->removeAttribute(InterfaceElement::NODE_DEF_ATTRIBUTE); + + newImpl->copyContentFrom(impl); + } + return newInterface; +} + // // Implementation methods // @@ -174,6 +256,12 @@ void Implementation::setNodeDef(ConstNodeDefPtr nodeDef) NodeDefPtr Implementation::getNodeDef() const { + ElementPtr parent = getSelfNonConst()->getParent(); + NodeDefPtr nodedef = parent->asA(); + if (nodedef) + { + return nodedef; + } return resolveNameReference(getNodeDefString()); } diff --git a/source/MaterialXCore/Definition.h b/source/MaterialXCore/Definition.h index 01595b0c53..eb366afaf6 100644 --- a/source/MaterialXCore/Definition.h +++ b/source/MaterialXCore/Definition.h @@ -177,6 +177,26 @@ class MX_CORE_API NodeDef : public InterfaceElement /// by the given target name. ConstInterfaceElementPtr getDeclaration(const string& target = EMPTY_STRING) const override; + /// Get definitions that this definition inherits from. + StringVec getMatchingDefinitions() const; + + /// Check to see if an implementation is reused across more than one definition + /// @param target Target name. If empty will use the first matching implementation. + bool hasSharedImplementation(const string& target = EMPTY_STRING) const; + + /// For the given target, this method locates associated implementation, + /// creates an copy of each implementation as a child of this definition, + /// and removes the original association between the implementation and the definition. + /// The original implementation element remain unchanged elsewhere in the document. + /// + /// Currently only implementations which are functional node graphs are supported. + /// + /// @param target Target name. If empty will use the first matching implementation. + /// @param requireExclusive If true and a shared implementation is encountered, + /// the method will not inline that implementation. + /// @return The reference to the child graph, or nullptr if no implementation was found. + InterfaceElementPtr inlineImplementation(const string& target = EMPTY_STRING, bool requireExclusive = false); + /// @} public: diff --git a/source/MaterialXCore/Document.cpp b/source/MaterialXCore/Document.cpp index ec8bb54e84..59eb5f214f 100644 --- a/source/MaterialXCore/Document.cpp +++ b/source/MaterialXCore/Document.cpp @@ -103,6 +103,7 @@ class Document::Cache _portElementMap.clear(); _nodeDefMap.clear(); _implementationMap.clear(); + std::unordered_map> funcNodeDefMap; // Traverse the document to build a new cache. for (ElementPtr elem : doc->traverseTree()) @@ -127,6 +128,13 @@ class Document::Cache if (nodeDef) { _nodeDefMap[nodeDef->getQualifiedName(nodeString)].push_back(nodeDef); + + // Look for child nodegraph implementations + vector nodeGraphs = nodeDef->getChildrenOfType(); + for (NodeGraphPtr nodeGraph : nodeGraphs) + { + funcNodeDefMap[elem->getName()].push_back(nodeGraph); + } } } if (!nodeDefString.empty()) @@ -140,6 +148,16 @@ class Document::Cache } } } + + // Functional node definitions have lower precedence than non-functional ones. + // So append them to the back of implementation list associated + // with any existing nodedef entry (or create a new one if does not exist). + // + for (const auto& [nodedefKey, appendImplementations] : funcNodeDefMap) + { + auto& implementations = _implementationMap[nodedefKey]; + implementations.insert(implementations.end(), appendImplementations.begin(), appendImplementations.end()); + } } _valid = true; @@ -178,7 +196,8 @@ void Document::initialize() } NodeDefPtr Document::addNodeDefFromGraph(NodeGraphPtr nodeGraph, const string& nodeDefName, - const string& category, const string& newGraphName) + const string& category, const string& newGraphName, + DefinitionOptions *options) { if (category.empty()) { @@ -190,14 +209,20 @@ NodeDefPtr Document::addNodeDefFromGraph(NodeGraphPtr nodeGraph, const string& n throw Exception("Cannot create duplicate nodedef: " + nodeDefName); } - if (getNodeGraph(newGraphName)) + bool addAsChild = options ? options->addImplementationAsChild : false; + + if (!addAsChild && getNodeGraph(newGraphName)) { throw Exception("Cannot create duplicate nodegraph: " + newGraphName); } + // Create a new nodedef and set its category + NodeDefPtr nodeDef = addNodeDef(nodeDefName, EMPTY_STRING); + nodeDef->setNodeString(category); + // Create a new functional nodegraph, and copy over the // contents from the compound nodegraph - NodeGraphPtr graph = addNodeGraph(newGraphName); + NodeGraphPtr graph = !addAsChild ? addNodeGraph(newGraphName) : nodeDef->addChild(newGraphName); graph->copyContentFrom(nodeGraph); for (auto graphChild : graph->getChildren()) @@ -205,11 +230,13 @@ NodeDefPtr Document::addNodeDefFromGraph(NodeGraphPtr nodeGraph, const string& n graphChild->removeAttribute(Element::XPOS_ATTRIBUTE); graphChild->removeAttribute(Element::YPOS_ATTRIBUTE); } - graph->setNodeDefString(nodeDefName); - // Create a new nodedef and set its category - NodeDefPtr nodeDef = addNodeDef(nodeDefName, EMPTY_STRING); - nodeDef->setNodeString(category); + // Reference from nodegraph to nodedef is not required + // as the graph is a child of the nodedef. + if (!addAsChild) + { + graph->setNodeDefString(nodeDefName); + } // Expose any existing interfaces from the graph. // Any connection attributes ("nodegraph", "nodename", "interfacename") on the diff --git a/source/MaterialXCore/Document.h b/source/MaterialXCore/Document.h index ab01bbbc11..0e002da7ab 100644 --- a/source/MaterialXCore/Document.h +++ b/source/MaterialXCore/Document.h @@ -23,6 +23,8 @@ using DocumentPtr = shared_ptr; /// A shared pointer to a const Document using ConstDocumentPtr = shared_ptr; +class DefinitionOptions; + /// @class Document /// A MaterialX document, which represents the top-level element in the /// MaterialX ownership hierarchy. @@ -361,9 +363,11 @@ class MX_CORE_API Document : public GraphElement /// @param newGraphName Name of new functional NodeGraph. /// @param nodeDefName Name of new NodeDef /// @param category Category of the new NodeDef + /// @param options Optional creation options. Default is nullptr. /// @return New declaration if successful. NodeDefPtr addNodeDefFromGraph(NodeGraphPtr nodeGraph, const string& nodeDefName, - const string& category, const string& newGraphName); + const string& category, const string& newGraphName, + DefinitionOptions * options = nullptr); /// Return the NodeDef, if any, with the given name. NodeDefPtr getNodeDef(const string& name) const @@ -708,6 +712,15 @@ class MX_CORE_API Document : public GraphElement std::unique_ptr _cache; }; +/// @class DefinitionOptions +/// Options for defining a NodeDef from an implementation +class MX_CORE_API DefinitionOptions +{ +public: + /// Add implementation as child of NodeDef as opposed a sibling. + bool addImplementationAsChild = false; +}; + /// Create a new Document. /// @relates Document MX_CORE_API DocumentPtr createDocument(); diff --git a/source/MaterialXCore/Node.cpp b/source/MaterialXCore/Node.cpp index f5923f337c..4a1f4a39c8 100644 --- a/source/MaterialXCore/Node.cpp +++ b/source/MaterialXCore/Node.cpp @@ -741,7 +741,14 @@ void NodeGraph::modifyInterfaceName(const string& inputPath, const string& inter NodeDefPtr NodeGraph::getNodeDef() const { - NodeDefPtr nodedef = resolveNameReference(getNodeDefString()); + ElementPtr parent = getSelfNonConst()->getParent(); + NodeDefPtr nodedef = parent->asA(); + if (nodedef) + { + return nodedef; + } + + nodedef = resolveNameReference(getNodeDefString()); // If not directly defined look for an implementation which has a nodedef association if (!nodedef) { diff --git a/source/MaterialXTest/MaterialXCore/Node.cpp b/source/MaterialXTest/MaterialXCore/Node.cpp index 6031799afb..0d87792c90 100644 --- a/source/MaterialXTest/MaterialXCore/Node.cpp +++ b/source/MaterialXTest/MaterialXCore/Node.cpp @@ -675,7 +675,130 @@ TEST_CASE("Organization", "[nodegraph]") CHECK(nodeGraph->getBackdrops().empty()); } -TEST_CASE("Node Definition Creation", "[nodedef]") +void testFunctionalNodeDef() +{ + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr doc = mx::createDocument(); + mx::readFromXmlFile(doc, "resources/Materials/TestSuite/stdlib/definition/functional_nodedef.mtlx", searchPath); + + std::vector nodedefs = doc->getNodeDefs(); + for (mx::NodeDefPtr nodeDef : nodedefs) + { + std::string nodeDefName = nodeDef->getName(); + std::string nodeGraphName = "NG_" + nodeDefName.substr(3); // Remove the 'ND_' prefix + + mx::InterfaceElementPtr implementation = nodeDef->getImplementation(); + REQUIRE(implementation != nullptr); + mx::NodeGraphPtr functionalNodeGraph = implementation->asA(); + REQUIRE(functionalNodeGraph != nullptr); + if (functionalNodeGraph) + { + // Test that the child nodegraph is found via implementation search + REQUIRE(functionalNodeGraph->getName() == nodeGraphName); + + // Test that this is actually a child nodegraph of the NodeDef + std::vector childNodeGraphs = nodeDef->getChildrenOfType(); + for (mx::NodeGraphPtr childNodeGraph : childNodeGraphs) + { + if (childNodeGraph->getName() == nodeGraphName) + { + // Test that the child nodegraph is the functional graph + REQUIRE(childNodeGraph == functionalNodeGraph); + } + } + } + + std::string referenceGraphName = nodeGraphName + "_reference"; + mx::NodeGraphPtr referenceNodeGraph = doc->getNodeGraph(referenceGraphName); + REQUIRE(referenceNodeGraph != nullptr); + if (referenceNodeGraph) + { + referenceNodeGraph->setNodeDefString(nodeDefName); + + implementation = nodeDef->getImplementation(); + REQUIRE(implementation != nullptr); + std::string msg = "Testing NodeDef: " + nodeDefName + " with implementation: " + implementation->getName(); + INFO(msg); + functionalNodeGraph = implementation->asA(); + REQUIRE(functionalNodeGraph != nullptr); + if (functionalNodeGraph) + { + // Test the functional node graph is the reference graph + REQUIRE(functionalNodeGraph->getName() == referenceGraphName); + } + } + } +} + +void testInlineImplementation() +{ + mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); + mx::DocumentPtr stdlib = mx::createDocument(); + mx::loadLibraries({ "libraries" }, searchPath, stdlib); + + // Test inline nodegraph implementation + std::string testName = "ND_tiledimage_color3"; + mx::NodeDefPtr nodeDef = stdlib->getNodeDef(testName); + mx::InterfaceElementPtr prevImpl = nodeDef->getImplementation(); + mx::InterfaceElementPtr newImpl = nodeDef->inlineImplementation(mx::EMPTY_STRING, false); + REQUIRE(newImpl != nullptr); + + // Checks for validity of inlined implementation + REQUIRE(newImpl->getName() == prevImpl->getName()); + REQUIRE(newImpl->getNodeDefString().empty()); + REQUIRE(prevImpl->getNodeDefString().empty()); + + // Try to refind the inlined implementation and make sure they are equivalent + mx::ElementPtr findImpl = nodeDef->getImplementation(); + REQUIRE(findImpl != prevImpl); + REQUIRE(newImpl->getName() == findImpl->getName()); + mx::ElementEquivalenceOptions options; + std::string message; + bool equivalent = newImpl->isEquivalent(prevImpl, options, &message); + REQUIRE(equivalent == true); + WARN("New impl: " + newImpl->getNamePath() + " is equivalent to old impl: " + prevImpl->getNamePath() + "\n" + mx::prettyPrint(nodeDef)); + + // Firewall test to disallow inlining non-graph implementations + testName = "ND_position_vector3"; + nodeDef = stdlib->getNodeDef(testName); + newImpl = nodeDef->inlineImplementation(mx::EMPTY_STRING, false); + REQUIRE(newImpl == nullptr); + + // Convert entire inhertance chain for a definition + testName = "ND_UsdUVTexture"; + nodeDef = stdlib->getNodeDef(testName); + mx::StringVec matchingDefs = nodeDef->getMatchingDefinitions(); + REQUIRE(matchingDefs.size() > 1); + bool hasSharedImplementation = nodeDef->hasSharedImplementation(); + REQUIRE(hasSharedImplementation == false); + for (const std::string& defName : matchingDefs) + { + nodeDef = stdlib->getNodeDef(defName); + prevImpl = nodeDef->getImplementation(); + newImpl = nodeDef->inlineImplementation(mx::EMPTY_STRING, false); + REQUIRE(newImpl != nullptr); + equivalent = newImpl->isEquivalent(prevImpl, options, &message); + REQUIRE(equivalent == true); + } + + // Test for disallowing inline shared implementations + testName = "ND_standard_surface_surfaceshader"; + nodeDef = stdlib->getNodeDef(testName); + hasSharedImplementation = nodeDef->hasSharedImplementation(); + REQUIRE(hasSharedImplementation == true); + newImpl = nodeDef->inlineImplementation(mx::EMPTY_STRING, true); + REQUIRE(newImpl == nullptr); + + // Test flag to ignore shared implementations + nodeDef = stdlib->getNodeDef(testName); + prevImpl = nodeDef->getImplementation(); + newImpl = nodeDef->inlineImplementation(mx::EMPTY_STRING, false); + REQUIRE(newImpl != nullptr); + equivalent = newImpl->isEquivalent(prevImpl, options, &message); + REQUIRE(equivalent == true); +} + +void testNodeDefCreationFromGraph(mx::DefinitionOptions options) { mx::FileSearchPath searchPath = mx::getDefaultDataSearchPath(); mx::DocumentPtr stdlib = mx::createDocument(); @@ -714,10 +837,12 @@ TEST_CASE("Node Definition Creation", "[nodedef]") bool isDefaultVersion = false; const std::string NODENAME = graph->getName(); + // Create a new functional graph and definition from a compound graph + bool addAsChild = options.addImplementationAsChild; std::string newNodeDefName = doc->createValidChildName("ND_" + graph->getName()); std::string newGraphName = doc->createValidChildName("NG_" + graph->getName()); - mx::NodeDefPtr nodeDef = doc->addNodeDefFromGraph(graph, newNodeDefName, NODENAME, newGraphName); + mx::NodeDefPtr nodeDef = doc->addNodeDefFromGraph(graph, newNodeDefName, NODENAME, newGraphName, &options); REQUIRE(nodeDef != nullptr); nodeDef->setVersionString(VERSION1); nodeDef->setDefaultVersion(isDefaultVersion); @@ -759,9 +884,16 @@ TEST_CASE("Node Definition Creation", "[nodedef]") } // Check validity of new functional nodegraph - mx::NodeGraphPtr newGraph = doc->getNodeGraph(newGraphName); + mx::NodeGraphPtr newGraph = !addAsChild ? doc->getNodeGraph(newGraphName) : nodeDef->getChildOfType(newGraphName); REQUIRE(newGraph != nullptr); - REQUIRE(newGraph->getNodeDefString() == newNodeDefName); + if (!addAsChild) + { + REQUIRE(newGraph->getNodeDefString() == newNodeDefName); + } + else + { + REQUIRE(newGraph->getNodeDefString().empty()); + } mx::ConstInterfaceElementPtr decl = newGraph->getDeclaration(); REQUIRE(decl->getName() == nodeDef->getName()); REQUIRE(doc->validate()); @@ -826,6 +958,33 @@ TEST_CASE("Node Definition Creation", "[nodedef]") REQUIRE(doc->validate()); } +TEST_CASE("Node Definition Creation", "[nodedef_create]") +{ + mx::DefinitionOptions defOptions; + + SECTION("Without implementation as child") + { + defOptions.addImplementationAsChild = false; + testNodeDefCreationFromGraph(defOptions); + } + + SECTION("With implementation as child") + { + defOptions.addImplementationAsChild = true; + testNodeDefCreationFromGraph(defOptions); + } + + SECTION("Functional NodeDef test") + { + testFunctionalNodeDef(); + } + + SECTION("Functional Nodef Inlining test") + { + testInlineImplementation(); + } +} + TEST_CASE("Set Name Global", "[node, nodegraph]") { mx::DocumentPtr doc = mx::createDocument(); diff --git a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp index 98e02a3991..be549801b8 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDefinition.cpp @@ -25,6 +25,13 @@ void bindPyDefinition(py::module& mod) py::arg("target") = mx::EMPTY_STRING, py::arg("resolveNodeGraph") = true) .def("isVersionCompatible", &mx::NodeDef::isVersionCompatible) + .def("getDeclaration", &mx::NodeDef::getDeclaration, + py::arg("target") = mx::EMPTY_STRING) + .def("getMatchingDefinitions", &mx::NodeDef::getMatchingDefinitions) + .def("hasSharedImplementation", &mx::NodeDef::hasSharedImplementation, + py::arg("target") = mx::EMPTY_STRING) + .def("inlineImplementation", &mx::NodeDef::inlineImplementation, + py::arg("target") = mx::EMPTY_STRING, py::arg("requireExclusive") = false) .def_readonly_static("CATEGORY", &mx::NodeDef::CATEGORY) .def_readonly_static("NODE_ATTRIBUTE", &mx::NodeDef::NODE_ATTRIBUTE) .def_readonly_static("TEXTURE_NODE_GROUP", &mx::NodeDef::TEXTURE_NODE_GROUP) diff --git a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp index e021bc92de..caadcd77ff 100644 --- a/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp +++ b/source/PyMaterialX/PyMaterialXCore/PyDocument.cpp @@ -26,6 +26,9 @@ void bindPyDocument(py::module& mod) { mod.def("createDocument", &mx::createDocument); + py::class_(mod, "DefinitionOptions") + .def_readwrite("addImplementationAsChild", &mx::DefinitionOptions::addImplementationAsChild); + py::class_(mod, "Document") .def("initialize", &mx::Document::initialize) .def("copy", &mx::Document::copy) @@ -74,7 +77,7 @@ void bindPyDocument(py::module& mod) .def("removeTypeDef", &mx::Document::removeTypeDef) .def("addNodeDef", &mx::Document::addNodeDef, py::arg("name") = mx::EMPTY_STRING, py::arg("type") = mx::DEFAULT_TYPE_STRING, py::arg("node") = mx::EMPTY_STRING) - .def("addNodeDefFromGraph", (mx::NodeDefPtr (mx::Document::*)(mx::NodeGraphPtr, const std::string&, const std::string&, const std::string&)) & mx::Document::addNodeDefFromGraph) + .def("addNodeDefFromGraph", (mx::NodeDefPtr (mx::Document::*)(mx::NodeGraphPtr, const std::string&, const std::string&, const std::string&, mx::DefinitionOptions*)) & mx::Document::addNodeDefFromGraph) .def("addNodeDefFromGraph", (mx::NodeDefPtr(mx::Document::*)(mx::NodeGraphPtr, const std::string&, const std::string&, const std::string&, bool, const std::string&, const std::string& )) & PyBindDocument::old_addNodeDefFromGraph) .def("getNodeDef", &mx::Document::getNodeDef)