@@ -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+
31833222void 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
41764134void 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
0 commit comments