mirror of
https://git.mirrors.martin98.com/https://github.com/prusa3d/PrusaSlicer.git
synced 2025-08-15 11:25:53 +08:00
Added command Export as OBJ
This commit is contained in:
parent
f2a7245f36
commit
dc75a59115
@ -718,6 +718,12 @@ void MenuFactory::append_menu_item_export_stl(wxMenu* menu)
|
|||||||
const Selection& selection = plater()->canvas3D()->get_selection();
|
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();
|
return selection.is_single_full_instance() || selection.is_single_full_object() || selection.is_single_volume() || selection.is_single_modifier();
|
||||||
}, m_parent);
|
}, 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();
|
menu->AppendSeparator();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,6 +37,7 @@
|
|||||||
#include "libslic3r/Format/STL.hpp"
|
#include "libslic3r/Format/STL.hpp"
|
||||||
#include "libslic3r/Format/AMF.hpp"
|
#include "libslic3r/Format/AMF.hpp"
|
||||||
#include "libslic3r/Format/3mf.hpp"
|
#include "libslic3r/Format/3mf.hpp"
|
||||||
|
#include "libslic3r/Format/OBJ.hpp"
|
||||||
#include "libslic3r/GCode/ThumbnailData.hpp"
|
#include "libslic3r/GCode/ThumbnailData.hpp"
|
||||||
#include "libslic3r/Model.hpp"
|
#include "libslic3r/Model.hpp"
|
||||||
#include "libslic3r/SLA/Hollowing.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);
|
inst_mesh.merge(inst_object_mesh);
|
||||||
|
|
||||||
// ensure that the instance lays on the bed
|
// 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
|
// merge instance with global mesh
|
||||||
mesh.merge(inst_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));
|
// 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<float>());
|
||||||
|
}
|
||||||
|
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<SLAPrintObject::Instance>& 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()
|
void Plater::export_amf()
|
||||||
{
|
{
|
||||||
if (p->model.objects.empty()) { return; }
|
if (p->model.objects.empty()) { return; }
|
||||||
|
@ -257,6 +257,7 @@ public:
|
|||||||
|
|
||||||
void export_gcode(bool prefer_removable);
|
void export_gcode(bool prefer_removable);
|
||||||
void export_stl(bool extended = false, bool selection_only = false);
|
void export_stl(bool extended = false, bool selection_only = false);
|
||||||
|
void export_obj(bool extended = false, bool selection_only = false);
|
||||||
void export_amf();
|
void export_amf();
|
||||||
bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
|
bool export_3mf(const boost::filesystem::path& output_path = boost::filesystem::path());
|
||||||
void reload_from_disk();
|
void reload_from_disk();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user