From 1f6aab312b544ff2c0225cc3516c543a3454f078 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Aug 2019 09:01:09 +0200 Subject: [PATCH 1/6] 1st installment of export of gcode toolpaths to obj file --- src/slic3r/GUI/3DScene.cpp | 102 +++++++++++++++++++++++++++++++++ src/slic3r/GUI/3DScene.hpp | 3 + src/slic3r/GUI/GLCanvas3D.cpp | 5 ++ src/slic3r/GUI/GLCanvas3D.hpp | 2 + src/slic3r/GUI/GUI_Preview.hpp | 2 + src/slic3r/GUI/MainFrame.cpp | 11 +++- src/slic3r/GUI/MainFrame.hpp | 1 + src/slic3r/GUI/Plater.cpp | 30 ++++++++++ src/slic3r/GUI/Plater.hpp | 5 ++ 9 files changed, 160 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index ad2d9305f..6d9302ba6 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -853,6 +853,108 @@ std::string GLVolumeCollection::log_memory_info() const return " (GLVolumeCollection RAM: " + format_memsize_MB(this->cpu_memory_used()) + " GPU: " + format_memsize_MB(this->gpu_memory_used()) + " Both: " + format_memsize_MB(this->gpu_memory_used()) + ")"; } +void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const +{ + if (filename == nullptr) + return; + + FILE* fp = boost::nowide::fopen(filename, "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) << "GLVolumeCollection::export_toolpaths_to_obj: Couldn't open " << filename << " for writing"; + return; + } + + fprintf(fp, "# G-Code Toolpaths\n"); + fprintf(fp, "# Generated by %s based on Slic3r\n\n", SLIC3R_BUILD_ID); + + unsigned int vertices_count = 0; + unsigned int volume_count = 0; + + for (const GLVolume* volume : this->volumes) + { + if (!volume->is_extrusion_path) + continue; + + std::vector vertices_and_normals_interleaved; + std::vector triangle_indices; + std::vector quad_indices; + + if (!volume->indexed_vertex_array.vertices_and_normals_interleaved.empty()) + vertices_and_normals_interleaved = volume->indexed_vertex_array.vertices_and_normals_interleaved; + else if ((volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id != 0) && (volume->indexed_vertex_array.vertices_and_normals_interleaved_size != 0)) + { + vertices_and_normals_interleaved = std::vector(volume->indexed_vertex_array.vertices_and_normals_interleaved_size, 0.0f); + + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)); + glsafe(::glGetBufferSubData(GL_ARRAY_BUFFER, 0, vertices_and_normals_interleaved.size() * sizeof(float), vertices_and_normals_interleaved.data())); + glsafe(::glBindBuffer(GL_ARRAY_BUFFER, 0)); + } + else + continue; + + if (!volume->indexed_vertex_array.triangle_indices.empty()) + triangle_indices = volume->indexed_vertex_array.triangle_indices; + else if ((volume->indexed_vertex_array.triangle_indices_VBO_id != 0) && (volume->indexed_vertex_array.triangle_indices_size != 0)) + { + triangle_indices = std::vector(volume->indexed_vertex_array.triangle_indices_size, 0); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id)); + glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, triangle_indices.size() * sizeof(int), triangle_indices.data())); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + + if (!volume->indexed_vertex_array.quad_indices.empty()) + quad_indices = volume->indexed_vertex_array.quad_indices; + if ((volume->indexed_vertex_array.quad_indices_VBO_id != 0) && (volume->indexed_vertex_array.quad_indices_size != 0)) + { + quad_indices = std::vector(volume->indexed_vertex_array.quad_indices_size, 0); + + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id)); + glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, quad_indices.size() * sizeof(int), quad_indices.data())); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } + + if (triangle_indices.empty() && quad_indices.empty()) + continue; + + fprintf(fp, "\n# vertices volume %d\n", volume_count); + for (unsigned int i = 0; i < vertices_and_normals_interleaved.size(); i += 6) + { + fprintf(fp, "v %f %f %f\n", vertices_and_normals_interleaved[i + 3], vertices_and_normals_interleaved[i + 4], vertices_and_normals_interleaved[i + 5]); + } + + fprintf(fp, "\n# normals volume %d\n", volume_count); + for (unsigned int i = 0; i < vertices_and_normals_interleaved.size(); i += 6) + { + fprintf(fp, "vn %f %f %f\n", vertices_and_normals_interleaved[i + 0], vertices_and_normals_interleaved[i + 1], vertices_and_normals_interleaved[i + 2]); + } + + fprintf(fp, "\n# triangular facets volume %d\n", volume_count); + for (unsigned int i = 0; i < triangle_indices.size(); i += 3) + { + int id_v1 = vertices_count + 1 + triangle_indices[i + 0]; + int id_v2 = vertices_count + 1 + triangle_indices[i + 1]; + int id_v3 = vertices_count + 1 + triangle_indices[i + 2]; + fprintf(fp, "f %d//%d %d//%d %d//%d\n", id_v1, id_v1, id_v2, id_v2, id_v3, id_v3); + } + + fprintf(fp, "\n# quadrangular facets volume %d\n", volume_count); + for (unsigned int i = 0; i < quad_indices.size(); i += 4) + { + int id_v1 = vertices_count + 1 + quad_indices[i + 0]; + int id_v2 = vertices_count + 1 + quad_indices[i + 1]; + int id_v3 = vertices_count + 1 + quad_indices[i + 2]; + int id_v4 = vertices_count + 1 + quad_indices[i + 3]; + fprintf(fp, "f %d//%d %d//%d %d//%d %d//%d\n", id_v1, id_v1, id_v2, id_v2, id_v3, id_v3, id_v4, id_v4); + } + + ++volume_count; + vertices_count += vertices_and_normals_interleaved.size() / 6; + } + + fclose(fp); +} + // caller is responsible for supplying NO lines with zero length static void thick_lines_to_indexed_vertex_array( const Lines &lines, diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 482c2f580..779194720 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -564,6 +564,9 @@ public: // Return CPU, GPU and total memory log line. std::string log_memory_info() const; + // Export the geometry of the GLVolumes toolpaths of this collection into the file with the given path, in obj format + void export_toolpaths_to_obj(const char* filename) const; + private: GLVolumeCollection(const GLVolumeCollection &other); GLVolumeCollection& operator=(const GLVolumeCollection &); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 981cb38a9..2a87efab9 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3403,6 +3403,11 @@ void GLCanvas3D::msw_rescale() m_warning_texture.msw_rescale(*this); } +void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const +{ + m_volumes.export_toolpaths_to_obj(filename); +} + bool GLCanvas3D::_is_shown_on_screen() const { return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false; diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 577682fe2..4fd37e9b1 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -644,6 +644,8 @@ public: void get_undoredo_toolbar_additional_tooltip(unsigned int item_id, std::string& text) { return m_undoredo_toolbar.get_additional_tooltip(item_id, text); } void set_undoredo_toolbar_additional_tooltip(unsigned int item_id, const std::string& text) { m_undoredo_toolbar.set_additional_tooltip(item_id, text); } + void export_toolpaths_to_obj(const char* filename) const; + private: bool _is_shown_on_screen() const; diff --git a/src/slic3r/GUI/GUI_Preview.hpp b/src/slic3r/GUI/GUI_Preview.hpp index b626bd7bd..401b17a4b 100644 --- a/src/slic3r/GUI/GUI_Preview.hpp +++ b/src/slic3r/GUI/GUI_Preview.hpp @@ -130,6 +130,8 @@ public: void update_view_type(); + bool is_loaded() const { return m_loaded; } + private: bool init(wxWindow* parent, Bed3D& bed, Camera& camera, GLToolbar& view_toolbar, Model* model); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index b0945aea8..2609926ed 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -245,6 +245,11 @@ bool MainFrame::can_export_model() const return (m_plater != nullptr) && !m_plater->model().objects.empty(); } +bool MainFrame::can_export_toolpaths() const +{ + return (m_plater != nullptr) && (m_plater->printer_technology() == ptFFF) && m_plater->is_preview_shown() && m_plater->is_preview_loaded(); +} + bool MainFrame::can_export_supports() const { if ((m_plater == nullptr) || (m_plater->printer_technology() != ptSLA) || m_plater->model().objects.empty()) @@ -471,13 +476,17 @@ void MainFrame::init_menubar() append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &STL")) + dots, _(L("Export current plate as STL")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(); }, menu_icon("export_plater"), nullptr, [this](){return can_export_model(); }, this); - append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL including supports")) + dots, _(L("Export current plate as STL including supports")), + append_menu_item(export_menu, wxID_ANY, _(L("Export plate as STL &including supports")) + dots, _(L("Export current plate as STL including supports")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, menu_icon("export_plater"), nullptr, [this](){return can_export_supports(); }, this); append_menu_item(export_menu, wxID_ANY, _(L("Export plate as &AMF")) + dots, _(L("Export current plate as AMF")), [this](wxCommandEvent&) { if (m_plater) m_plater->export_amf(); }, menu_icon("export_plater"), nullptr, [this](){return can_export_model(); }, this); export_menu->AppendSeparator(); + append_menu_item(export_menu, wxID_ANY, _(L("Export &toolpaths as OBJ")) + dots, _(L("Export toolpaths as OBJ")), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_toolpaths_to_obj(); }, menu_icon("export_plater"), nullptr, + [this]() {return can_export_toolpaths(); }, this); + export_menu->AppendSeparator(); append_menu_item(export_menu, wxID_ANY, _(L("Export &Config")) +dots +"\tCtrl+E", _(L("Export current configuration to file")), [this](wxCommandEvent&) { export_config(); }, menu_icon("export_config")); append_menu_item(export_menu, wxID_ANY, _(L("Export Config &Bundle")) + dots, _(L("Export all presets to file")), diff --git a/src/slic3r/GUI/MainFrame.hpp b/src/slic3r/GUI/MainFrame.hpp index 0e8a053e0..aa1e3d500 100644 --- a/src/slic3r/GUI/MainFrame.hpp +++ b/src/slic3r/GUI/MainFrame.hpp @@ -65,6 +65,7 @@ class MainFrame : public DPIFrame bool can_start_new_project() const; bool can_save() const; bool can_export_model() const; + bool can_export_toolpaths() const; bool can_export_supports() const; bool can_export_gcode() const; bool can_send_gcode() const; diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 99f63da8a..2eb572931 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1764,6 +1764,11 @@ struct Plater::priv void select_view(const std::string& direction); void select_view_3D(const std::string& name); void select_next_view_3D(); + + bool is_preview_shown() const { return current_panel == preview; } + bool is_preview_loaded() const { return preview->is_loaded(); } + bool is_view3D_shown() const { return current_panel == view3D; } + void reset_all_gizmos(); void update_ui_from_settings(); ProgressStatusBar* statusbar(); @@ -2458,6 +2463,7 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type) case FT_AMF: case FT_3MF: case FT_GCODE: + case FT_OBJ: wildcard = file_wildcards(file_type); break; default: @@ -2508,6 +2514,12 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type) dlg_title = _(L("Save file as:")); break; } + case FT_OBJ: + { + output_file.replace_extension("obj"); + dlg_title = _(L("Export OBJ file:")); + break; + } default: break; } @@ -4097,6 +4109,10 @@ void Plater::select_view(const std::string& direction) { p->select_view(directio void Plater::select_view_3D(const std::string& name) { p->select_view_3D(name); } +bool Plater::is_preview_shown() const { return p->is_preview_shown(); } +bool Plater::is_preview_loaded() const { return p->is_preview_loaded(); } +bool Plater::is_view3D_shown() const { return p->is_view3D_shown(); } + void Plater::select_all() { p->select_all(); } void Plater::deselect_all() { p->deselect_all(); } @@ -4420,6 +4436,20 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) } } +void Plater::export_toolpaths_to_obj() +{ + if ((printer_technology() != ptFFF) || !is_preview_loaded()) + return; + + wxString path = p->get_export_file(FT_OBJ); + if (path.empty()) + return; + + wxBusyCursor wait; + + p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str()); +} + void Plater::reslice() { // Stop arrange and (or) optimize rotation tasks. diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index d7f7f3791..40961ae46 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -156,6 +156,10 @@ public: void select_view(const std::string& direction); void select_view_3D(const std::string& name); + bool is_preview_shown() const; + bool is_preview_loaded() const; + bool is_view3D_shown() const; + // Called after the Preferences dialog is closed and the program settings are saved. // Update the UI based on the current preferences. void update_ui_from_settings(); @@ -179,6 +183,7 @@ public: void export_stl(bool extended = false, bool selection_only = false); void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); + void export_toolpaths_to_obj(); void reslice(); void reslice_SLA_supports(const ModelObject &object); void changed_object(int obj_idx); From 730283a9e9dc06426b1927e60c1dca52ef2da571 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Aug 2019 09:51:25 +0200 Subject: [PATCH 2/6] Export to obj file only toolpaths visible in 3D scene --- src/slic3r/GUI/3DScene.cpp | 26 +++++++++++++++++--------- src/slic3r/GUI/3DScene.hpp | 2 +- 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 6d9302ba6..d5fed6d42 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -872,7 +872,7 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const for (const GLVolume* volume : this->volumes) { - if (!volume->is_extrusion_path) + if (!volume->is_active || !volume->is_extrusion_path) continue; std::vector vertices_and_normals_interleaved; @@ -896,22 +896,30 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const triangle_indices = volume->indexed_vertex_array.triangle_indices; else if ((volume->indexed_vertex_array.triangle_indices_VBO_id != 0) && (volume->indexed_vertex_array.triangle_indices_size != 0)) { - triangle_indices = std::vector(volume->indexed_vertex_array.triangle_indices_size, 0); + size_t size = std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first); + if (size != 0) + { + triangle_indices = std::vector(size, 0); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id)); - glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, triangle_indices.size() * sizeof(int), triangle_indices.data())); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.triangle_indices_VBO_id)); + glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, volume->tverts_range.first * sizeof(int), size * sizeof(int), triangle_indices.data())); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } } if (!volume->indexed_vertex_array.quad_indices.empty()) quad_indices = volume->indexed_vertex_array.quad_indices; if ((volume->indexed_vertex_array.quad_indices_VBO_id != 0) && (volume->indexed_vertex_array.quad_indices_size != 0)) { - quad_indices = std::vector(volume->indexed_vertex_array.quad_indices_size, 0); + size_t size = std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first); + if (size != 0) + { + quad_indices = std::vector(size, 0); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id)); - glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, quad_indices.size() * sizeof(int), quad_indices.data())); - glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, volume->indexed_vertex_array.quad_indices_VBO_id)); + glsafe(::glGetBufferSubData(GL_ELEMENT_ARRAY_BUFFER, volume->qverts_range.first * sizeof(int), size * sizeof(int), quad_indices.data())); + glsafe(::glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0)); + } } if (triangle_indices.empty() && quad_indices.empty()) diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 779194720..9c0e50cad 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -439,7 +439,7 @@ public: bool empty() const { return this->indexed_vertex_array.empty(); } - void set_range(coordf_t low, coordf_t high); + void set_range(double low, double high); void render() const; void render(int color_id, int detection_id, int worldmatrix_id) const; From 58473f84eedac0556e11ab54db6f58ac0d1353a3 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Aug 2019 11:33:58 +0200 Subject: [PATCH 3/6] Check for existence of gcode toolpaths that can be exported to obj file --- src/slic3r/GUI/3DScene.cpp | 42 +++++++++++++++++++++++++++++------ src/slic3r/GUI/3DScene.hpp | 1 + src/slic3r/GUI/GLCanvas3D.cpp | 5 +++++ src/slic3r/GUI/GLCanvas3D.hpp | 1 + src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 10 ++++++--- src/slic3r/GUI/Plater.hpp | 3 ++- 7 files changed, 52 insertions(+), 12 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index d5fed6d42..60928c78b 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -853,11 +853,39 @@ std::string GLVolumeCollection::log_memory_info() const return " (GLVolumeCollection RAM: " + format_memsize_MB(this->cpu_memory_used()) + " GPU: " + format_memsize_MB(this->gpu_memory_used()) + " Both: " + format_memsize_MB(this->gpu_memory_used()) + ")"; } +bool can_export_to_obj(const GLVolume& volume) +{ + if (!volume.is_active || !volume.is_extrusion_path) + return false; + + if (volume.indexed_vertex_array.triangle_indices.empty() && (std::min(volume.indexed_vertex_array.triangle_indices_size, volume.tverts_range.second - volume.tverts_range.first) == 0)) + return false; + + if (volume.indexed_vertex_array.quad_indices.empty() && (std::min(volume.indexed_vertex_array.quad_indices_size, volume.qverts_range.second - volume.qverts_range.first) == 0)) + return false; + + return true; +} + +bool GLVolumeCollection::has_toolpaths_to_export() const +{ + for (const GLVolume* volume : this->volumes) + { + if (can_export_to_obj(*volume)) + return true; + } + + return false; +} + void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const { if (filename == nullptr) return; + if (!has_toolpaths_to_export()) + return; + FILE* fp = boost::nowide::fopen(filename, "w"); if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "GLVolumeCollection::export_toolpaths_to_obj: Couldn't open " << filename << " for writing"; @@ -868,11 +896,11 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const fprintf(fp, "# Generated by %s based on Slic3r\n\n", SLIC3R_BUILD_ID); unsigned int vertices_count = 0; - unsigned int volume_count = 0; + unsigned int volumes_count = 0; for (const GLVolume* volume : this->volumes) { - if (!volume->is_active || !volume->is_extrusion_path) + if (!can_export_to_obj(*volume)) continue; std::vector vertices_and_normals_interleaved; @@ -925,19 +953,19 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const if (triangle_indices.empty() && quad_indices.empty()) continue; - fprintf(fp, "\n# vertices volume %d\n", volume_count); + fprintf(fp, "\n# vertices volume %d\n", volumes_count); for (unsigned int i = 0; i < vertices_and_normals_interleaved.size(); i += 6) { fprintf(fp, "v %f %f %f\n", vertices_and_normals_interleaved[i + 3], vertices_and_normals_interleaved[i + 4], vertices_and_normals_interleaved[i + 5]); } - fprintf(fp, "\n# normals volume %d\n", volume_count); + fprintf(fp, "\n# normals volume %d\n", volumes_count); for (unsigned int i = 0; i < vertices_and_normals_interleaved.size(); i += 6) { fprintf(fp, "vn %f %f %f\n", vertices_and_normals_interleaved[i + 0], vertices_and_normals_interleaved[i + 1], vertices_and_normals_interleaved[i + 2]); } - fprintf(fp, "\n# triangular facets volume %d\n", volume_count); + fprintf(fp, "\n# triangular facets volume %d\n", volumes_count); for (unsigned int i = 0; i < triangle_indices.size(); i += 3) { int id_v1 = vertices_count + 1 + triangle_indices[i + 0]; @@ -946,7 +974,7 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const fprintf(fp, "f %d//%d %d//%d %d//%d\n", id_v1, id_v1, id_v2, id_v2, id_v3, id_v3); } - fprintf(fp, "\n# quadrangular facets volume %d\n", volume_count); + fprintf(fp, "\n# quadrangular facets volume %d\n", volumes_count); for (unsigned int i = 0; i < quad_indices.size(); i += 4) { int id_v1 = vertices_count + 1 + quad_indices[i + 0]; @@ -956,7 +984,7 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const fprintf(fp, "f %d//%d %d//%d %d//%d %d//%d\n", id_v1, id_v1, id_v2, id_v2, id_v3, id_v3, id_v4, id_v4); } - ++volume_count; + ++volumes_count; vertices_count += vertices_and_normals_interleaved.size() / 6; } diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 9c0e50cad..e0603ebc0 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -564,6 +564,7 @@ public: // Return CPU, GPU and total memory log line. std::string log_memory_info() const; + bool has_toolpaths_to_export() const; // Export the geometry of the GLVolumes toolpaths of this collection into the file with the given path, in obj format void export_toolpaths_to_obj(const char* filename) const; diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2a87efab9..67f30c6e4 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -3403,6 +3403,11 @@ void GLCanvas3D::msw_rescale() m_warning_texture.msw_rescale(*this); } +bool GLCanvas3D::has_toolpaths_to_export() const +{ + return m_volumes.has_toolpaths_to_export(); +} + void GLCanvas3D::export_toolpaths_to_obj(const char* filename) const { m_volumes.export_toolpaths_to_obj(filename); diff --git a/src/slic3r/GUI/GLCanvas3D.hpp b/src/slic3r/GUI/GLCanvas3D.hpp index 4fd37e9b1..d23e29478 100644 --- a/src/slic3r/GUI/GLCanvas3D.hpp +++ b/src/slic3r/GUI/GLCanvas3D.hpp @@ -644,6 +644,7 @@ public: void get_undoredo_toolbar_additional_tooltip(unsigned int item_id, std::string& text) { return m_undoredo_toolbar.get_additional_tooltip(item_id, text); } void set_undoredo_toolbar_additional_tooltip(unsigned int item_id, const std::string& text) { m_undoredo_toolbar.set_additional_tooltip(item_id, text); } + bool has_toolpaths_to_export() const; void export_toolpaths_to_obj(const char* filename) const; private: diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index b3a87f635..800ac55a9 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -247,7 +247,7 @@ bool MainFrame::can_export_model() const bool MainFrame::can_export_toolpaths() const { - return (m_plater != nullptr) && (m_plater->printer_technology() == ptFFF) && m_plater->is_preview_shown() && m_plater->is_preview_loaded(); + return (m_plater != nullptr) && (m_plater->printer_technology() == ptFFF) && m_plater->is_preview_shown() && m_plater->is_preview_loaded() && m_plater->has_toolpaths_to_export(); } bool MainFrame::can_export_supports() const diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 773557ccf..b804c2479 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -4434,7 +4434,12 @@ void Plater::export_3mf(const boost::filesystem::path& output_path) } } -void Plater::export_toolpaths_to_obj() +bool Plater::has_toolpaths_to_export() const +{ + return p->preview->get_canvas3d()->has_toolpaths_to_export(); +} + +void Plater::export_toolpaths_to_obj() const { if ((printer_technology() != ptFFF) || !is_preview_loaded()) return; @@ -4442,9 +4447,8 @@ void Plater::export_toolpaths_to_obj() wxString path = p->get_export_file(FT_OBJ); if (path.empty()) return; - + wxBusyCursor wait; - p->preview->get_canvas3d()->export_toolpaths_to_obj(into_u8(path).c_str()); } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index 40961ae46..0bd01835e 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -183,7 +183,8 @@ public: void export_stl(bool extended = false, bool selection_only = false); void export_amf(); void export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); - void export_toolpaths_to_obj(); + bool has_toolpaths_to_export() const; + void export_toolpaths_to_obj() const; void reslice(); void reslice_SLA_supports(const ModelObject &object); void changed_object(int obj_idx); From f63b3ba600d26efcd31db87979849d6fc25fc88e Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Aug 2019 12:10:15 +0200 Subject: [PATCH 4/6] Fixed gcode toolpaths data for export to obj file when taken from cpu --- src/slic3r/GUI/3DScene.cpp | 28 +++++++++++++++++++++++++--- 1 file changed, 25 insertions(+), 3 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 60928c78b..88a7edee6 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -908,9 +908,11 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const std::vector quad_indices; if (!volume->indexed_vertex_array.vertices_and_normals_interleaved.empty()) + // data are in CPU memory vertices_and_normals_interleaved = volume->indexed_vertex_array.vertices_and_normals_interleaved; else if ((volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id != 0) && (volume->indexed_vertex_array.vertices_and_normals_interleaved_size != 0)) { + // data are in GPU memory vertices_and_normals_interleaved = std::vector(volume->indexed_vertex_array.vertices_and_normals_interleaved_size, 0.0f); glsafe(::glBindBuffer(GL_ARRAY_BUFFER, volume->indexed_vertex_array.vertices_and_normals_interleaved_VBO_id)); @@ -921,9 +923,19 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const continue; if (!volume->indexed_vertex_array.triangle_indices.empty()) - triangle_indices = volume->indexed_vertex_array.triangle_indices; + { + // data are in CPU memory + size_t size = std::min(volume->indexed_vertex_array.triangle_indices.size(), volume->tverts_range.second - volume->tverts_range.first); + if (size != 0) + { + std::vector::const_iterator it_begin = volume->indexed_vertex_array.triangle_indices.begin() + volume->tverts_range.first; + std::vector::const_iterator it_end = volume->indexed_vertex_array.triangle_indices.begin() + volume->tverts_range.first + size; + std::copy(it_begin, it_end, std::back_inserter(triangle_indices)); + } + } else if ((volume->indexed_vertex_array.triangle_indices_VBO_id != 0) && (volume->indexed_vertex_array.triangle_indices_size != 0)) { + // data are in GPU memory size_t size = std::min(volume->indexed_vertex_array.triangle_indices_size, volume->tverts_range.second - volume->tverts_range.first); if (size != 0) { @@ -936,9 +948,19 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const } if (!volume->indexed_vertex_array.quad_indices.empty()) - quad_indices = volume->indexed_vertex_array.quad_indices; - if ((volume->indexed_vertex_array.quad_indices_VBO_id != 0) && (volume->indexed_vertex_array.quad_indices_size != 0)) { + // data are in CPU memory + size_t size = std::min(volume->indexed_vertex_array.quad_indices.size(), volume->qverts_range.second - volume->qverts_range.first); + if (size != 0) + { + std::vector::const_iterator it_begin = volume->indexed_vertex_array.quad_indices.begin() + volume->qverts_range.first; + std::vector::const_iterator it_end = volume->indexed_vertex_array.quad_indices.begin() + volume->qverts_range.first + size; + std::copy(it_begin, it_end, std::back_inserter(quad_indices)); + } + } + else if ((volume->indexed_vertex_array.quad_indices_VBO_id != 0) && (volume->indexed_vertex_array.quad_indices_size != 0)) + { + // data are in GPU memory size_t size = std::min(volume->indexed_vertex_array.quad_indices_size, volume->qverts_range.second - volume->qverts_range.first); if (size != 0) { From 1b6490af4cded5d0fc4a1f25bac430d5aa4a9416 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Aug 2019 14:35:23 +0200 Subject: [PATCH 5/6] Export materials file for gcode toolpaths when exported to obj file --- src/slic3r/GUI/3DScene.cpp | 47 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 88a7edee6..d29e41d35 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -886,14 +886,51 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const if (!has_toolpaths_to_export()) return; - FILE* fp = boost::nowide::fopen(filename, "w"); + // collect color information to generate materials + std::set> colors; + for (const GLVolume* volume : this->volumes) + { + if (!can_export_to_obj(*volume)) + continue; + + std::array color; + ::memcpy((void*)color.data(), (const void*)volume->color, 4 * sizeof(float)); + colors.insert(color); + } + + // save materials file + boost::filesystem::path mat_filename(filename); + mat_filename.replace_extension("mtl"); + FILE* fp = boost::nowide::fopen(mat_filename.string().c_str(), "w"); + if (fp == nullptr) { + BOOST_LOG_TRIVIAL(error) << "GLVolumeCollection::export_toolpaths_to_obj: Couldn't open " << mat_filename.string().c_str() << " for writing"; + return; + } + + fprintf(fp, "# G-Code Toolpaths Materials\n"); + fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID); + + unsigned int colors_count = 1; + for (const std::array& color : colors) + { + fprintf(fp, "\nnewmtl material_%d\n", colors_count++); + fprintf(fp, "Ka 1 1 1\n"); + fprintf(fp, "Kd %f %f %f\n", color[0], color[1], color[2]); + fprintf(fp, "Ks 0 0 0\n"); + } + + fclose(fp); + + // save geometry file + fp = boost::nowide::fopen(filename, "w"); if (fp == nullptr) { BOOST_LOG_TRIVIAL(error) << "GLVolumeCollection::export_toolpaths_to_obj: Couldn't open " << filename << " for writing"; return; } fprintf(fp, "# G-Code Toolpaths\n"); - fprintf(fp, "# Generated by %s based on Slic3r\n\n", SLIC3R_BUILD_ID); + fprintf(fp, "# Generated by %s based on Slic3r\n", SLIC3R_BUILD_ID); + fprintf(fp, "\nmtllib ./%s\n", mat_filename.filename().string().c_str()); unsigned int vertices_count = 0; unsigned int volumes_count = 0; @@ -987,6 +1024,12 @@ void GLVolumeCollection::export_toolpaths_to_obj(const char* filename) const fprintf(fp, "vn %f %f %f\n", vertices_and_normals_interleaved[i + 0], vertices_and_normals_interleaved[i + 1], vertices_and_normals_interleaved[i + 2]); } + std::array color; + ::memcpy((void*)color.data(), (const void*)volume->color, 4 * sizeof(float)); + colors.insert(color); + fprintf(fp, "\n# material volume %d\n", volumes_count); + fprintf(fp, "usemtl material_%lld\n", 1 + std::distance(colors.begin(), colors.find(color))); + fprintf(fp, "\n# triangular facets volume %d\n", volumes_count); for (unsigned int i = 0; i < triangle_indices.size(); i += 3) { From 6e522cea15833b0b525c8dddcf56fe9b9dceed40 Mon Sep 17 00:00:00 2001 From: Enrico Turri Date: Tue, 20 Aug 2019 14:58:27 +0200 Subject: [PATCH 6/6] Added missing include --- src/slic3r/GUI/3DScene.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index d29e41d35..8d1a154b4 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -25,6 +25,8 @@ #include #include +#include + #include #include