Skip to content

Commit 7412715

Browse files
committed
Cleanup code.
- Add in save dialog and help. - Ad in CTRL-I vs CTRL-SHIFT-I to capture editor or whole window.
1 parent 7bb3835 commit 7412715

2 files changed

Lines changed: 114 additions & 145 deletions

File tree

source/MaterialXGraphEditor/Graph.cpp

Lines changed: 105 additions & 143 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ Graph::Graph(const std::string& materialFilename,
143143
_initial(false),
144144
_delete(false),
145145
_fileDialogSave(FileDialog::EnterNewFilename),
146+
_fileDialogSnapshot(FileDialog::EnterNewFilename),
146147
_isNodeGraph(false),
147148
_graphTotalSize(0),
148149
_popup(false),
@@ -802,6 +803,10 @@ void Graph::setRenderMaterial(UiNodePtr node)
802803
for (const std::string& testPath : testPaths)
803804
{
804805
mx::ElementPtr testElem = _graphDoc->getDescendant(testPath);
806+
if (!testElem)
807+
{
808+
continue;
809+
}
805810
mx::NodePtr testNode = testElem->asA<mx::Node>();
806811
std::vector<mx::PortElementPtr> downstreamPorts;
807812
if (testNode)
@@ -3180,6 +3185,40 @@ void Graph::saveGraphToFile()
31803185
_fileDialogSave.open();
31813186
}
31823187

3188+
void Graph::showSnapshotDialog()
3189+
{
3190+
if (!_capturedImage)
3191+
{
3192+
return;
3193+
}
3194+
mx::StringVec imageFilter;
3195+
imageFilter.push_back(".png");
3196+
_fileDialogSnapshot.setTypeFilters(imageFilter);
3197+
_fileDialogSnapshot.setTitle("Save SnapshotAs");
3198+
_fileDialogSnapshot.open();
3199+
}
3200+
3201+
void Graph::saveSnapshotToFile()
3202+
{
3203+
ed::Suspend();
3204+
_fileDialogSnapshot.display();
3205+
3206+
// Save capture
3207+
if (_fileDialogSnapshot.hasSelected())
3208+
{
3209+
std::string captureName = _fileDialogSnapshot.getSelected();
3210+
std::cout << "Save image to: " << captureName << std::endl;
3211+
_renderer->getImageHandler()->saveImage(captureName, _capturedImage, true);
3212+
_capturedImage = nullptr;
3213+
ed::Resume();
3214+
_fileDialogSnapshot.clearSelected();
3215+
}
3216+
else
3217+
{
3218+
ed::Resume();
3219+
}
3220+
}
3221+
31833222
void Graph::loadGeometry()
31843223
{
31853224
_fileDialogGeom.setTitle("Load Geometry");
@@ -3281,7 +3320,7 @@ void Graph::graphButtons()
32813320
//std::cout << "Right pane width: " << _rightPaneWidth << std::endl;
32823321

32833322
// Create back button and graph hierarchy name display
3284-
ImGui::Indent(_leftPaneWidth + _leftPanelIndent);
3323+
ImGui::Indent(_leftPaneWidth + _nodeEditorIndent);
32853324
if (ImGui::Button("<"))
32863325
{
32873326
upNodeGraph();
@@ -3301,7 +3340,7 @@ void Graph::graphButtons()
33013340
}
33023341
}
33033342
ImVec2 windowPos2 = ImGui::GetWindowPos();
3304-
ImGui::Unindent(_leftPaneWidth + _leftPanelIndent);
3343+
ImGui::Unindent(_leftPaneWidth + _nodeEditorIndent);
33053344
ImGui::PopStyleColor();
33063345
ImGui::NewLine();
33073346

@@ -3735,6 +3774,8 @@ void Graph::showHelp() const
37353774
ImGui::BulletText("CTRL-F : Find a node by name.");
37363775
ImGui::BulletText("CTRL-X : Delete selected nodes and add to clipboard.");
37373776
ImGui::BulletText("DELETE : Delete selected nodes or connections.");
3777+
ImGui::BulletText("CTRL-I : Capture Editor Panel to disk.");
3778+
ImGui::BulletText("CTRL-SHIFT-I : Capture entire window to disk.");
37383779
ImGui::TreePop();
37393780
}
37403781
}
@@ -4031,147 +4072,64 @@ void Graph::handleRenderViewInputs()
40314072
}
40324073
}
40334074

4034-
mx::ImagePtr Graph::getFrameImage() const
4075+
mx::ImagePtr Graph::performSnapshot(bool captureWindow) const
40354076
{
4077+
if (captureWindow)
4078+
{
4079+
ImVec2 windowPos = ImGui::GetWindowPos();
4080+
ImVec2 windowSize = ImGui::GetWindowSize();
4081+
unsigned int w = static_cast<unsigned int>(windowSize.x);
4082+
unsigned int h = static_cast<unsigned int>(windowSize.y);
40364083

4037-
// Capture the graph editor window to an image file
4038-
#if 0
4039-
ImVec2 windowPos = ImGui::GetWindowPos();
4040-
ImVec2 windowSize = ImGui::GetWindowSize();
4041-
int x = static_cast<int>(windowPos.x);
4042-
int y = static_cast<int>(windowPos.y);
4043-
int w = static_cast<int>(windowSize.x);
4044-
int h = static_cast<int>(windowSize.y);
4045-
4046-
mx::ImagePtr frameImage = mx::Image::create((unsigned int)(w),
4047-
(unsigned int)(h), 3);
4048-
frameImage->createResourceBuffer();
4049-
4050-
glFlush();
4051-
glReadPixels(0, 0, frameImage->getWidth(), frameImage->getHeight(), GL_RGB, GL_UNSIGNED_BYTE, image->getResourceBuffer());
4052-
4053-
#elif 0
4054-
// 1. Get the current right pane dimensions (accounts for resizing)
4055-
ImVec2 rightPanePos = ImGui::GetWindowPos();
4056-
rightPanePos.x += leftPaneWidth + 4.0f; // Adjust for left pane + splitter thickness
4057-
4058-
ImVec2 rightPaneSize = ImGui::GetContentRegionAvail(); // Current available space
4059-
rightPaneSize.x = rightPaneWidth; // Use the dynamic right pane width
4060-
4061-
ImVec2 capturePos = rightPanePos;
4062-
capturePos.y = ImGui::GetIO().DisplaySize.y - rightPanePos.y - rightPaneSize.y;
4063-
4064-
int w = static_cast<int>(rightPaneSize.x);
4065-
int h = static_cast<int>(rightPaneSize.y);
4066-
mx::ImagePtr frameImage = mx::Image::create((unsigned int) (w),
4067-
(unsigned int) (h), 3);
4068-
frameImage->createResourceBuffer();
4069-
4070-
glFlush();
4071-
glReadPixels(
4072-
(int)capturePos.x,
4073-
(int)capturePos.y,
4074-
(int)rightPaneSize.x,
4075-
(int)rightPaneSize.y,
4076-
GL_RGB,
4077-
GL_UNSIGNED_BYTE,
4078-
frameImage->getResourceBuffer()
4079-
);
4080-
#elif 1
4081-
4082-
// 1. Get the current window and splitter state
4084+
mx::ImagePtr image = mx::Image::create(w, h, 3);
4085+
if (image)
4086+
{
4087+
image->createResourceBuffer();
4088+
4089+
glFlush();
4090+
glReadPixels(0, 0, image->getWidth(), image->getHeight(), GL_RGB, GL_UNSIGNED_BYTE, image->getResourceBuffer());
4091+
}
4092+
return image;
4093+
}
4094+
4095+
// Get main window information
40834096
ImGuiWindow* window = ImGui::GetCurrentWindow();
40844097
ImVec2 windowPos = window->Pos;
40854098
ImVec2 windowSize = window->Size;
4086-
std::cout << "Window Position: " << windowPos.x << ", " << windowPos.y << std::endl;
4087-
std::cout << "Window Size: " << windowSize.x << ", " << windowSize.y << std::endl;
4088-
ImVec2 contentMin = ImGui::GetWindowContentRegionMin(); // Get the content region min position
4089-
ImVec2 contentMax = ImGui::GetWindowContentRegionMax(); // Get the content region max position
4090-
std::cout << "Content Region Min: " << contentMin.x << ", " << contentMin.y << std::endl;
4091-
std::cout << "Content Region Max: " << contentMax.x << ", " << contentMax.y << std::endl;
4092-
ImVec2 displaySize = ImGui::GetContentRegionAvail(); // Excludes menu bars
4093-
std::cout << "Content Size: " << displaySize.x << ", " << displaySize.y << std::endl;
4094-
4095-
// 2. Calculate the ACTUAL right pane dimensions (not just the stored variables)
4096-
ImVec2 rightPanePos = windowPos;
4097-
rightPanePos.x += _leftPaneWidth + _leftPanelIndent + 4;// +_leftPanelIndent; // leftPane + splitter width
4098-
rightPanePos.y += _leftPanelIndent;
40994099

4100-
ImVec2 rightPaneSize;
4101-
rightPaneSize.x = windowSize.x - (_leftPaneWidth + _leftPanelIndent + (_leftPanelIndent)); // dynamic width
4102-
4103-
//rightPaneSize.y = windowSize.y - (windowPos.y - ImGui::GetCursorStartPos().y + 30.0f); // content height
4104-
//rightPaneSize.y = windowSize.y - (windowPos.y + 30.0f); // content height
4105-
rightPaneSize.y = displaySize.y - (windowPos.y + 15.0f); // content height
4100+
// Get editor information
4101+
ImVec2 contentSize = ImGui::GetContentRegionAvail(); // Excludes menu bars
41064102

4107-
std::cout << "Right Pane Position: " << rightPanePos.x << ", " << rightPanePos.y << std::endl;
4108-
std::cout << "Right Pane Size: " << rightPaneSize.x << ", " << rightPaneSize.y << std::endl;
4103+
float splitterSize = 4.0f;
4104+
float leftOffset = _leftPaneWidth + _nodeEditorIndent;
4105+
float topOffset = _nodeEditorIndent;
4106+
float rightOffset = _nodeEditorIndent;
41094107

4110-
// 3. Get the actual viewport coordinates (accounting for DPI scaling)
4111-
//ImVec2 displaySize = ImGui::GetIO().DisplaySize;
4112-
ImVec2 capturePos = rightPanePos;
4113-
//capturePos.y = displaySize.y - rightPanePos.y - rightPaneSize.y; // Flip Y for OpenGL
4114-
capturePos.y = displaySize.y + 4 - rightPanePos.y - rightPaneSize.y; // Flip Y for OpenGL
4115-
std::cout << "Capture Position: " << capturePos.x << ", " << capturePos.y << std::endl;
4108+
// Calculate the right pane position. Include splitter and indentation from top left corner
4109+
ImVec2 rightPanePos;
4110+
rightPanePos.x = leftOffset + splitterSize;
4111+
rightPanePos.y = topOffset;
4112+
4113+
// Calculate the right panel size. Remove left and right editor offsets.
4114+
ImVec2 rightPaneSize;
4115+
rightPaneSize.x = windowSize.x - (leftOffset + rightOffset);
4116+
rightPaneSize.y = contentSize.y - (windowPos.y + 2.0f * topOffset);
41164117

41174118
int w = static_cast<int>(rightPaneSize.x);
4118-
int h = static_cast<int>(rightPaneSize.y) - 15;
4119-
mx::ImagePtr frameImage = mx::Image::create((unsigned int)(w),
4120-
(unsigned int)(h), 3);
4121-
frameImage->createResourceBuffer();
4122-
4123-
glFlush();
4124-
glReadPixels(
4125-
(int)capturePos.x,
4126-
(int)capturePos.y - 11,
4127-
(int)rightPaneSize.x,
4128-
(int)rightPaneSize.y - 15,
4129-
GL_RGB,
4130-
GL_UNSIGNED_BYTE,
4131-
frameImage->getResourceBuffer()
4132-
);
4133-
#else
4134-
// 1. Get the main viewport (accounts for menu bars/toolbars)
4135-
ImGuiViewport* viewport = ImGui::GetMainViewport();
4136-
4137-
// 2. Calculate the right pane's content area
4138-
ImVec2 rightPaneStart = ImGui::GetWindowPos();
4139-
rightPaneStart.x += _leftPaneWidth + 15.0f; // After left pane + splitter
4140-
4141-
ImVec2 contentSize = ImGui::GetContentRegionAvail(); // Excludes menu bars
4142-
ImVec2 rightPaneEnd = rightPaneStart + contentSize;
4143-
4144-
// 3. Adjust for viewport offsets (multi-monitor/DPI aware)
4145-
rightPaneStart -= viewport->Pos;
4146-
rightPaneEnd -= viewport->Pos;
4147-
4148-
// 4. Calculate capture dimensions
4149-
int captureWidth = (int)(rightPaneEnd.x - rightPaneStart.x);
4150-
int captureHeight = (int)(rightPaneEnd.y - rightPaneStart.y);
4151-
4152-
int w = static_cast<int>(captureWidth);
4153-
int h = static_cast<int>(captureHeight);
4154-
mx::ImagePtr frameImage = mx::Image::create((unsigned int)(w),
4155-
(unsigned int)(h), 3);
4156-
frameImage->createResourceBuffer();
4157-
4158-
// 5. Read pixels (with OpenGL Y flip)
4159-
//std::vector<unsigned char> pixels(captureWidth * captureHeight * 4);
4160-
glReadPixels(
4161-
(int)rightPaneStart.x,
4162-
(int)(viewport->Size.y - rightPaneEnd.y), // Flip Y coordinate
4163-
captureWidth,
4164-
captureHeight,
4165-
GL_RGB,
4166-
GL_UNSIGNED_BYTE,
4167-
frameImage->getResourceBuffer()
4168-
);
4169-
4170-
#endif
4171-
4172-
return frameImage;
4173-
}
4119+
int h = static_cast<int>(rightPaneSize.y);
4120+
mx::ImagePtr image = mx::Image::create(static_cast<unsigned int>(w), static_cast<unsigned int>(h), 3);
4121+
if (image)
4122+
{
4123+
image->createResourceBuffer();
41744124

4125+
glFlush();
4126+
glReadPixels(static_cast<int>(rightPanePos.x), 0, w, h,
4127+
GL_RGB, GL_UNSIGNED_BYTE,
4128+
image->getResourceBuffer()
4129+
);
4130+
}
4131+
return image;
4132+
}
41754133

41764134
void Graph::drawGraph(ImVec2 mousePos)
41774135
{
@@ -4193,24 +4151,26 @@ void Graph::drawGraph(ImVec2 mousePos)
41934151
io2.ConfigFlags = ImGuiConfigFlags_IsSRGB | ImGuiConfigFlags_NavEnableKeyboard;
41944152
io2.MouseDoubleClickTime = .5;
41954153

4196-
// Capture. Do before anything else to avoid window position changes from later UI calls
4197-
if (ImGui::IsKeyDown(ImGuiKey_LeftCtrl) && ImGui::IsKeyPressed(ImGuiKey_B))
4154+
// Capture. Perform before other UI calls which can change window size / positioning.
4155+
if (io2.KeyCtrl && ImGui::IsKeyReleased(ImGuiKey_I))
41984156
{
4199-
mx::ImagePtr frameImage = getFrameImage();
4200-
std::string filename = "graph_capture.png";
4201-
if (frameImage && _renderer->getImageHandler()->saveImage(filename, frameImage, true))
4202-
{
4203-
std::cout << "Frame saved to: " << filename << std::endl;
4204-
}
4205-
else
4157+
// Capture entire window if SHIFT, otherwise capture node editor
4158+
bool captureWindow = io2.KeyShift;
4159+
_capturedImage = performSnapshot(captureWindow);
4160+
//std::string filename = "graph_capture.png";
4161+
if (_capturedImage)
42064162
{
4207-
std::cout << "Failed to write frame to disk: " << filename << std::endl;
4163+
//std::cout << "Frame saved to: " << filename << std::endl;
4164+
showSnapshotDialog();
42084165
}
4166+
//else
4167+
//{
4168+
// std::cout << "Failed to write frame to disk: " << filename << std::endl;
4169+
//}
42094170
}
42104171

42114172
graphButtons();
42124173

4213-
42144174
ed::Begin("My Editor");
42154175
{
42164176
ed::Suspend();
@@ -4627,6 +4587,8 @@ void Graph::drawGraph(ImVec2 mousePos)
46274587
ed::Resume();
46284588
}
46294589

4590+
saveSnapshotToFile();
4591+
46304592
ed::End();
46314593
ImGui::End();
46324594

source/MaterialXGraphEditor/Graph.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,10 @@ class Graph
236236

237237
void showHelp() const;
238238

239-
mx::ImagePtr getFrameImage() const;
239+
// Methods to capture snapshot to file
240+
mx::ImagePtr performSnapshot(bool captureWindow = false) const;
241+
void showSnapshotDialog();
242+
void saveSnapshotToFile();
240243

241244
private:
242245
mx::StringVec _geomFilter;
@@ -297,6 +300,7 @@ class Graph
297300
FileDialog _fileDialogSave;
298301
FileDialog _fileDialogImage;
299302
FileDialog _fileDialogGeom;
303+
FileDialog _fileDialogSnapshot;
300304
std::string _fileDialogImageInputName;
301305

302306
bool _isNodeGraph;
@@ -329,10 +333,13 @@ class Graph
329333
// Options
330334
bool _saveNodePositions;
331335

336+
// Panel information
332337
float _leftPaneWidth = 375.0f;
333-
float _leftPanelIndent = 15.0f;
338+
float _nodeEditorIndent = 15.0f;
334339
float _rightPaneWidth = 750.0f;
335340

341+
// Image capture
342+
mx::ImagePtr _capturedImage;
336343
};
337344

338345
#endif

0 commit comments

Comments
 (0)