Skip to content
Open
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
50a029e
First trial to capture graph editor.
kwokcb May 29, 2025
a7c9885
Tweak capture. Fix build.
kwokcb May 29, 2025
7bb3835
Clip top.
kwokcb May 29, 2025
7412715
Cleanup code.
kwokcb May 30, 2025
202c434
Cleanup.
kwokcb May 30, 2025
a5ac56d
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Jun 5, 2025
ea709aa
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Jul 8, 2025
32b171c
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Jul 15, 2025
9f00de9
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Aug 19, 2025
55f9b53
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Aug 26, 2025
5c5cf3a
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Sep 7, 2025
7b766f3
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Sep 12, 2025
3fc6e10
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Sep 16, 2025
14b73c4
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Oct 5, 2025
cdaa343
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Oct 14, 2025
b67917a
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Oct 23, 2025
83c0000
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Nov 10, 2025
183a942
Merge branch 'AcademySoftwareFoundation:main' into editor_capture
kwokcb Nov 12, 2025
e525984
Merge branch 'main' into editor_capture
kwokcb Nov 26, 2025
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
134 changes: 128 additions & 6 deletions source/MaterialXGraphEditor/Graph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ Graph::Graph(const std::string& materialFilename,
_initial(false),
_delete(false),
_fileDialogSave(FileDialog::EnterNewFilename),
_fileDialogSnapshot(FileDialog::EnterNewFilename),
_isNodeGraph(false),
_graphTotalSize(0),
_popup(false),
Expand Down Expand Up @@ -802,6 +803,10 @@ void Graph::setRenderMaterial(UiNodePtr node)
for (const std::string& testPath : testPaths)
{
mx::ElementPtr testElem = _graphDoc->getDescendant(testPath);
if (!testElem)
{
continue;
}
mx::NodePtr testNode = testElem->asA<mx::Node>();
std::vector<mx::PortElementPtr> downstreamPorts;
if (testNode)
Expand Down Expand Up @@ -3180,6 +3185,40 @@ void Graph::saveGraphToFile()
_fileDialogSave.open();
}

void Graph::showSnapshotDialog()
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Some file dialog infrastructure. Save uses the current captured image.

{
if (!_capturedImage)
{
return;
}
mx::StringVec imageFilter;
imageFilter.push_back(".png");
_fileDialogSnapshot.setTypeFilters(imageFilter);
_fileDialogSnapshot.setTitle("Save SnapshotAs");
_fileDialogSnapshot.open();
}

void Graph::saveSnapshotToFile()
{
ed::Suspend();
_fileDialogSnapshot.display();

// Save capture
if (_fileDialogSnapshot.hasSelected())
{
std::string captureName = _fileDialogSnapshot.getSelected();
std::cout << "Save image to: " << captureName << std::endl;
_renderer->getImageHandler()->saveImage(captureName, _capturedImage, true);
_capturedImage = nullptr;
ed::Resume();
_fileDialogSnapshot.clearSelected();
}
else
{
ed::Resume();
}
}

void Graph::loadGeometry()
{
_fileDialogGeom.setTitle("Load Geometry");
Expand Down Expand Up @@ -3276,12 +3315,12 @@ void Graph::graphButtons()
}

// Split window into panes for NodeEditor
static float leftPaneWidth = 375.0f;
static float rightPaneWidth = 750.0f;
splitter(true, 4.0f, &leftPaneWidth, &rightPaneWidth, 20.0f, 20.0f);
splitter(true, 4.0f, &_leftPaneWidth, &_rightPaneWidth, 20.0f, 20.0f);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Remove some hard-coded statics and make class members for reuse during snapshot.

//std::cout << "Left pane width: " << _leftPaneWidth << std::endl;
//std::cout << "Right pane width: " << _rightPaneWidth << std::endl;

// Create back button and graph hierarchy name display
ImGui::Indent(leftPaneWidth + 15.f);
ImGui::Indent(_leftPaneWidth + _nodeEditorIndent);
if (ImGui::Button("<"))
{
upNodeGraph();
Expand All @@ -3301,12 +3340,12 @@ void Graph::graphButtons()
}
}
ImVec2 windowPos2 = ImGui::GetWindowPos();
ImGui::Unindent(leftPaneWidth + 15.f);
ImGui::Unindent(_leftPaneWidth + _nodeEditorIndent);
ImGui::PopStyleColor();
ImGui::NewLine();

// Create two windows using splitter
float paneWidth = (leftPaneWidth - 2.0f);
float paneWidth = (_leftPaneWidth - 2.0f);

float aspectRatio = _renderer->getPixelRatio();
ImVec2 screenSize = ImVec2(paneWidth, paneWidth / aspectRatio);
Expand Down Expand Up @@ -3735,6 +3774,8 @@ void Graph::showHelp() const
ImGui::BulletText("CTRL-F : Find a node by name.");
ImGui::BulletText("CTRL-X : Delete selected nodes and add to clipboard.");
ImGui::BulletText("DELETE : Delete selected nodes or connections.");
ImGui::BulletText("CTRL-I : Capture Editor Panel to disk.");
ImGui::BulletText("CTRL-SHIFT-I : Capture entire window to disk.");
ImGui::TreePop();
}
}
Expand Down Expand Up @@ -4031,6 +4072,65 @@ void Graph::handleRenderViewInputs()
}
}

mx::ImagePtr Graph::performSnapshot(bool captureWindow) const
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Snapshot function : takes into account the current splitter layout and extra formatting (indentation).

{
if (captureWindow)
{
ImVec2 windowPos = ImGui::GetWindowPos();
ImVec2 windowSize = ImGui::GetWindowSize();
unsigned int w = static_cast<unsigned int>(windowSize.x);
unsigned int h = static_cast<unsigned int>(windowSize.y);

mx::ImagePtr image = mx::Image::create(w, h, 3);
if (image)
{
image->createResourceBuffer();

glFlush();
glReadPixels(0, 0, image->getWidth(), image->getHeight(), GL_RGB, GL_UNSIGNED_BYTE, image->getResourceBuffer());
}
return image;
}

// Get main window information
ImGuiWindow* window = ImGui::GetCurrentWindow();
ImVec2 windowPos = window->Pos;
ImVec2 windowSize = window->Size;

// Get editor information
ImVec2 contentSize = ImGui::GetContentRegionAvail(); // Excludes menu bars

float splitterSize = 4.0f;
float leftOffset = _leftPaneWidth + _nodeEditorIndent;
float topOffset = _nodeEditorIndent;
float rightOffset = _nodeEditorIndent;

// Calculate the right pane position. Include splitter and indentation from top left corner
ImVec2 rightPanePos;
rightPanePos.x = leftOffset + splitterSize;
rightPanePos.y = topOffset;

// Calculate the right panel size. Remove left and right editor offsets.
ImVec2 rightPaneSize;
rightPaneSize.x = windowSize.x - (leftOffset + rightOffset);
rightPaneSize.y = contentSize.y - (windowPos.y + 2.0f * topOffset);

int w = static_cast<int>(rightPaneSize.x);
int h = static_cast<int>(rightPaneSize.y);
mx::ImagePtr image = mx::Image::create(static_cast<unsigned int>(w), static_cast<unsigned int>(h), 3);
if (image)
{
image->createResourceBuffer();

glFlush();
glReadPixels(static_cast<int>(rightPanePos.x), 0, w, h,
GL_RGB, GL_UNSIGNED_BYTE,
image->getResourceBuffer()
);
}
return image;
}

void Graph::drawGraph(ImVec2 mousePos)
{
if (_searchNodeId > 0)
Expand All @@ -4050,6 +4150,25 @@ void Graph::drawGraph(ImVec2 mousePos)

io2.ConfigFlags = ImGuiConfigFlags_IsSRGB | ImGuiConfigFlags_NavEnableKeyboard;
io2.MouseDoubleClickTime = .5;

// Capture. Perform before other UI calls which can change window size / positioning.
if (io2.KeyCtrl && ImGui::IsKeyReleased(ImGuiKey_I))
{
// Capture entire window if SHIFT, otherwise capture node editor
bool captureWindow = io2.KeyShift;
_capturedImage = performSnapshot(captureWindow);
//std::string filename = "graph_capture.png";
if (_capturedImage)
{
//std::cout << "Frame saved to: " << filename << std::endl;
showSnapshotDialog();
}
//else
//{
// std::cout << "Failed to write frame to disk: " << filename << std::endl;
//}
}

graphButtons();

ed::Begin("My Editor");
Expand Down Expand Up @@ -4365,6 +4484,7 @@ void Graph::drawGraph(ImVec2 mousePos)
}
}
ed::EndDelete();

}

// Dive into a node that has a subgraph
Expand Down Expand Up @@ -4467,6 +4587,8 @@ void Graph::drawGraph(ImVec2 mousePos)
ed::Resume();
}

saveSnapshotToFile();

ed::End();
ImGui::End();

Expand Down
14 changes: 14 additions & 0 deletions source/MaterialXGraphEditor/Graph.h
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,11 @@ class Graph

void showHelp() const;

// Methods to capture snapshot to file
mx::ImagePtr performSnapshot(bool captureWindow = false) const;
void showSnapshotDialog();
void saveSnapshotToFile();

private:
mx::StringVec _geomFilter;
mx::StringVec _mtlxFilter;
Expand Down Expand Up @@ -295,6 +300,7 @@ class Graph
FileDialog _fileDialogSave;
FileDialog _fileDialogImage;
FileDialog _fileDialogGeom;
FileDialog _fileDialogSnapshot;
std::string _fileDialogImageInputName;

bool _isNodeGraph;
Expand Down Expand Up @@ -326,6 +332,14 @@ class Graph

// Options
bool _saveNodePositions;

// Panel information
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Make class members to track panel placement / size changes.
Also pull out constant 15 indent to avoid hard-coded numbers.

float _leftPaneWidth = 375.0f;
float _nodeEditorIndent = 15.0f;
float _rightPaneWidth = 750.0f;

// Image capture
mx::ImagePtr _capturedImage;
};

#endif