Skip to content

Commit

Permalink
Fix downstream traversal in graph editor (AcademySoftwareFoundation#1349
Browse files Browse the repository at this point in the history
)

- Includes nodegraph references in port connection cache
- Adds NodeGraph::getDownstreamPorts() so downstream traversal is possible from graphs
- Update GraphEditor logic to use downstream traversal which will now properly traverse through nodegraphs.
  • Loading branch information
kwokcb authored Aug 24, 2023
1 parent 84adc65 commit d329ed7
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 67 deletions.
12 changes: 12 additions & 0 deletions source/MaterialXCore/Document.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,7 @@ class Document::Cache
for (ElementPtr elem : doc.lock()->traverseTree())
{
const string& nodeName = elem->getAttribute(PortElement::NODE_NAME_ATTRIBUTE);
const string& nodeGraphName = elem->getAttribute(PortElement::NODE_GRAPH_ATTRIBUTE);
const string& nodeString = elem->getAttribute(NodeDef::NODE_ATTRIBUTE);
const string& nodeDefString = elem->getAttribute(InterfaceElement::NODE_DEF_ATTRIBUTE);

Expand All @@ -97,6 +98,17 @@ class Document::Cache
portElementMap.emplace(portElem->getQualifiedName(nodeName), portElem);
}
}
else
{
if (!nodeGraphName.empty())
{
PortElementPtr portElem = elem->asA<PortElement>();
if (portElem)
{
portElementMap.emplace(portElem->getQualifiedName(nodeGraphName), portElem);
}
}
}
if (!nodeString.empty())
{
NodeDefPtr nodeDef = elem->asA<NodeDef>();
Expand Down
19 changes: 19 additions & 0 deletions source/MaterialXCore/Node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -728,6 +728,25 @@ InterfaceElementPtr NodeGraph::getImplementation() const
return nodedef ? nodedef->getImplementation() : InterfaceElementPtr();
}

vector<PortElementPtr> NodeGraph::getDownstreamPorts() const
{
vector<PortElementPtr> downstreamPorts;
for (PortElementPtr port : getDocument()->getMatchingPorts(getQualifiedName(getName())))
{
ElementPtr node = port->getParent();
ElementPtr graph = node ? node->getParent() : nullptr;
if (graph && graph->isA<GraphElement>() && graph == getParent())
{
downstreamPorts.push_back(port);
}
}
std::sort(downstreamPorts.begin(), downstreamPorts.end(), [](const ConstElementPtr& a, const ConstElementPtr& b)
{
return a->getName() > b->getName();
});
return downstreamPorts;
}

bool NodeGraph::validate(string* message) const
{
bool res = true;
Expand Down
8 changes: 8 additions & 0 deletions source/MaterialXCore/Node.h
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,14 @@ class MX_CORE_API NodeGraph : public GraphElement
/// none was found.
InterfaceElementPtr getImplementation() const;

/// @}
/// @name Traversal
/// @{

/// Return a vector of all downstream ports that connect to this graph, ordered by
/// the names of the port elements.
vector<PortElementPtr> getDownstreamPorts() const;

/// @}
/// @name Utility
/// @{
Expand Down
175 changes: 109 additions & 66 deletions source/MaterialXGraphEditor/Graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -644,8 +644,8 @@ void Graph::setPinColor()
_pinColor.insert(std::make_pair("BSDF", ImColor(10, 181, 150, 255)));
_pinColor.insert(std::make_pair("EDF", ImColor(255, 50, 100, 255)));
_pinColor.insert(std::make_pair("VDF", ImColor(0, 100, 151, 255)));
_pinColor.insert(std::make_pair("surfaceshader", ImColor(150, 255, 255, 255)));
_pinColor.insert(std::make_pair("material", ImColor(255, 255, 255, 255)));
_pinColor.insert(std::make_pair(mx::SURFACE_SHADER_TYPE_STRING, ImColor(150, 255, 255, 255)));
_pinColor.insert(std::make_pair(mx::MATERIAL_TYPE_STRING, ImColor(255, 255, 255, 255)));
_pinColor.insert(std::make_pair(mx::DISPLACEMENT_SHADER_TYPE_STRING, ImColor(155, 50, 100, 255)));
_pinColor.insert(std::make_pair(mx::VOLUME_SHADER_TYPE_STRING, ImColor(155, 250, 100, 255)));
_pinColor.insert(std::make_pair(mx::LIGHT_SHADER_TYPE_STRING, ImColor(100, 150, 100, 255)));
Expand Down Expand Up @@ -701,8 +701,13 @@ void Graph::selectMaterial(UiNodePtr uiNode)
// set the node to display in render veiw based off the selected node or nodegraph
void Graph::setRenderMaterial(UiNodePtr node)
{
// set render node right away is node is a material
if (node->getNode() && node->getNode()->getType() == "material")
// For now surface shaders and materials are considered renderable.
// This can be adjusted as desired to include being able to use outputs,
// and / a sub-graph in the nodegraph.
const mx::StringSet RENDERABLE_TYPES = { mx::MATERIAL_TYPE_STRING, mx::SURFACE_SHADER_TYPE_STRING };

// set render node right away is node is renderable
if (node->getNode() && RENDERABLE_TYPES.count(node->getNode()->getType()))
{
// only set new render node if different material has been selected
if (_currRenderNode != node)
Expand All @@ -712,94 +717,139 @@ void Graph::setRenderMaterial(UiNodePtr node)
_renderer->setMaterialCompilation(true);
}
}

// Traverse downstream looking for the first renderable element.
else
{
// continue downstream using output connections until a material node is found
std::vector<UiNodePtr> outNodes = node->getOutputConnections();
if (outNodes.size() > 0)
mx::NodePtr mtlxNode = node->getNode();
mx::NodeGraphPtr mtlxNodeGraph = node->getNodeGraph();
mx::OutputPtr mtlxOutput = node->getOutput();
if (mtlxOutput)
{
mx::ElementPtr parent = mtlxOutput->getParent();
if (parent->isA<mx::NodeGraph>())
mtlxNodeGraph = parent->asA<mx::NodeGraph>();
else if (parent->isA<mx::Node>())
mtlxNode = parent->asA<mx::Node>();
}
mx::StringSet testPaths;
if (mtlxNode)
{
if (outNodes[0]->getNode())
mx::ElementPtr parent = mtlxNode->getParent();
if (parent->isA<mx::NodeGraph>())
{
if (outNodes[0]->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING)
// There is no logic to support traversing from inside a functional graph
// to it's instance and hence downstream so skip this from consideration.
// The closest approach would be to "flatten" all definitions to compound graphs.
mx::NodeGraphPtr parentGraph = parent->asA<mx::NodeGraph>();
if (parentGraph->getNodeDef())
{
return;
}
}
testPaths.insert(mtlxNode->getNamePath());
}
else if (mtlxNodeGraph)
{
testPaths.insert(mtlxNodeGraph->getNamePath());
}

mx::NodePtr foundNode = nullptr;
while (!testPaths.empty() && !foundNode)
{
mx::StringSet nextPaths;
for (const std::string& testPath : testPaths)
{
mx::ElementPtr testElem = _graphDoc->getDescendant(testPath);
mx::NodePtr testNode = testElem->asA<mx::Node>();
std::vector<mx::PortElementPtr> downstreamPorts;
if (testNode)
{
downstreamPorts = testNode->getDownstreamPorts();
}
else
{
mx::NodeGraphPtr testGraph = testElem->asA<mx::NodeGraph>();
if (testGraph)
{
downstreamPorts = testGraph->getDownstreamPorts();
}
}
// Test all downstream ports. If the port's node is renderable
// then stop searching.
for (mx::PortElementPtr downstreamPort : downstreamPorts)
{
std::vector<UiNodePtr> shaderOut = outNodes[0]->getOutputConnections();
if (shaderOut.size() > 0)
mx::ElementPtr parent = downstreamPort->getParent();
if (parent)
{
if (shaderOut[0])
mx::NodePtr downstreamNode = parent->asA<mx::Node>();
if (downstreamNode)
{
if (shaderOut[0]->getNode()->getType() == "material")
mx::NodeDefPtr nodeDef = downstreamNode->getNodeDef();
if (nodeDef)
{
if (_currRenderNode != shaderOut[0])
if (RENDERABLE_TYPES.count(nodeDef->getType()))
{
_currRenderNode = shaderOut[0];
_frameCount = ImGui::GetFrameCount();
_renderer->setMaterialCompilation(true);
foundNode = downstreamNode;
break;
}
}
}
}
else
{
_currRenderNode = nullptr;
if (!foundNode)
{
nextPaths.insert(parent->getNamePath());
}
}
}
else if (outNodes[0]->getNode()->getType() == mx::MATERIAL_TYPE_STRING)
if (foundNode)
{
break;
}
}

// Set up next set of nodes to search downstream
testPaths = nextPaths;
}

// Update rendering. If found use that node, otherwise
// use the current fallback of using the first renderable node.
if (foundNode)
{
for (auto uiNode : _graphNodes)
{
if (uiNode->getNode() == foundNode)
{
if (_currRenderNode != outNodes[0])
if (_currRenderNode != uiNode)
{
_currRenderNode = outNodes[0];
_currRenderNode = uiNode;
_frameCount = ImGui::GetFrameCount();
_renderer->setMaterialCompilation(true);
}
break;
}
}
else
{
_currRenderNode = nullptr;
}
}
else
{
_currRenderNode = nullptr;
_frameCount = ImGui::GetFrameCount();
_renderer->setMaterialCompilation(true);
}
}
}

void Graph::updateMaterials(mx::InputPtr input, mx::ValuePtr value)
{
std::string renderablePath;
mx::TypedElementPtr renderableElem;
std::vector<mx::TypedElementPtr> elems = mx::findRenderableElements(_graphDoc);

size_t num = 0;
int num2 = 0;
for (mx::TypedElementPtr elem : elems)
if (_currRenderNode)
{
renderableElem = elem;
mx::NodePtr node = elem->asA<mx::Node>();
if (node)
if (_currRenderNode->getNode())
{
if (_currRenderNode)
{
if (node->getName() == _currRenderNode->getName())
{
renderablePath = renderableElem->getNamePath();
break;
}
}
else
{
renderablePath = renderableElem->getNamePath();
}
renderablePath = _currRenderNode->getNode()->getNamePath();
}
else
else if (_currRenderNode->getOutput())
{
renderablePath = renderableElem->getNamePath();
if (num2 == 2)
{
break;
}
num2++;
renderablePath = _currRenderNode->getOutput()->getNamePath();
}
}

Expand All @@ -824,7 +874,7 @@ void Graph::updateMaterials(mx::InputPtr input, mx::ValuePtr value)
// Note that if there is a topogical change due to
// this value change or a transparency change, then
// this is not currently caught here.
_renderer->getMaterials()[num]->modifyUniform(name, value);
_renderer->getMaterials()[0]->modifyUniform(name, value);
}
}
}
Expand Down Expand Up @@ -2737,7 +2787,7 @@ void Graph::deleteNode(UiNodePtr node)
{
mx::NodeDefPtr nodeDef = pin->_pinNode->getNode()->getNodeDef(pin->_pinNode->getNode()->getName());
val = nodeDef->getActiveInput(pin->_input->getName())->getValue();
if (pin->_pinNode->getNode()->getType() == "surfaceshader")
if (pin->_pinNode->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING)
{
pin->_input->setConnectedOutput(nullptr);
}
Expand Down Expand Up @@ -3710,17 +3760,10 @@ void Graph::drawGraph(ImVec2 mousePos)
_currUiNode = _graphNodes[graphPos];
// update render material if needed
if (_currUiNode->getNode())
{
if (_currUiNode->getNode()->getType() == mx::SURFACE_SHADER_TYPE_STRING || _currUiNode->getNode()->getType() == mx::MATERIAL_TYPE_STRING)
{
setRenderMaterial(_currUiNode);
}
}
else if (_currUiNode->getNodeGraph())
{
setRenderMaterial(_currUiNode);
}
else if (_currUiNode->getOutput())
else if (_currUiNode->getNodeGraph() || _currUiNode->getOutput())
{
setRenderMaterial(_currUiNode);
}
Expand Down
2 changes: 1 addition & 1 deletion source/MaterialXGraphEditor/RenderView.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ void RenderView::updateMaterials(mx::TypedElementPtr typedElem)
if (typedElem)
{
mx::NodePtr node = typedElem->asA<mx::Node>();
materialNode = node && node->getType() == mx::MATERIAL_TYPE_STRING ? node : nullptr;
materialNode = node;
if (udimSetValue && udimSetValue->isA<mx::StringVec>())
{
for (const std::string& udim : udimSetValue->asA<mx::StringVec>())
Expand Down
1 change: 1 addition & 0 deletions source/PyMaterialX/PyMaterialXCore/PyNode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ void bindPyNode(py::module& mod)
.def("addInterfaceName", &mx::NodeGraph::addInterfaceName)
.def("removeInterfaceName", &mx::NodeGraph::removeInterfaceName)
.def("modifyInterfaceName", &mx::NodeGraph::modifyInterfaceName)
.def("getDownstreamPorts", &mx::NodeGraph::getDownstreamPorts)
.def_readonly_static("CATEGORY", &mx::NodeGraph::CATEGORY);

py::class_<mx::Backdrop, mx::BackdropPtr, mx::Element>(mod, "Backdrop")
Expand Down

0 comments on commit d329ed7

Please sign in to comment.