Merge remote-tracking branch 'remotes/origin/et_toolpaths_export'
This commit is contained in:
commit
c6604ff55d
9 changed files with 276 additions and 2 deletions
|
@ -25,6 +25,8 @@
|
|||
#include <boost/filesystem/operations.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
#include <tbb/parallel_for.h>
|
||||
#include <tbb/spin_mutex.h>
|
||||
|
||||
|
@ -853,6 +855,209 @@ 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;
|
||||
|
||||
// collect color information to generate materials
|
||||
std::set<std::array<float, 4>> colors;
|
||||
for (const GLVolume* volume : this->volumes)
|
||||
{
|
||||
if (!can_export_to_obj(*volume))
|
||||
continue;
|
||||
|
||||
std::array<float, 4> 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<float, 4>& 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", SLIC3R_BUILD_ID);
|
||||
fprintf(fp, "\nmtllib ./%s\n", mat_filename.filename().string().c_str());
|
||||
|
||||
unsigned int vertices_count = 0;
|
||||
unsigned int volumes_count = 0;
|
||||
|
||||
for (const GLVolume* volume : this->volumes)
|
||||
{
|
||||
if (!can_export_to_obj(*volume))
|
||||
continue;
|
||||
|
||||
std::vector<float> vertices_and_normals_interleaved;
|
||||
std::vector<int> triangle_indices;
|
||||
std::vector<int> 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<float>(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())
|
||||
{
|
||||
// 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<int>::const_iterator it_begin = volume->indexed_vertex_array.triangle_indices.begin() + volume->tverts_range.first;
|
||||
std::vector<int>::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)
|
||||
{
|
||||
triangle_indices = std::vector<int>(size, 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())
|
||||
{
|
||||
// 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<int>::const_iterator it_begin = volume->indexed_vertex_array.quad_indices.begin() + volume->qverts_range.first;
|
||||
std::vector<int>::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)
|
||||
{
|
||||
quad_indices = std::vector<int>(size, 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())
|
||||
continue;
|
||||
|
||||
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", 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]);
|
||||
}
|
||||
|
||||
std::array<float, 4> 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)
|
||||
{
|
||||
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", volumes_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);
|
||||
}
|
||||
|
||||
++volumes_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,
|
||||
|
|
|
@ -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;
|
||||
|
@ -564,6 +564,10 @@ 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;
|
||||
|
||||
private:
|
||||
GLVolumeCollection(const GLVolumeCollection &other);
|
||||
GLVolumeCollection& operator=(const GLVolumeCollection &);
|
||||
|
|
|
@ -3403,6 +3403,16 @@ 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);
|
||||
}
|
||||
|
||||
bool GLCanvas3D::_is_shown_on_screen() const
|
||||
{
|
||||
return (m_canvas != nullptr) ? m_canvas->IsShownOnScreen() : false;
|
||||
|
|
|
@ -644,6 +644,9 @@ 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:
|
||||
bool _is_shown_on_screen() const;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -247,6 +247,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() && m_plater->has_toolpaths_to_export();
|
||||
}
|
||||
|
||||
bool MainFrame::can_export_supports() const
|
||||
{
|
||||
if ((m_plater == nullptr) || (m_plater->printer_technology() != ptSLA) || m_plater->model().objects.empty())
|
||||
|
@ -473,13 +478,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")),
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1763,6 +1763,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();
|
||||
|
@ -2457,6 +2462,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:
|
||||
|
@ -2507,6 +2513,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;
|
||||
}
|
||||
|
||||
|
@ -4095,6 +4107,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(); }
|
||||
|
||||
|
@ -4418,6 +4434,24 @@ void Plater::export_3mf(const boost::filesystem::path& output_path)
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
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.
|
||||
|
|
|
@ -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,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());
|
||||
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);
|
||||
|
|
Loading…
Reference in a new issue