Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions resources/Materials/TestSuite/stdlib/nodegraphs/nested_graphs.mtlx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?xml version="1.0"?>
<materialx version="1.38" colorspace="lin_rec709">
<nodegraph name="parentNG">
<input name="parentNGInput" type="color3" value="1, 1, 0.2" />
<input name="parentNGInput2" type="color3" value="0.2, 1, 1" />
<input name="parentNGInput3" type="color3" value="1, 0.2, 1" />
<nodegraph name="childNG">
<input name="childNGInput" type="color3" interfacename="parentNGInput" />
<input name="childNGInput2" type="color3" interfacename="parentNGInput2" />
<multiply name="multiplyNode" type="color3">
<input name="in1" type="color3" interfacename="childNGInput" />
<input name="in2" type="color3" interfacename="childNGInput2" />
</multiply>
<output name="childNGOutput" type="color3" nodename="multiplyNode" />
</nodegraph>
<multiply name="parentmultiplyNode" type="color3">
<input name="in1" type="color3" nodegraph="childNG" output="childNGOutput" />
<input name="in2" type="color3" interfacename="parentNGInput3" />
</multiply>
<output name="parentNGOutput1" type="color3" nodegraph="childNG" />
<output name="parentNGOutput2" type="color3" nodename="parentmultiplyNode" />
</nodegraph>
<standard_surface name="nested_graph_shader" type="surfaceshader">
<input name="base_color" type="color3" output="parentNGOutput1" nodegraph="parentNG" />
</standard_surface>
<surfacematerial name="nested_graph_material" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="nested_graph_shader" />
<input name="displacementshader" type="displacementshader" value="" />
</surfacematerial>
<standard_surface name="nested_graph_shader2" type="surfaceshader">
<input name="base_color" type="color3" nodegraph="parentNG" output="parentNGOutput2" />
</standard_surface>
<surfacematerial name="nested_graph_material2" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="nested_graph_shader2" />
<input name="displacementshader" type="displacementshader" value="" />
</surfacematerial>
</materialx>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0"?>
<materialx version="1.39">
<nodegraph name="level1_graph">
<nodegraph name="level2_graph">
<nodegraph name="level3_graph">
<input name="level3_input" type="color3" interfacename="level2_input2" />
<output name="level3_output" type="color3" nodename="invert_color3" />
<invert name="invert_color3" type="color3">
<input name="in" type="color3" interfacename="level3_input" />
</invert>
</nodegraph>
<input name="level2_input" type="color3" interfacename="level1_input" />
<input name="level2_input2" type="color3" interfacename="level1_input2" />
<output name="level2_output" type="color3" nodename="color3" />
<add name="color3" type="color3">
<input name="in1" type="color3" interfacename="level2_input2" />
<input name="in2" type="color3" interfacename="level2_input" />
</add>
<output name="level2_output2" type="color3" nodegraph="level3_graph" output="level3_output" />
</nodegraph>
<input name="level1_input" type="color3" value="0.216726, 0.0162733, 0.128939" />
<input name="level1_input2" type="color3" value="0.366403, 0.691541, 0.251566" />
<multiply name="color3" type="color3">
<input name="in1" type="color3" nodegraph="level2_graph" output="level2_output" />
</multiply>
<output name="level1_output" type="color3" nodegraph="level2_graph" />
<output name="level1_output2" type="color3" output="level2_output2" nodegraph="level2_graph" />
</nodegraph>
<output name="doc_output" type="color3" nodegraph="level1_graph" output="level1_output2" />
<surface_unlit name="surface_unlit" type="surfaceshader">
<input name="emission_color" type="color3" output="level1_output" nodegraph="level1_graph" />
</surface_unlit>
<surface_unlit name="surface_unlit2" type="surfaceshader">
<input name="emission_color" type="color3" output="level1_output2" nodegraph="level1_graph" />
</surface_unlit>
</materialx>
32 changes: 0 additions & 32 deletions source/MaterialXCore/Document.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,38 +84,6 @@ class MX_CORE_API Document : public GraphElement
/// @param library The data library to be imported.
void importLibrary(const ConstDocumentPtr& library);

/// @}
/// @name NodeGraph Elements
/// @{

/// Add a NodeGraph to the document.
/// @param name The name of the new NodeGraph.
/// If no name is specified, then a unique name will automatically be
/// generated.
/// @return A shared pointer to the new NodeGraph.
NodeGraphPtr addNodeGraph(const string& name = EMPTY_STRING)
{
return addChild<NodeGraph>(name);
}

/// Return the NodeGraph, if any, with the given name.
NodeGraphPtr getNodeGraph(const string& name) const
{
return getChildOfType<NodeGraph>(name);
}

/// Return a vector of all NodeGraph elements in the document.
vector<NodeGraphPtr> getNodeGraphs() const
{
return getChildrenOfType<NodeGraph>();
}

/// Remove the NodeGraph, if any, with the given name.
void removeNodeGraph(const string& name)
{
removeChildOfType<NodeGraph>(name);
}

/// Return a vector of all port elements that match the given node name.
/// Port elements support spatially-varying upstream connections to
/// nodes, and include both Input and Output elements.
Expand Down
1 change: 1 addition & 0 deletions source/MaterialXCore/Element.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -813,6 +813,7 @@ bool ValueElement::validate(string* message) const
if (hasInterfaceName())
{
validateRequire(isA<Input>() || isA<Token>(), res, message, "Only input and token elements support interface names");

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No change. ( I had changes but the parent scope additions by Jonathan is a better fix. )

ConstGraphElementPtr graph = getAncestorOfType<GraphElement>();
if (graph && graph == getParent())
{
Expand Down
147 changes: 122 additions & 25 deletions source/MaterialXCore/Interface.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,13 +87,17 @@ OutputPtr PortElement::getConnectedOutput() const
ConstElementPtr parent = getParent();
ConstElementPtr scope = parent ? parent->getParent() : nullptr;

// Look for a nodegraph output.
// Look for a nodegraph output. Need to check parent, grandparent and document (root) scope.
if (hasNodeGraphString())
{
NodeGraphPtr nodeGraph = resolveNameReference<NodeGraph>(getNodeGraphString(), scope);
if (!nodeGraph)
{
nodeGraph = resolveNameReference<NodeGraph>(getNodeGraphString());
nodeGraph = resolveNameReference<NodeGraph>(getNodeGraphString(), parent);
if (!nodeGraph)
{
nodeGraph = resolveNameReference<NodeGraph>(getNodeGraphString());
}
}
if (nodeGraph)
{
Expand All @@ -110,6 +114,12 @@ OutputPtr PortElement::getConnectedOutput() const
}
}
}

// Handle direct nodegraph -> nodegraph output connections
while (result && result->hasNodeGraphString())
{
result = result->getConnectedOutput();
}
}
// Look for a node output.
else if (hasNodeName())
Expand Down Expand Up @@ -149,42 +159,60 @@ bool PortElement::validate(string* message) const
{
bool res = true;

NodePtr connectedNode = getConnectedNode();
if (hasNodeName() || hasOutputString())
const string& outputString = getOutputString();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rework validation a bit so that you can have upstream nodegraph references

InterfaceElementPtr connectedElement = nullptr;
NodePtr connectedNode = nullptr;
NodeGraphPtr connectedGraph = nullptr;
if (hasNodeName())
{
NodeGraphPtr nodeGraph = resolveNameReference<NodeGraph>(getNodeName());
if (!nodeGraph)
connectedElement = connectedNode = getConnectedNode();
}
else if (hasNodeGraphString())
{
const string& nodeGraphString = getNodeGraphString();
connectedGraph = resolveNameReference<NodeGraph>(nodeGraphString);
if (!connectedGraph)
{
validateRequire(connectedNode != nullptr, res, message, "Invalid port connection");
ConstElementPtr parent = getParent();
if (parent)
{
connectedGraph = resolveNameReference<NodeGraph>(nodeGraphString, parent);
if (!connectedGraph)
{
ConstElementPtr grandparent = parent->getParent();
connectedGraph = resolveNameReference<NodeGraph>(nodeGraphString, grandparent);
}
}
}
connectedElement = connectedGraph;
validateRequire(connectedGraph != nullptr, res, message,
"Nodegraph '" + nodeGraphString + "' not found for connection");
}
if (connectedNode)

if (connectedElement)
{
const string& outputString = getOutputString();
if (!outputString.empty())
{
OutputPtr output;
if (hasNodeName())
OutputPtr output = nullptr;
if (connectedNode)
{
NodeDefPtr nodeDef = connectedNode->getNodeDef();
output = nodeDef ? nodeDef->getOutput(outputString) : OutputPtr();
if (output)
{
validateRequire(connectedNode->getType() == MULTI_OUTPUT_TYPE_STRING, res, message,
"Multi-output type expected in port connection");
"Multi-output type expected in port connection'");
}
}
else if (hasNodeGraphString())
else if (connectedGraph)
{
NodeGraphPtr nodeGraph = resolveNameReference<NodeGraph>(getNodeGraphString());
if (nodeGraph)
output = connectedGraph->getOutput(outputString);
validateRequire(output != nullptr, res, message,
"Nodegraph output '" + outputString + "' not found for connection");
if (connectedGraph->getNodeDef())
{
output = nodeGraph->getOutput(outputString);
if (nodeGraph->getNodeDef())
{
validateRequire(nodeGraph->getOutputCount() > 1, res, message,
"Multi-output type expected in port connection");
}
validateRequire(connectedGraph->getOutputCount() > 1, res, message,
"Multi-output type expected in port connection");
}
}
else
Expand All @@ -196,14 +224,44 @@ bool PortElement::validate(string* message) const

if (output)
{
validateRequire(getType() == output->getType(), res, message, "Mismatched types in port connection");
validateRequire(getType() == output->getType(), res, message, "Mismatched types in port connection:" +
getType() + " versus " + output->getType());
}
}
else if (connectedNode->getType() != MULTI_OUTPUT_TYPE_STRING)

// Check first output. Special case multioutput nodes which are not supposed to have an output
// versus nodegraphs which must have an output specified.
else

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Extra logic to handle special casing nodes which may or may not have an output. It would be great to get rid of dis-allowing output name if not multioutput.

{
validateRequire(getType() == connectedNode->getType(), res, message, "Mismatched types in port connection");
OutputPtr output = nullptr;
string outputType = EMPTY_STRING;

if (connectedNode)
{
outputType = connectedNode->getType();
}
else if (connectedGraph)
{
std::vector<OutputPtr> outputs = connectedGraph->getOutputs();
validateRequire(!outputs.empty(), res, message, "Nodegraph has no outputs for port connection");
if (!outputs.empty())
{
output = outputs[0];
outputType = output->getType();
}
}

if (!outputType.empty())
{
if (outputType != MULTI_OUTPUT_TYPE_STRING)
{
validateRequire(getType() == outputType, res, message, "Mismatched types in port connection:" +
getType() + " versus " + outputType);
}
}
}
}

return ValueElement::validate(message) && res;
}

Expand Down Expand Up @@ -275,11 +333,50 @@ InputPtr Input::getInterfaceInput() const
{
if (hasInterfaceName())
{
const string& interfaceName = getInterfaceName();
ConstGraphElementPtr graph = getAncestorOfType<GraphElement>();
if (graph)
if (graph && graph == getParent())
{
// This element is a direct child of a graph element, so its
// interface name references a value element in the parent scope.
ConstElementPtr graphParent = graph->getParent();
graph = graphParent ? graphParent->getAncestorOfType<GraphElement>() : nullptr;
}
ConstNodeGraphPtr nodeGraph = graph ? graph->asA<NodeGraph>() : nullptr;
if (!nodeGraph)
{
return graph->getInput(getInterfaceName());
}
if (nodeGraph)
{
// If this is a functional nodegraph, the input is defined by the nodegraph's nodedef
NodeDefPtr nodedef = nodeGraph->getNodeDef();
if (nodedef)
{
return nodedef->getInput(interfaceName);
}
// Otherwise traverse through interface element connections recursivley.
else

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allow traversal through interface / interface connections properly.

{
InputPtr returnVal = nodeGraph->getInput(interfaceName);
if (returnVal)
{
if (returnVal->hasInterfaceName())
{
InputPtr val = returnVal->getInterfaceInput();
if (val)
{
returnVal = val;
}
return returnVal;
}
else
{
return returnVal;
}
}
}
}
}
return nullptr;
}
Expand Down
32 changes: 32 additions & 0 deletions source/MaterialXCore/Node.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,38 @@ class MX_CORE_API GraphElement : public InterfaceElement
removeChildOfType<Backdrop>(name);
}

/// @}

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just a migration from Document to GraphElement to allow exposure beyond the root level for graph operations.

/// @name NodeGraph Elements
/// @{

/// Add a NodeGraph to the document.
/// @param name The name of the new NodeGraph.
/// If no name is specified, then a unique name will automatically be
/// generated.
/// @return A shared pointer to the new NodeGraph.
NodeGraphPtr addNodeGraph(const string& name = EMPTY_STRING)
{
return addChild<NodeGraph>(name);
}

/// Return the NodeGraph, if any, with the given name.
NodeGraphPtr getNodeGraph(const string& name) const
{
return getChildOfType<NodeGraph>(name);
}

/// Return a vector of all NodeGraph elements in the document.
vector<NodeGraphPtr> getNodeGraphs() const
{
return getChildrenOfType<NodeGraph>();
}

/// Remove the NodeGraph, if any, with the given name.
void removeNodeGraph(const string& name)
{
removeChildOfType<NodeGraph>(name);
}

/// @}
/// @name Utility
/// @{
Expand Down
Loading
Loading