diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index a46c7202da..d07bb268a3 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -483,6 +483,7 @@ struct FileWildcards { static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_STL */ { "STL files"sv, { ".stl"sv } }, /* FT_OBJ */ { "OBJ files"sv, { ".obj"sv } }, + /* FT_OBJECT */ { "Object files"sv, { ".stl"sv, ".obj"sv } }, /* FT_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } }, /* FT_3MF */ { "3MF files"sv, { ".3mf"sv } }, /* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"sv } }, diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index d3009a0fa6..bdbe420704 100644 --- a/src/slic3r/GUI/GUI_App.hpp +++ b/src/slic3r/GUI/GUI_App.hpp @@ -53,6 +53,7 @@ enum FileType { FT_STL, FT_OBJ, + FT_OBJECT, FT_AMF, FT_3MF, FT_GCODE, diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index 9854decf71..7b3476d711 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -712,14 +712,8 @@ wxMenuItem* MenuFactory::append_menu_item_simplify(wxMenu* menu) void MenuFactory::append_menu_item_export_stl(wxMenu* menu) { - append_menu_item(menu, wxID_ANY, _L("Export as STL") + dots, "", - [](wxCommandEvent&) { plater()->export_stl(false, true); }, "", nullptr, - []() { - const Selection& selection = plater()->canvas3D()->get_selection(); - return selection.is_single_full_instance() || selection.is_single_full_object() || selection.is_single_volume() || selection.is_single_modifier(); - }, m_parent); - append_menu_item(menu, wxID_ANY, _L("Export as OBJ") + dots, "", - [](wxCommandEvent&) { plater()->export_obj(false, true); }, "", nullptr, + append_menu_item(menu, wxID_ANY, _L("Export as STL/OBJ") + dots, "", + [](wxCommandEvent&) { plater()->export_stl_obj(false, true); }, "", nullptr, []() { const Selection& selection = plater()->canvas3D()->get_selection(); return selection.is_single_full_instance() || selection.is_single_full_object() || selection.is_single_volume() || selection.is_single_modifier(); diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index dd6f45318f..08d2f682b4 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1239,11 +1239,11 @@ void MainFrame::init_menubar_as_editor() [this](wxCommandEvent&) { if (m_plater) m_plater->export_gcode(true); }, "export_to_sd", nullptr, [this]() {return can_export_gcode_sd(); }, this); export_menu->AppendSeparator(); - 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(); }, "export_plater", nullptr, + append_menu_item(export_menu, wxID_ANY, _L("Export Plate as &STL/OBJ") + dots, _L("Export current plate as STL/OBJ"), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl_obj(); }, "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"), - [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl(true); }, "export_plater", nullptr, + append_menu_item(export_menu, wxID_ANY, _L("Export Plate as STL/OBJ &Including Supports") + dots, _L("Export current plate as STL/OBJ including supports"), + [this](wxCommandEvent&) { if (m_plater) m_plater->export_stl_obj(true); }, "export_plater", nullptr, [this](){return can_export_supports(); }, this); // Deprecating AMF export. Let's wait for user feedback. // append_menu_item(export_menu, wxID_ANY, _L("Export Plate as &AMF") + dots, _L("Export current plate as AMF"), diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f7d0b7fd75..22d2dc9a66 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -2803,6 +2803,7 @@ wxString Plater::priv::get_export_file(GUI::FileType file_type) case FT_3MF: case FT_GCODE: case FT_OBJ: + case FT_OBJECT: wildcard = file_wildcards(file_type); break; default: @@ -5733,11 +5734,11 @@ void Plater::export_gcode(bool prefer_removable) } } -void Plater::export_stl(bool extended, bool selection_only) +void Plater::export_stl_obj(bool extended, bool selection_only) { if (p->model.objects.empty()) { return; } - wxString path = p->get_export_file(FT_STL); + wxString path = p->get_export_file(FT_OBJECT); if (path.empty()) { return; } const std::string path_u8 = into_u8(path); @@ -5868,148 +5869,13 @@ void Plater::export_stl(bool extended, bool selection_only) } } - Slic3r::store_stl(path_u8.c_str(), &mesh, true); + if (path.EndsWith(".stl")) + Slic3r::store_stl(path_u8.c_str(), &mesh, true); + else if (path.EndsWith(".obj")) + Slic3r::store_obj(path_u8.c_str(), &mesh); // p->statusbar()->set_status_text(format_wxstr(_L("STL file exported to %s"), path)); } -void Plater::export_obj(bool extended, bool selection_only) -{ - if (p->model.objects.empty()) { return; } - - wxString path = p->get_export_file(FT_OBJ); - if (path.empty()) { return; } - const std::string path_u8 = into_u8(path); - - wxBusyCursor wait; - - const auto& selection = p->get_selection(); - const auto obj_idx = selection.get_object_idx(); - if (selection_only && (obj_idx == -1 || selection.is_wipe_tower())) - return; - - // Following lambda generates a combined mesh for export with normals pointing outwards. - auto mesh_to_export = [](const ModelObject& mo, int instance_id) { - TriangleMesh mesh; - for (const ModelVolume* v : mo.volumes) - if (v->is_model_part()) { - TriangleMesh vol_mesh(v->mesh()); - vol_mesh.transform(v->get_matrix(), true); - mesh.merge(vol_mesh); - } - if (instance_id == -1) { - TriangleMesh vols_mesh(mesh); - mesh = TriangleMesh(); - for (const ModelInstance* i : mo.instances) { - TriangleMesh m = vols_mesh; - m.transform(i->get_matrix(), true); - mesh.merge(m); - } - } - else if (0 <= instance_id && instance_id < int(mo.instances.size())) - mesh.transform(mo.instances[instance_id]->get_matrix(), true); - - return mesh; - }; - - TriangleMesh mesh; - if (p->printer_technology == ptFFF) { - if (selection_only) { - const ModelObject* model_object = p->model.objects[obj_idx]; - if (selection.get_mode() == Selection::Instance) - mesh = mesh_to_export(*model_object, (selection.is_single_full_object() && model_object->instances.size() > 1) ? -1 : selection.get_instance_idx()); - else { - const GLVolume* volume = selection.get_volume(*selection.get_volume_idxs().begin()); - mesh = model_object->volumes[volume->volume_idx()]->mesh(); - mesh.transform(volume->get_volume_transformation().get_matrix(), true); - } - - if (!selection.is_single_full_object() || model_object->instances.size() == 1) - mesh.translate(-model_object->origin_translation.cast()); - } - else { - for (const ModelObject* o : p->model.objects) { - mesh.merge(mesh_to_export(*o, -1)); - } - } - } - else { - // This is SLA mode, all objects have only one volume. - // However, we must have a look at the backend to load - // hollowed mesh and/or supports - - const PrintObjects& objects = p->sla_print.objects(); - for (const SLAPrintObject* object : objects) { - const ModelObject* model_object = object->model_object(); - if (selection_only) { - if (model_object->id() != p->model.objects[obj_idx]->id()) - continue; - } - const Transform3d mesh_trafo_inv = object->trafo().inverse(); - const bool is_left_handed = object->is_left_handed(); - - TriangleMesh pad_mesh; - const bool has_pad_mesh = extended && object->has_mesh(slaposPad); - if (has_pad_mesh) { - pad_mesh = object->get_mesh(slaposPad); - pad_mesh.transform(mesh_trafo_inv); - } - - TriangleMesh supports_mesh; - const bool has_supports_mesh = extended && object->has_mesh(slaposSupportTree); - if (has_supports_mesh) { - supports_mesh = object->get_mesh(slaposSupportTree); - supports_mesh.transform(mesh_trafo_inv); - } - const std::vector& obj_instances = object->instances(); - for (const SLAPrintObject::Instance& obj_instance : obj_instances) { - auto it = std::find_if(model_object->instances.begin(), model_object->instances.end(), - [&obj_instance](const ModelInstance* mi) { return mi->id() == obj_instance.instance_id; }); - assert(it != model_object->instances.end()); - - if (it != model_object->instances.end()) { - const bool one_inst_only = selection_only && !selection.is_single_full_object(); - - const int instance_idx = it - model_object->instances.begin(); - const Transform3d& inst_transform = one_inst_only - ? Transform3d::Identity() - : object->model_object()->instances[instance_idx]->get_transformation().get_matrix(); - - TriangleMesh inst_mesh; - - if (has_pad_mesh) { - TriangleMesh inst_pad_mesh = pad_mesh; - inst_pad_mesh.transform(inst_transform, is_left_handed); - inst_mesh.merge(inst_pad_mesh); - } - - if (has_supports_mesh) { - TriangleMesh inst_supports_mesh = supports_mesh; - inst_supports_mesh.transform(inst_transform, is_left_handed); - inst_mesh.merge(inst_supports_mesh); - } - - TriangleMesh inst_object_mesh = object->get_mesh_to_slice(); - inst_object_mesh.transform(mesh_trafo_inv); - inst_object_mesh.transform(inst_transform, is_left_handed); - - inst_mesh.merge(inst_object_mesh); - - // ensure that the instance lays on the bed - inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z()); - - // merge instance with global mesh - mesh.merge(inst_mesh); - - if (one_inst_only) - break; - } - } - } - } - - Slic3r::store_obj(path_u8.c_str(), &mesh); -} - void Plater::export_amf() { if (p->model.objects.empty()) { return; } diff --git a/src/slic3r/GUI/Plater.hpp b/src/slic3r/GUI/Plater.hpp index a3b686549b..99396b8c2a 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -256,8 +256,7 @@ public: void cut(size_t obj_idx, size_t instance_idx, coordf_t z, ModelObjectCutAttributes attributes); void export_gcode(bool prefer_removable); - void export_stl(bool extended = false, bool selection_only = false); - void export_obj(bool extended = false, bool selection_only = false); + void export_stl_obj(bool extended = false, bool selection_only = false); void export_amf(); bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); void reload_from_disk();