Skip to content

Commit e9cefcc

Browse files
authored
Graph Editor : Support Node Up/Down Stream Connection Traversal in Property Editor (#2225)
* Background color + upstream traversal. * Initial output traversal code working on nodes. outputs will be shown more than once. Minor UI to show label once and then blank for other lines. * Formatting. * Fix build. * Apply input / output pin display logic to appropriate pins on different node types. * Cleanup. Remove color code. * Revert main. * Add in arrow key traversal.
1 parent 32e6ab7 commit e9cefcc

File tree

4 files changed

+246
-19
lines changed

4 files changed

+246
-19
lines changed

source/MaterialXGraphEditor/Graph.cpp

Lines changed: 233 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -811,7 +811,7 @@ void Graph::setRenderMaterial(UiNodePtr node)
811811
for (const std::string& testPath : testPaths)
812812
{
813813
mx::ElementPtr testElem = _graphDoc->getDescendant(testPath);
814-
mx::NodePtr testNode = testElem->asA<mx::Node>();
814+
mx::NodePtr testNode = testElem ? testElem->asA<mx::Node>() : nullptr;
815815
std::vector<mx::PortElementPtr> downstreamPorts;
816816
if (testNode)
817817
{
@@ -931,7 +931,7 @@ void Graph::updateMaterials(mx::InputPtr input /* = nullptr */, mx::ValuePtr val
931931
}
932932
}
933933

934-
void Graph::setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIProperties& uiProperties)
934+
void Graph::showPropertyEditorValue(UiNodePtr node, mx::InputPtr& input, const mx::UIProperties& uiProperties)
935935
{
936936
ImGui::PushItemWidth(-1);
937937

@@ -3377,6 +3377,129 @@ void Graph::graphButtons()
33773377
}
33783378
}
33793379

3380+
void Graph::showPropertyEditorOutputConnections(UiNodePtr node)
3381+
{
3382+
if (node->_showOutputsInEditor)
3383+
{
3384+
std::vector<UiPinPtr> pinList = node->outputPins;
3385+
size_t pinCount = 0;
3386+
for (UiPinPtr outputPin : node->outputPins)
3387+
{
3388+
std::vector<UiPinPtr> connectedPins = outputPin->getConnections();
3389+
pinCount += connectedPins.size();
3390+
}
3391+
3392+
if (pinCount > 0)
3393+
{
3394+
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing() * 1.3f;
3395+
const int SCROLL_LINE_COUNT = 20;
3396+
ImGuiTableFlags tableFlags = ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable | ImGuiTableFlags_NoSavedSettings |
3397+
ImGuiTableFlags_BordersOuterH | ImGuiTableFlags_NoBordersInBody;
3398+
3399+
ImVec2 tableSize(0.0f, TEXT_BASE_HEIGHT * std::min(SCROLL_LINE_COUNT, (int)pinCount));
3400+
bool haveTable = ImGui::BeginTable("outputs_node_table", 2, tableFlags, tableSize);
3401+
if (haveTable)
3402+
{
3403+
ImGui::SetWindowFontScale(_fontScale);
3404+
for (UiPinPtr outputPin : node->outputPins)
3405+
{
3406+
bool firstPin = true;
3407+
std::string outputPinName = outputPin->_name;
3408+
std::string blankPinName;
3409+
for (size_t i = 0; i < outputPinName.length(); i++)
3410+
{
3411+
blankPinName += ' ';
3412+
}
3413+
3414+
std::vector<UiPinPtr> connectedPins = outputPin->getConnections();
3415+
for (UiPinPtr connectedPin : connectedPins)
3416+
{
3417+
ImGui::TableNextRow();
3418+
ImGui::TableNextColumn();
3419+
3420+
std::string connectedPinName = connectedPin->_name;
3421+
if (connectedPin->_pinNode)
3422+
{
3423+
connectedPinName = connectedPin->_pinNode->getName() + "." + connectedPinName;
3424+
}
3425+
// Display outputPin name, and connectedPinName in same row
3426+
//
3427+
if (firstPin)
3428+
{
3429+
ImGui::Text("%s", outputPinName.c_str());
3430+
firstPin = false;
3431+
}
3432+
else
3433+
{
3434+
ImGui::Text("%s", blankPinName.c_str());
3435+
}
3436+
3437+
std::string displayString = connectedPinName;
3438+
ImGui::SameLine();
3439+
ImGui::TableNextColumn();
3440+
ImGui::PushItemWidth(-1);
3441+
3442+
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.15f, 0.15f, 1.0f));
3443+
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.4f, 0.6f, 1.0f));
3444+
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.2f, 0.4f, 1.0f));
3445+
3446+
if (ImGui::Button(displayString.c_str()))
3447+
{
3448+
std::shared_ptr<UiNode> pinNode = connectedPin->_pinNode;
3449+
if (pinNode)
3450+
{
3451+
ed::SelectNode(pinNode->getId());
3452+
ed::NavigateToSelection();
3453+
}
3454+
}
3455+
ImGui::PopStyleColor(3);
3456+
ImGui::PopItemWidth();
3457+
}
3458+
}
3459+
ImGui::EndTable();
3460+
ImGui::SetWindowFontScale(1.0f);
3461+
}
3462+
}
3463+
}
3464+
3465+
}
3466+
3467+
void Graph::showPropertyEditorInputConnection(UiPinPtr displayPin)
3468+
{
3469+
// Allow for upstream traversal via button
3470+
//
3471+
std::string displayString = displayPin->_input->getType();
3472+
const std::vector<UiPinPtr>& connections = displayPin->getConnections();
3473+
std::shared_ptr<UiNode> pinNode = nullptr;
3474+
if (!connections.empty())
3475+
{
3476+
UiPinPtr pin = connections[0];
3477+
std::string pinName = std::string(pin->_name);
3478+
3479+
pinNode = pin->_pinNode;
3480+
if (pinNode)
3481+
{
3482+
pinName = std::string(pinNode->getName()) + "." + pinName;
3483+
}
3484+
displayString = pinName;
3485+
}
3486+
3487+
ImGui::PushStyleColor(ImGuiCol_Button, ImVec4(0.15f, 0.15f, 0.15f, 1.0f));
3488+
ImGui::PushStyleColor(ImGuiCol_ButtonHovered, ImVec4(0.2f, 0.4f, 0.6f, 1.0f));
3489+
ImGui::PushStyleColor(ImGuiCol_ButtonActive, ImVec4(0.1f, 0.2f, 0.4f, 1.0f));
3490+
ImGui::PushItemWidth(-1);
3491+
if (ImGui::Button(displayString.c_str()))
3492+
{
3493+
if (pinNode)
3494+
{
3495+
ed::SelectNode(pinNode->getId());
3496+
ed::NavigateToSelection();
3497+
}
3498+
}
3499+
ImGui::PopItemWidth();
3500+
ImGui::PopStyleColor(3);
3501+
}
3502+
33803503
void Graph::propertyEditor()
33813504
{
33823505
// Get parent dimensions
@@ -3532,6 +3655,7 @@ void Graph::propertyEditor()
35323655
}
35333656

35343657
ImGui::Checkbox("Show all inputs", &_currUiNode->_showAllInputs);
3658+
ImGui::Checkbox("Show output connections", &_currUiNode->_showOutputsInEditor);
35353659

35363660
int count = 0;
35373661
for (UiPinPtr input : _currUiNode->inputPins)
@@ -3580,12 +3704,11 @@ void Graph::propertyEditor()
35803704
ImGui::TableNextColumn();
35813705
if (!input->getConnected())
35823706
{
3583-
setConstant(_currUiNode, input->_input, uiProperties);
3707+
showPropertyEditorValue(_currUiNode, input->_input, uiProperties);
35843708
}
35853709
else
35863710
{
3587-
std::string typeText = " [" + input->_input->getType() + "]";
3588-
ImGui::Text("%s", typeText.c_str());
3711+
showPropertyEditorInputConnection(input);
35893712
}
35903713

35913714
ImGui::PopID();
@@ -3596,6 +3719,8 @@ void Graph::propertyEditor()
35963719
ImGui::SetWindowFontScale(1.0f);
35973720
}
35983721
}
3722+
3723+
showPropertyEditorOutputConnections(_currUiNode);;
35993724
}
36003725

36013726
else if (_currUiNode->getInput() != nullptr)
@@ -3630,28 +3755,34 @@ void Graph::propertyEditor()
36303755
// Set constant sliders for input values
36313756
if (!inputs[i]->getConnected())
36323757
{
3633-
setConstant(_currUiNode, inputs[i]->_input, uiProperties);
3758+
showPropertyEditorValue(_currUiNode, inputs[i]->_input, uiProperties);
36343759
}
36353760
else
36363761
{
3637-
std::string typeText = " [" + inputs[i]->_input->getType() + "]";
3638-
ImGui::Text("%s", typeText.c_str());
3762+
showPropertyEditorInputConnection(inputs[i]);
36393763
}
36403764
ImGui::PopID();
36413765
}
36423766
ImGui::EndTable();
36433767
ImGui::SetWindowFontScale(1.0f);
36443768
}
36453769
}
3770+
3771+
showPropertyEditorOutputConnections(_currUiNode);;
36463772
}
36473773
else if (_currUiNode->getOutput() != nullptr)
36483774
{
36493775
ImGui::Text("%s", _currUiNode->getOutput()->getCategory().c_str());
36503776
}
36513777
else if (_currUiNode->getNodeGraph() != nullptr)
36523778
{
3779+
36533780
std::vector<UiPinPtr> inputs = _currUiNode->inputPins;
36543781
ImGui::Text("%s", _currUiNode->getCategory().c_str());
3782+
3783+
ImGui::Checkbox("Show all inputs", &_currUiNode->_showAllInputs);
3784+
ImGui::Checkbox("Show output connections", &_currUiNode->_showOutputsInEditor);
3785+
36553786
int count = 0;
36563787
for (UiPinPtr input : inputs)
36573788
{
@@ -3688,12 +3819,11 @@ void Graph::propertyEditor()
36883819
ImGui::TableNextColumn();
36893820
if (!input->_input->getConnectedNode() && _currUiNode->getNodeGraph()->getActiveInput(input->_input->getName()))
36903821
{
3691-
setConstant(_currUiNode, input->_input, uiProperties);
3822+
showPropertyEditorValue(_currUiNode, input->_input, uiProperties);
36923823
}
36933824
else
36943825
{
3695-
std::string typeText = " [" + input->_input->getType() + "]";
3696-
ImGui::Text("%s", typeText.c_str());
3826+
showPropertyEditorInputConnection(input);
36973827
}
36983828

36993829
ImGui::PopID();
@@ -3703,7 +3833,8 @@ void Graph::propertyEditor()
37033833
ImGui::SetWindowFontScale(1.0f);
37043834
}
37053835
}
3706-
ImGui::Checkbox("Show all inputs", &_currUiNode->_showAllInputs);
3836+
3837+
showPropertyEditorOutputConnections(_currUiNode);;
37073838
}
37083839

37093840
// Find tokens within currUiNode
@@ -4078,6 +4209,53 @@ void Graph::handleRenderViewInputs()
40784209
}
40794210
}
40804211

4212+
4213+
UiNodePtr Graph::traverseConnection(UiNodePtr node, bool traverseDownstream)
4214+
{
4215+
if (!node)
4216+
{
4217+
return nullptr;
4218+
}
4219+
4220+
// Get first connected downstream node
4221+
if (traverseDownstream)
4222+
{
4223+
for (UiPinPtr outputPin : node->outputPins)
4224+
{
4225+
// Update downNode info
4226+
for (UiPinPtr connectedPin : outputPin.get()->getConnections())
4227+
{
4228+
std::shared_ptr<UiNode> pinNode = connectedPin->_pinNode;
4229+
if (pinNode)
4230+
{
4231+
return pinNode;
4232+
}
4233+
}
4234+
}
4235+
}
4236+
4237+
// Get first upstream connected node
4238+
else
4239+
{
4240+
for (UiPinPtr inputPin: node->inputPins)
4241+
{
4242+
const std::vector<UiPinPtr>& connections = inputPin->getConnections();
4243+
std::shared_ptr<UiNode> pinNode = nullptr;
4244+
if (!connections.empty())
4245+
{
4246+
UiPinPtr pin = connections[0];
4247+
pinNode = pin->_pinNode;
4248+
if (pinNode)
4249+
{
4250+
return pinNode;
4251+
}
4252+
}
4253+
}
4254+
}
4255+
4256+
return nullptr;
4257+
}
4258+
40814259
void Graph::drawGraph(ImVec2 mousePos)
40824260
{
40834261
if (_searchNodeId > 0)
@@ -4299,7 +4477,47 @@ void Graph::drawGraph(ImVec2 mousePos)
42994477
// or if the shortcut for cut is used
43004478
if (ImGui::IsWindowFocused(ImGuiFocusedFlags_RootWindow))
43014479
{
4302-
if (ImGui::IsKeyReleased(ImGuiKey_Delete) || ImGui::IsKeyReleased(ImGuiKey_Backspace) || _isCut)
4480+
bool traverseDownstream = ImGui::IsKeyReleased(ImGuiKey_RightArrow);
4481+
bool traverseUpstream = ImGui::IsKeyReleased(ImGuiKey_LeftArrow);
4482+
4483+
// Traverse connections with arrow keys
4484+
if (traverseDownstream || traverseUpstream)
4485+
{
4486+
UiNodePtr selectedNode = nullptr;
4487+
if (selectedNodes.size() > 0)
4488+
{
4489+
for (ed::NodeId id : selectedNodes)
4490+
{
4491+
if (int(id.Get()) > 0)
4492+
{
4493+
int pos = findNode(int(id.Get()));
4494+
if (pos >= 0)
4495+
{
4496+
selectedNode = _graphNodes[pos];
4497+
break;
4498+
}
4499+
}
4500+
}
4501+
}
4502+
if (selectedNode && _currUiNode)
4503+
{
4504+
selectedNode = _currUiNode;
4505+
}
4506+
4507+
if (selectedNode)
4508+
{
4509+
UiNodePtr connectedNode = traverseConnection(selectedNode, traverseDownstream);
4510+
if (connectedNode)
4511+
{
4512+
_currUiNode = connectedNode;
4513+
ed::SelectNode(connectedNode->getId());
4514+
ed::NavigateToSelection();
4515+
}
4516+
4517+
}
4518+
}
4519+
4520+
else if (ImGui::IsKeyReleased(ImGuiKey_Delete) || ImGui::IsKeyReleased(ImGuiKey_Backspace) || _isCut)
43034521
{
43044522
if (selectedNodes.size() > 0)
43054523
{
@@ -4352,13 +4570,13 @@ void Graph::drawGraph(ImVec2 mousePos)
43524570
}
43534571

43544572
// Hotkey to frame selected node(s)
4355-
if (ImGui::IsKeyReleased(ImGuiKey_F) && !_fileDialogSave.isOpened())
4573+
else if (ImGui::IsKeyReleased(ImGuiKey_F) && !_fileDialogSave.isOpened())
43564574
{
43574575
ed::NavigateToSelection();
43584576
}
43594577

43604578
// Go back up from inside a subgraph
4361-
if (ImGui::IsKeyReleased(ImGuiKey_U) && (!ImGui::IsPopupOpen("add node")) && (!ImGui::IsPopupOpen("search")) && !_fileDialogSave.isOpened())
4579+
else if (ImGui::IsKeyReleased(ImGuiKey_U) && (!ImGui::IsPopupOpen("add node")) && (!ImGui::IsPopupOpen("search")) && !_fileDialogSave.isOpened())
43624580
{
43634581
upNodeGraph();
43644582
}

source/MaterialXGraphEditor/Graph.h

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -190,11 +190,17 @@ class Graph
190190
// Add input pointer to node based on input pin
191191
void addNodeInput(UiNodePtr node, mx::InputPtr& input);
192192

193+
// Traversal methods
193194
void upNodeGraph();
194-
195-
// Set the value of the selected node constants in the node property editor
196-
void setConstant(UiNodePtr node, mx::InputPtr& input, const mx::UIProperties& uiProperties);
197-
195+
UiNodePtr traverseConnection(UiNodePtr node, bool traverseDownstream);
196+
197+
// Show input values in property editor for a given input
198+
void showPropertyEditorValue(UiNodePtr node, mx::InputPtr& input, const mx::UIProperties& uiProperties);
199+
// Show input connections in property editor for a given node
200+
void showPropertyEditorOutputConnections(UiNodePtr node);
201+
// Show output connections in property editor for a given output pin
202+
void showPropertyEditorInputConnection(UiPinPtr pin);
203+
// Show property editor for a given node
198204
void propertyEditor();
199205
void setDefaults(mx::InputPtr input);
200206

source/MaterialXGraphEditor/UiNode.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const int INVALID_POS = -10000;
1515
UiNode::UiNode() :
1616
_level(-1),
1717
_showAllInputs(false),
18+
_showOutputsInEditor(true),
1819
_id(0),
1920
_nodePos(INVALID_POS, INVALID_POS),
2021
_inputNodeNum(0)
@@ -24,6 +25,7 @@ UiNode::UiNode() :
2425
UiNode::UiNode(const std::string& name, int id) :
2526
_level(-1),
2627
_showAllInputs(false),
28+
_showOutputsInEditor(true),
2729
_id(id),
2830
_nodePos(INVALID_POS, INVALID_POS),
2931
_name(name),

0 commit comments

Comments
 (0)