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)