From dc75a591151592dbd7df424e689ccd9a017b5efa Mon Sep 17 00:00:00 2001 From: enricoturri1966 Date: Wed, 19 Jan 2022 12:39:04 +0100 Subject: [PATCH] Added command Export as OBJ --- src/slic3r/GUI/GUI_Factories.cpp | 6 ++ src/slic3r/GUI/Plater.cpp | 141 ++++++++++++++++++++++++++++++- src/slic3r/GUI/Plater.hpp | 1 + 3 files changed, 147 insertions(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_Factories.cpp b/src/slic3r/GUI/GUI_Factories.cpp index c3aaa2f4ef..9854decf71 100644 --- a/src/slic3r/GUI/GUI_Factories.cpp +++ b/src/slic3r/GUI/GUI_Factories.cpp @@ -718,6 +718,12 @@ void MenuFactory::append_menu_item_export_stl(wxMenu* menu) 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, + []() { + 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); menu->AppendSeparator(); } diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index f3b5b144ae..f7d0b7fd75 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -37,6 +37,7 @@ #include "libslic3r/Format/STL.hpp" #include "libslic3r/Format/AMF.hpp" #include "libslic3r/Format/3mf.hpp" +#include "libslic3r/Format/OBJ.hpp" #include "libslic3r/GCode/ThumbnailData.hpp" #include "libslic3r/Model.hpp" #include "libslic3r/SLA/Hollowing.hpp" @@ -5855,7 +5856,7 @@ void Plater::export_stl(bool extended, bool selection_only) 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[2]); + inst_mesh.translate(0.0f, 0.0f, -inst_mesh.bounding_box().min.z()); // merge instance with global mesh mesh.merge(inst_mesh); @@ -5871,6 +5872,144 @@ void Plater::export_stl(bool extended, bool selection_only) // 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 0b26f09e11..a3b686549b 100644 --- a/src/slic3r/GUI/Plater.hpp +++ b/src/slic3r/GUI/Plater.hpp @@ -257,6 +257,7 @@ public: 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_amf(); bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path()); void reload_from_disk();