From 5049d04c448c01d7977ceb89c3e52a97e5419f2e Mon Sep 17 00:00:00 2001 From: Jonathan Stone Date: Sun, 1 Mar 2026 11:04:36 -0800 Subject: [PATCH] Consolidate edge creation in Graph Editor This changelist consolidates edge creation logic in the MaterialX Graph Editor, adding shared helper methods and merging parallel code paths. The following specific changes are included: - Add a `findUpstreamNode` helper method to handle the resolution of upstream connections for an input. - Add a `createEdgeForOutput` helper method to handle the creation of edges from output elements to their connected upstream nodes. - Unify edge creation in `buildUiBaseGraph` into a single loop over all graph nodes, replacing separate loops for nodegraphs and surface shader nodes. - Add missing edge creation and render material updates for document-scope outputs (addresses issue https://github.com/AcademySoftwareFoundation/MaterialX/issues/2294). --- source/MaterialXGraphEditor/Graph.cpp | 162 +++++++++++++------------- source/MaterialXGraphEditor/Graph.h | 6 + 2 files changed, 86 insertions(+), 82 deletions(-) diff --git a/source/MaterialXGraphEditor/Graph.cpp b/source/MaterialXGraphEditor/Graph.cpp index 98c61a7857..9ef632d4a9 100644 --- a/source/MaterialXGraphEditor/Graph.cpp +++ b/source/MaterialXGraphEditor/Graph.cpp @@ -544,6 +544,17 @@ void Graph::setRenderMaterial(UiNodePtr node) mtlxNodeGraph = parent->asA(); else if (parent->isA()) mtlxNode = parent->asA(); + else if (parent->isA()) + { + // Document-scope outputs are directly renderable. + if (_currRenderNode != node) + { + _currRenderNode = node; + _frameCount = ImGui::GetFrameCount(); + _renderer->setMaterialCompilation(true); + } + return; + } } mx::StringSet testPaths; if (mtlxNode) @@ -1161,28 +1172,22 @@ void Graph::buildUiBaseGraph(mx::DocumentPtr doc) setUiNodeInfo(currNode, output->getType(), output->getCategory()); } - // Create edges for nodegraphs - for (mx::NodeGraphPtr graph : nodeGraphs) + // Create edges for nodegraph and node inputs + for (size_t i = 0; i < _state.nodes.size(); i++) { - int downNum = findNode(graph->getName(), "nodegraph"); - if (downNum < 0) + UiNodePtr& uiNode = _state.nodes[i]; + mx::ElementPtr elem = uiNode->getElement(); + mx::InterfaceElementPtr interface = elem ? elem->asA() : nullptr; + if (!interface) { continue; } - for (mx::InputPtr input : graph->getActiveInputs()) - { - int upNum = -1; - mx::string nodeGraphName = input->getNodeGraphString(); - mx::NodePtr connectedNode = input->getConnectedNode(); - if (!nodeGraphName.empty()) - { - upNum = findNode(nodeGraphName, "nodegraph"); - } - else if (connectedNode) - { - upNum = findNode(connectedNode->getName(), "node"); - } + int downNum = static_cast(i); + + for (mx::InputPtr input : interface->getActiveInputs()) + { + int upNum = findUpstreamNode(input); if (upNum >= 0) { createEdge(_state.nodes[upNum], _state.nodes[downNum], input); @@ -1190,38 +1195,10 @@ void Graph::buildUiBaseGraph(mx::DocumentPtr doc) } } - // Create edges for surface and material nodes - for (mx::NodePtr node : docNodes) + // Create edges for document-scope outputs + for (mx::OutputPtr output : outputNodes) { - mx::NodeDefPtr nD = node->getNodeDef(node->getName()); - for (mx::InputPtr input : node->getActiveInputs()) - { - mx::string nodeGraphName = input->getNodeGraphString(); - mx::NodePtr connectedNode = input->getConnectedNode(); - mx::OutputPtr connectedOutput = input->getConnectedOutput(); - int upNum = -1; - int downNum = findNode(node->getName(), "node"); - if (!nodeGraphName.empty()) - { - upNum = findNode(nodeGraphName, "nodegraph"); - } - else if (connectedNode) - { - upNum = findNode(connectedNode->getName(), "node"); - } - else if (connectedOutput) - { - upNum = findNode(connectedOutput->getName(), "output"); - } - else if (!input->getInterfaceName().empty()) - { - upNum = findNode(input->getInterfaceName(), "input"); - } - if (upNum >= 0 && downNum >= 0) - { - createEdge(_state.nodes[upNum], _state.nodes[downNum], input); - } - } + createEdgeForOutput(output); } } @@ -1232,7 +1209,6 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) mx::NodeGraphPtr nodeGraph = nodeGraphs; std::vector children = nodeGraph->topologicalSort(); mx::NodeDefPtr nodeDef = nodeGraph->getNodeDef(); - mx::NodeDefPtr currNodeDef; // Create input nodes if (nodeDef) @@ -1285,13 +1261,13 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) mx::ElementPtr connectingElem = edge.getConnectingElement(); mx::NodePtr upstreamNode = upstreamElem->asA(); - mx::NodePtr downstreamNode = downstreamElem->asA(); mx::InputPtr upstreamInput = upstreamElem->asA(); - mx::InputPtr downstreamInput = downstreamElem->asA(); mx::OutputPtr upstreamOutput = upstreamElem->asA(); + mx::NodePtr downstreamNode = downstreamElem->asA(); + mx::InputPtr downstreamInput = downstreamElem->asA(); mx::OutputPtr downstreamOutput = downstreamElem->asA(); - std::string downName = downstreamElem->getName(); std::string upName = upstreamElem->getName(); + std::string downName = downstreamElem->getName(); std::string upstreamType; std::string downstreamType; if (upstreamNode) @@ -1363,44 +1339,23 @@ void Graph::buildUiNodeGraph(const mx::NodeGraphPtr& nodeGraphs) mx::OutputPtr output = elem->asA(); if (node) { + int downNum = findNode(node->getName(), "node"); + if (downNum < 0) + { + continue; + } for (mx::InputPtr input : node->getActiveInputs()) { - mx::NodePtr connectedNode = input->getConnectedNode(); - int downNum = findNode(node->getName(), "node"); - if (downNum < 0) + int upNum = findUpstreamNode(input); + if (upNum >= 0) { - continue; - } - if (connectedNode) - { - int upNum = findNode(connectedNode->getName(), "node"); - if (upNum >= 0) - { - createEdge(_state.nodes[upNum], _state.nodes[downNum], input); - } - } - else if (input->getInterfaceInput()) - { - int upNum = findNode(input->getInterfaceInput()->getName(), "input"); - if (upNum >= 0) - { - createEdge(_state.nodes[upNum], _state.nodes[downNum], input); - } + createEdge(_state.nodes[upNum], _state.nodes[downNum], input); } } } else if (output) { - mx::NodePtr connectedNode = output->getConnectedNode(); - if (connectedNode) - { - int upNum = findNode(connectedNode->getName(), "node"); - int downNum = findNode(output->getName(), "output"); - if (upNum >= 0 && downNum >= 0) - { - createEdge(_state.nodes[upNum], _state.nodes[downNum], nullptr); - } - } + createEdgeForOutput(output); } } } @@ -1435,6 +1390,35 @@ int Graph::findNode(const std::string& name, const std::string& type) return -1; } +int Graph::findUpstreamNode(mx::InputPtr input) +{ + const mx::string& nodeGraphName = input->getNodeGraphString(); + if (!nodeGraphName.empty()) + { + return findNode(nodeGraphName, "nodegraph"); + } + + mx::NodePtr connectedNode = input->getConnectedNode(); + if (connectedNode) + { + return findNode(connectedNode->getName(), "node"); + } + + mx::OutputPtr connectedOutput = input->getConnectedOutput(); + if (connectedOutput) + { + return findNode(connectedOutput->getName(), "output"); + } + + const mx::string& interfaceName = input->getInterfaceName(); + if (!interfaceName.empty()) + { + return findNode(interfaceName, "input"); + } + + return -1; +} + void Graph::positionPasteBin(ImVec2 pos) { ImVec2 totalPos = ImVec2(0, 0); @@ -1486,6 +1470,20 @@ bool Graph::createEdge(UiNodePtr upNode, UiNodePtr downNode, mx::InputPtr connec return true; } +void Graph::createEdgeForOutput(mx::OutputPtr output) +{ + mx::NodePtr connectedNode = output->getConnectedNode(); + if (connectedNode) + { + int upNum = findNode(connectedNode->getName(), "node"); + int downNum = findNode(output->getName(), "output"); + if (upNum >= 0 && downNum >= 0) + { + createEdge(_state.nodes[upNum], _state.nodes[downNum], nullptr); + } + } +} + void Graph::copyUiNode(UiNodePtr node) { UiNodePtr copyNode = std::make_shared(mx::EMPTY_STRING, int(_state.nextUiId + 1)); diff --git a/source/MaterialXGraphEditor/Graph.h b/source/MaterialXGraphEditor/Graph.h index 31c9f186e5..cc3450916d 100644 --- a/source/MaterialXGraphEditor/Graph.h +++ b/source/MaterialXGraphEditor/Graph.h @@ -181,6 +181,9 @@ class Graph // account for input/output UiNodes with same names as MaterialX nodes int findNode(const std::string& name, const std::string& type); + // Return the node position of the upstream connection from the given input. + int findUpstreamNode(mx::InputPtr input); + // Add node to graphNodes based on nodedef information void addNode(const std::string& category, const std::string& name, const std::string& type); @@ -196,6 +199,9 @@ class Graph // Returns true if the edge was created, false if invalid or already exists. bool createEdge(UiNodePtr upNode, UiNodePtr downNode, mx::InputPtr connectingInput); + // Create an edge from an output element to its connected upstream node. + void createEdgeForOutput(mx::OutputPtr output); + // Remove node edge based on connecting input void removeEdge(int downNode, int upNode, UiPinPtr pin);