From 88ba89dbbc8ccf62ae7a9a27a68da4ab9bc585ea Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Tue, 26 Jul 2022 14:48:26 +0200 Subject: [PATCH 01/10] STEP: Implementation ported from BambuStudio: CMake handling is different STEP: Removed preprocessing stage STEP: Small refactoring STEP: Bigger refactoring STEP: Changed naming on loaded object and volumes: If the STEP contains exactly one named volume, the object and its first volume will both have that name. Otherwise, filename w/o suffix is used as object name and volumes are named using names from the STEP (if there is none, untranslated "PartN" string is used). STEP: Load the libraries dynamically on Win wip --- deps/CMakeLists.txt | 2 + deps/OCCT/OCCT.cmake | 22 ++++ src/CMakeLists.txt | 5 +- src/libslic3r/CMakeLists.txt | 2 + src/libslic3r/Format/STEP.cpp | 110 +++++++++++++++++ src/libslic3r/Format/STEP.hpp | 19 +++ src/libslic3r/Model.cpp | 3 + src/libslic3r/TriangleMesh.cpp | 1 + src/occt_wrapper/CMakeLists.txt | 51 ++++++++ src/occt_wrapper/OCCTWrapper.cpp | 201 +++++++++++++++++++++++++++++++ src/occt_wrapper/OCCTWrapper.hpp | 27 +++++ src/slic3r/GUI/GUI_App.cpp | 3 +- src/slic3r/GUI/GUI_App.hpp | 1 + 13 files changed, 443 insertions(+), 4 deletions(-) create mode 100644 deps/OCCT/OCCT.cmake create mode 100644 src/libslic3r/Format/STEP.cpp create mode 100644 src/libslic3r/Format/STEP.hpp create mode 100644 src/occt_wrapper/CMakeLists.txt create mode 100644 src/occt_wrapper/OCCTWrapper.cpp create mode 100644 src/occt_wrapper/OCCTWrapper.hpp diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 56a8fa4299..0fe4e41c7e 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -189,6 +189,7 @@ endif () include(JPEG/JPEG.cmake) include(TIFF/TIFF.cmake) include(wxWidgets/wxWidgets.cmake) +include(OCCT/OCCT.cmake) set(_dep_list dep_Boost @@ -200,6 +201,7 @@ set(_dep_list dep_OpenVDB dep_OpenCSG dep_CGAL + dep_OCCT ${PNG_PKG} ${ZLIB_PKG} ${EXPAT_PKG} diff --git a/deps/OCCT/OCCT.cmake b/deps/OCCT/OCCT.cmake new file mode 100644 index 0000000000..9981ac15ee --- /dev/null +++ b/deps/OCCT/OCCT.cmake @@ -0,0 +1,22 @@ +prusaslicer_add_cmake_project(OCCT + #LMBBS: changed version to 7.6.2 + URL https://github.com/Open-Cascade-SAS/OCCT/archive/refs/tags/V7_6_2.zip + URL_HASH SHA256=c696b923593e8c18d059709717dbf155b3e72fdd283c8522047a790ec3a432c5 + + CMAKE_ARGS + -DINSTALL_DIR_LAYOUT=Unix # LMBBS + -DBUILD_LIBRARY_TYPE=Static + -DUSE_TK=OFF + -DUSE_TBB=OFF + -DUSE_FREETYPE=OFF + -DUSE_FFMPEG=OFF + -DUSE_VTK=OFF + -DUSE_FREETYPE=OFF + -DBUILD_MODULE_ApplicationFramework=OFF + #-DBUILD_MODULE_DataExchange=OFF + -DBUILD_MODULE_Draw=OFF + -DBUILD_MODULE_FoundationClasses=OFF + -DBUILD_MODULE_ModelingAlgorithms=OFF + -DBUILD_MODULE_ModelingData=OFF + -DBUILD_MODULE_Visualization=OFF +) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index cfee06342b..2ea8eaa043 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -15,11 +15,9 @@ add_subdirectory(semver) add_subdirectory(libigl) add_subdirectory(hints) add_subdirectory(qoi) - -# Adding libnest2d project for bin packing... add_subdirectory(libnest2d) - add_subdirectory(libslic3r) +add_subdirectory(occt_wrapper) if (SLIC3R_GUI) add_subdirectory(imgui) @@ -127,6 +125,7 @@ if (NOT WIN32 AND NOT APPLE) endif () target_link_libraries(PrusaSlicer libslic3r cereal) + if (APPLE) # add_compile_options(-stdlib=libc++) # add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 41ad68db5a..52ea9be081 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -94,6 +94,8 @@ add_library(libslic3r STATIC Format/STL.hpp Format/SL1.hpp Format/SL1.cpp + Format/STEP.hpp + Format/STEP.cpp GCode/ThumbnailData.cpp GCode/ThumbnailData.hpp GCode/Thumbnails.cpp diff --git a/src/libslic3r/Format/STEP.cpp b/src/libslic3r/Format/STEP.cpp new file mode 100644 index 0000000000..9b4d2c9eab --- /dev/null +++ b/src/libslic3r/Format/STEP.cpp @@ -0,0 +1,110 @@ +#include "STEP.hpp" +#include "occt_wrapper/OCCTWrapper.hpp" + +#include "libslic3r/Model.hpp" +#include "libslic3r/TriangleMesh.hpp" + +#include +#include + +#ifdef _WIN32 + #include +#else + #include + #include +#endif + + +namespace Slic3r { + +LoadStepFn get_load_step_fn() +{ + static LoadStepFn load_step_fn = nullptr; + + if (!load_step_fn) { +#ifdef _WIN32 + HMODULE module = LoadLibraryW(L"OCCTWrapper.dll"); + if (module == NULL) + throw Slic3r::RuntimeError("Cannot load OCCTWrapper.dll"); + + try { + const char* fn_name = "load_step_internal"; + FARPROC farproc = GetProcAddress(module, fn_name); + if (! farproc) { + DWORD ec = GetLastError(); + throw Slic3r::RuntimeError(std::string("Cannot load function from OCCTWrapper.dll: ") + fn_name + + "\n\nError code: " + std::to_string(ec)); + } + load_step_fn = reinterpret_cast(farproc); + } catch (const Slic3r::RuntimeError&) { + FreeLibrary(module); + throw; + } +#else + void *plugin_ptr = dlopen("OCCTWrapper.so", RTLD_NOW | RTLD_GLOBAL); + + if (plugin_ptr) { + load_step_fn = reinterpret_cast(dlsym(plugin_ptr, "load_step_internal")); + if (!load_step_fn) { + dlclose(plugin_ptr); + } + } +#endif + } + + return load_step_fn; +} + +bool load_step(const char *path, Model *model /*BBS:, ImportStepProgressFn proFn*/) +{ + OCCTResult occt_object; + + LoadStepFn load_step_fn = get_load_step_fn(); + + if (!load_step_fn) + return false; + + load_step_fn(path, &occt_object); + + assert(! occt_object.volumes.empty()); + + assert(boost::algorithm::iends_with(occt_object.object_name, ".stp") + || boost::algorithm::iends_with(occt_object.object_name, ".step")); + occt_object.object_name.erase(occt_object.object_name.find(".")); + assert(! occt_object.object_name.empty()); + + + ModelObject* new_object = model->add_object(); + new_object->input_file = path; + if (new_object->volumes.size() == 1 && ! occt_object.volumes.front().volume_name.empty()) + new_object->name = new_object->volumes.front()->name; + else + new_object->name = occt_object.object_name; + + + for (size_t i=0; iadd_volume(std::move(triangle_mesh)); + + new_volume->name = occt_object.volumes[i].volume_name.empty() + ? std::string("Part") + std::to_string(i+1) + : occt_object.volumes[i].volume_name; + new_volume->source.input_file = path; + new_volume->source.object_idx = (int)model->objects.size() - 1; + new_volume->source.volume_idx = (int)new_object->volumes.size() - 1; + } + + return true; +} + +}; // namespace Slic3r diff --git a/src/libslic3r/Format/STEP.hpp b/src/libslic3r/Format/STEP.hpp new file mode 100644 index 0000000000..8fbc604d8d --- /dev/null +++ b/src/libslic3r/Format/STEP.hpp @@ -0,0 +1,19 @@ +// Original implementation of STEP format import created by Bambulab. +// https://github.com/bambulab/BambuStudio +// Forked off commit 1555904, modified by Prusa Research. + +#ifndef slic3r_Format_STEP_hpp_ +#define slic3r_Format_STEP_hpp_ + +namespace Slic3r { + +class Model; + +//typedef std::function ImportStepProgressFn; + +// Load a step file into a provided model. +extern bool load_step(const char *path_str, Model *model /*LMBBS:, ImportStepProgressFn proFn = nullptr*/); + +}; // namespace Slic3r + +#endif /* slic3r_Format_STEP_hpp_ */ diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index efc66f4781..77011800dc 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -13,6 +13,7 @@ #include "Format/OBJ.hpp" #include "Format/STL.hpp" #include "Format/3mf.hpp" +#include "Format/STEP.hpp" #include @@ -114,6 +115,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c result = load_stl(input_file.c_str(), &model); else if (boost::algorithm::iends_with(input_file, ".obj")) result = load_obj(input_file.c_str(), &model); + else if (boost::algorithm::iends_with(input_file, ".step") || boost::algorithm::iends_with(input_file, ".stp")) + result = load_step(input_file.c_str(), &model); else if (boost::algorithm::iends_with(input_file, ".amf") || boost::algorithm::iends_with(input_file, ".amf.xml")) result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion); else if (boost::algorithm::iends_with(input_file, ".3mf")) diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 93a09a0d98..807ebcc9ac 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -1370,4 +1370,5 @@ bool its_write_stl_binary(const char *file, const char *label, const std::vector return true; } + } // namespace Slic3r diff --git a/src/occt_wrapper/CMakeLists.txt b/src/occt_wrapper/CMakeLists.txt new file mode 100644 index 0000000000..2629e1d59e --- /dev/null +++ b/src/occt_wrapper/CMakeLists.txt @@ -0,0 +1,51 @@ +cmake_minimum_required(VERSION 3.13) +project(OCCTWrapper) + +add_library(OCCTWrapper SHARED OCCTWrapper.cpp) + +set_target_properties(OCCTWrapper + PROPERTIES + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src" + PREFIX "" +) + +include(GenerateExportHeader) + +generate_export_header(OCCTWrapper) + +find_package(OpenCASCADE 7.6.2 REQUIRED) + +set(OCCT_LIBS + TKXDESTEP + TKSTEP + TKSTEP209 + TKSTEPAttr + TKSTEPBase + TKXCAF + TKXSBase + TKVCAF + TKCAF + TKLCAF + TKCDF + TKV3d + TKService + TKMesh + TKBO + TKPrim + TKHLR + TKShHealing + TKTopAlgo + TKGeomAlgo + TKBRep + TKGeomBase + TKG3d + TKG2d + TKMath + TKernel +) + +target_include_directories(OCCTWrapper PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) +target_include_directories(OCCTWrapper PUBLIC ${OpenCASCADE_INCLUDE_DIR}) +target_link_libraries(OCCTWrapper ${OCCT_LIBS}) + diff --git a/src/occt_wrapper/OCCTWrapper.cpp b/src/occt_wrapper/OCCTWrapper.cpp new file mode 100644 index 0000000000..87f15e7f9c --- /dev/null +++ b/src/occt_wrapper/OCCTWrapper.cpp @@ -0,0 +1,201 @@ +#include "OCCTWrapper.hpp" + +#include "occtwrapper_export.h" + +#include + +#ifdef _WIN32 +#define DIR_SEPARATOR '\\' +#else +#define DIR_SEPARATOR '/' +#endif + +#include "STEPCAFControl_Reader.hxx" +#include "BRepMesh_IncrementalMesh.hxx" +#include "XCAFDoc_DocumentTool.hxx" +#include "XCAFDoc_ShapeTool.hxx" +#include "XCAFApp_Application.hxx" +#include "TopoDS_Builder.hxx" +#include "TopoDS.hxx" +#include "TDataStd_Name.hxx" +#include "BRepBuilderAPI_Transform.hxx" +#include "TopExp_Explorer.hxx" +#include "BRep_Tool.hxx" + +const double STEP_TRANS_CHORD_ERROR = 0.005; +const double STEP_TRANS_ANGLE_RES = 1; + +// const int LOAD_STEP_STAGE_READ_FILE = 0; +// const int LOAD_STEP_STAGE_GET_SOLID = 1; +// const int LOAD_STEP_STAGE_GET_MESH = 2; + +namespace Slic3r { + +struct NamedSolid { + NamedSolid(const TopoDS_Shape& s, + const std::string& n) : solid{s}, name{n} {} + const TopoDS_Shape solid; + const std::string name; +}; + +static void getNamedSolids(const TopLoc_Location& location, const Handle(XCAFDoc_ShapeTool) shapeTool, + const TDF_Label label, std::vector& namedSolids) +{ + TDF_Label referredLabel{label}; + if (shapeTool->IsReference(label)) + shapeTool->GetReferredShape(label, referredLabel); + + std::string name; + Handle(TDataStd_Name) shapeName; + if (referredLabel.FindAttribute(TDataStd_Name::GetID(), shapeName)) + name = TCollection_AsciiString(shapeName->Get()).ToCString(); + + TopLoc_Location localLocation = location * shapeTool->GetLocation(label); + TDF_LabelSequence components; + if (shapeTool->GetComponents(referredLabel, components)) { + for (Standard_Integer compIndex = 1; compIndex <= components.Length(); ++compIndex) { + getNamedSolids(localLocation, shapeTool, components.Value(compIndex), namedSolids); + } + } else { + TopoDS_Shape shape; + shapeTool->GetShape(referredLabel, shape); + TopAbs_ShapeEnum shape_type = shape.ShapeType(); + BRepBuilderAPI_Transform transform(shape, localLocation, Standard_True); + switch (shape_type) { + case TopAbs_COMPOUND: + namedSolids.emplace_back(TopoDS::Compound(transform.Shape()), name); + break; + case TopAbs_COMPSOLID: + namedSolids.emplace_back(TopoDS::CompSolid(transform.Shape()), name); + break; + case TopAbs_SOLID: + namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), name); + break; + default: + break; + } + } +} + +extern "C" OCCTWRAPPER_EXPORT bool load_step_internal(const char *path, OCCTResult* res /*BBS:, ImportStepProgressFn proFn*/) +{ +try { + bool cb_cancel = false; + //if (proFn) { + // proFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel); + // if (cb_cancel) + // return false; + //} + + + std::vector namedSolids; + Handle(TDocStd_Document) document; + Handle(XCAFApp_Application) application = XCAFApp_Application::GetApplication(); + application->NewDocument(path, document); + STEPCAFControl_Reader reader; + reader.SetNameMode(true); + //BBS: Todo, read file is slow which cause the progress_bar no update and gui no response + IFSelect_ReturnStatus stat = reader.ReadFile(path); + if (stat != IFSelect_RetDone || !reader.Transfer(document)) { + application->Close(document); + res->error_str = std::string{"Could not read '"} + path + "'"; + return false; + } + Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(document->Main()); + TDF_LabelSequence topLevelShapes; + shapeTool->GetFreeShapes(topLevelShapes); + + Standard_Integer topShapeLength = topLevelShapes.Length() + 1; + for (Standard_Integer iLabel = 1; iLabel < topShapeLength; ++iLabel) { + //if (proFn) { + // proFn(LOAD_STEP_STAGE_GET_SOLID, iLabel, topShapeLength, cb_cancel); + // if (cb_cancel) { + // shapeTool.reset(nullptr); + // application->Close(document); + // return false; + // } + //} + getNamedSolids(TopLoc_Location{}, shapeTool, topLevelShapes.Value(iLabel), namedSolids); + } + + + + // Now the object name. Set it to filename without suffix. + // This will later be changed if only one volume is loaded. + const char *last_slash = strrchr(path, DIR_SEPARATOR); + std::string obj_name((last_slash == nullptr) ? path : last_slash + 1); + res->object_name = obj_name; + + for (size_t i = 0; i < namedSolids.size(); ++i) { + //BBS:if (proFn) { + // proFn(LOAD_STEP_STAGE_GET_MESH, i, namedSolids.size(), cb_cancel); + // if (cb_cancel) { + // model->delete_object(new_object); + // shapeTool.reset(nullptr); + // application->Close(document); + // return false; + // } + //} + + res->volumes.emplace_back(); + auto& vertices = res->volumes.back().vertices; + auto& indices = res->volumes.back().indices; + + BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true); + + for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) { + const int aNodeOffset = int(vertices.size()); + const TopoDS_Shape& aFace = anExpSF.Current(); + TopLoc_Location aLoc; + Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc); + if (aTriangulation.IsNull()) + continue; + + // First copy vertices (will create duplicates). + gp_Trsf aTrsf = aLoc.Transformation(); + for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) { + gp_Pnt aPnt = aTriangulation->Node(aNodeIter); + aPnt.Transform(aTrsf); + vertices.push_back({float(aPnt.X()), float(aPnt.Y()), float(aPnt.Z())}); + } + // Now the indices. + const TopAbs_Orientation anOrientation = anExpSF.Current().Orientation(); + for (Standard_Integer aTriIter = 1; aTriIter <= aTriangulation->NbTriangles(); ++aTriIter) { + Poly_Triangle aTri = aTriangulation->Triangle(aTriIter); + + Standard_Integer anId[3]; + aTri.Get(anId[0], anId[1], anId[2]); + if (anOrientation == TopAbs_REVERSED) + std::swap(anId[1], anId[2]); + + // Account for the vertices we already have from previous faces. + // anId is 1-based index ! + indices.push_back({anId[0] - 1 + aNodeOffset, + anId[1] - 1 + aNodeOffset, + anId[2] - 1 + aNodeOffset}); + } + } + + res->volumes.back().volume_name = namedSolids[i].name; + + if (vertices.empty()) + res->volumes.pop_back(); + } + + shapeTool.reset(nullptr); + application->Close(document); + + if (res->volumes.empty()) + return false; +} catch (const std::exception& ex) { + res->error_str = ex.what(); + return false; +} catch (...) { + res->error_str = "An exception was thrown in load_step_internal."; + return false; +} + + return true; +} + +}; // namespace Slic3r diff --git a/src/occt_wrapper/OCCTWrapper.hpp b/src/occt_wrapper/OCCTWrapper.hpp new file mode 100644 index 0000000000..e87becb70f --- /dev/null +++ b/src/occt_wrapper/OCCTWrapper.hpp @@ -0,0 +1,27 @@ + +#ifndef occtwrapper_OCCTWrapper_hpp_ +#define occtwrapper_OCCTWrapper_hpp_ + +#include +#include +#include + +namespace Slic3r { + +struct OCCTVolume { + std::string volume_name; + std::vector> vertices; + std::vector> indices; +}; + +struct OCCTResult { + std::string error_str; + std::string object_name; + std::vector volumes; +}; + +using LoadStepFn = bool (*)(const char *path, OCCTResult* occt_result); + +}; // namespace Slic3r + +#endif // occtwrapper_OCCTWrapper_hpp_ diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 5bc770b2c1..6362dc609c 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -482,10 +482,11 @@ 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_STEP */ { "STEP files"sv, { ".stp"sv, ".step"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 } }, - /* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv } }, + /* FT_MODEL */ { "Known files"sv, { ".stl"sv, ".obj"sv, ".3mf"sv, ".amf"sv, ".zip.amf"sv, ".xml"sv, ".step"sv, ".stp"sv } }, /* FT_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } }, /* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } }, diff --git a/src/slic3r/GUI/GUI_App.hpp b/src/slic3r/GUI/GUI_App.hpp index c60dc8d6f0..8775a5f31c 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_STEP, FT_AMF, FT_3MF, FT_GCODE, From 7d3f0b4b32460c599626a20eea4f2d81a5820041 Mon Sep 17 00:00:00 2001 From: "Prusa Research a.s" Date: Wed, 27 Jul 2022 11:22:20 +0200 Subject: [PATCH 02/10] Fixes on MacOS --- src/libslic3r/Format/STEP.cpp | 8 +++++++- src/occt_wrapper/CMakeLists.txt | 2 +- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Format/STEP.cpp b/src/libslic3r/Format/STEP.cpp index 9b4d2c9eab..bd99d073a5 100644 --- a/src/libslic3r/Format/STEP.cpp +++ b/src/libslic3r/Format/STEP.cpp @@ -21,6 +21,8 @@ LoadStepFn get_load_step_fn() { static LoadStepFn load_step_fn = nullptr; + constexpr const char* fn_name = "load_step_internal"; + if (!load_step_fn) { #ifdef _WIN32 HMODULE module = LoadLibraryW(L"OCCTWrapper.dll"); @@ -28,7 +30,6 @@ LoadStepFn get_load_step_fn() throw Slic3r::RuntimeError("Cannot load OCCTWrapper.dll"); try { - const char* fn_name = "load_step_internal"; FARPROC farproc = GetProcAddress(module, fn_name); if (! farproc) { DWORD ec = GetLastError(); @@ -47,7 +48,12 @@ LoadStepFn get_load_step_fn() load_step_fn = reinterpret_cast(dlsym(plugin_ptr, "load_step_internal")); if (!load_step_fn) { dlclose(plugin_ptr); + throw Slic3r::RuntimeError("Cannot load function from OCCTWrapper.so"); } + } else { + throw Slic3r::RuntimeError( + std::string("Cannot load function from OCCTWrapper.dll: ") + + fn_name + "\n\nError code: " + dlerror()); } #endif } diff --git a/src/occt_wrapper/CMakeLists.txt b/src/occt_wrapper/CMakeLists.txt index 2629e1d59e..ccdfb14a8e 100644 --- a/src/occt_wrapper/CMakeLists.txt +++ b/src/occt_wrapper/CMakeLists.txt @@ -1,7 +1,7 @@ cmake_minimum_required(VERSION 3.13) project(OCCTWrapper) -add_library(OCCTWrapper SHARED OCCTWrapper.cpp) +add_library(OCCTWrapper MODULE OCCTWrapper.cpp) set_target_properties(OCCTWrapper PROPERTIES From 1fd4659f0e29ee3ec6a4d33dcae8a30d39185521 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 27 Jul 2022 13:39:38 +0200 Subject: [PATCH 03/10] Use resources dir for occt shared lib --- .gitignore | 1 + CMakeLists.txt | 1 + src/CMakeLists.txt | 5 ++++- src/libslic3r/Format/STEP.cpp | 18 +++++++++++------- src/occt_wrapper/CMakeLists.txt | 4 ++-- 5 files changed, 19 insertions(+), 10 deletions(-) diff --git a/.gitignore b/.gitignore index e3a9db477f..a14504ea11 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,4 @@ local-lib build-linux/* deps/build-linux/* **/.DS_Store +resources/plugins diff --git a/CMakeLists.txt b/CMakeLists.txt index e749fb28c6..449f7539da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -33,6 +33,7 @@ option(SLIC3R_MSVC_COMPILE_PARALLEL "Compile on Visual Studio in parallel" 1) option(SLIC3R_MSVC_PDB "Generate PDB files on MSVC in Release mode" 1) option(SLIC3R_PERL_XS "Compile XS Perl module and enable Perl unit and integration tests" 0) option(SLIC3R_ASAN "Enable ASan on Clang and GCC" 0) +option(SLIC3R_ENABLE_FORMAT_STEP "Enable compilation of STEP file support" 1) # If SLIC3R_FHS is 1 -> SLIC3R_DESKTOP_INTEGRATION is always 0, othrewise variable. CMAKE_DEPENDENT_OPTION(SLIC3R_DESKTOP_INTEGRATION "Allow perfoming desktop integration during runtime" 1 "NOT SLIC3R_FHS" 0) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2ea8eaa043..b75dcaa2fe 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -17,7 +17,10 @@ add_subdirectory(hints) add_subdirectory(qoi) add_subdirectory(libnest2d) add_subdirectory(libslic3r) -add_subdirectory(occt_wrapper) + +if (SLIC3R_ENABLE_FORMAT_STEP) + add_subdirectory(occt_wrapper) +endif () if (SLIC3R_GUI) add_subdirectory(imgui) diff --git a/src/libslic3r/Format/STEP.cpp b/src/libslic3r/Format/STEP.cpp index bd99d073a5..7730b835a0 100644 --- a/src/libslic3r/Format/STEP.cpp +++ b/src/libslic3r/Format/STEP.cpp @@ -3,6 +3,9 @@ #include "libslic3r/Model.hpp" #include "libslic3r/TriangleMesh.hpp" +#include "libslic3r/Utils.hpp" + +#include #include #include @@ -24,8 +27,10 @@ LoadStepFn get_load_step_fn() constexpr const char* fn_name = "load_step_internal"; if (!load_step_fn) { + auto libpath = boost::filesystem::path(resources_dir()) / "plugins"; #ifdef _WIN32 - HMODULE module = LoadLibraryW(L"OCCTWrapper.dll"); + libpath /= "OCCTWrapper.dll"; + HMODULE module = LoadLibraryW(libpath.wstring().c_str()); if (module == NULL) throw Slic3r::RuntimeError("Cannot load OCCTWrapper.dll"); @@ -42,18 +47,17 @@ LoadStepFn get_load_step_fn() throw; } #else - void *plugin_ptr = dlopen("OCCTWrapper.so", RTLD_NOW | RTLD_GLOBAL); + libpath /= "OCCTWrapper.so"; + void *plugin_ptr = dlopen(libpath.c_str(), RTLD_NOW | RTLD_GLOBAL); if (plugin_ptr) { - load_step_fn = reinterpret_cast(dlsym(plugin_ptr, "load_step_internal")); + load_step_fn = reinterpret_cast(dlsym(plugin_ptr, fn_name)); if (!load_step_fn) { dlclose(plugin_ptr); - throw Slic3r::RuntimeError("Cannot load function from OCCTWrapper.so"); + BOOST_LOG_TRIVIAL(error) << dlerror(); } } else { - throw Slic3r::RuntimeError( - std::string("Cannot load function from OCCTWrapper.dll: ") + - fn_name + "\n\nError code: " + dlerror()); + BOOST_LOG_TRIVIAL(error) << dlerror(); } #endif } diff --git a/src/occt_wrapper/CMakeLists.txt b/src/occt_wrapper/CMakeLists.txt index ccdfb14a8e..4e78b83ebf 100644 --- a/src/occt_wrapper/CMakeLists.txt +++ b/src/occt_wrapper/CMakeLists.txt @@ -5,8 +5,8 @@ add_library(OCCTWrapper MODULE OCCTWrapper.cpp) set_target_properties(OCCTWrapper PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src" - RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src" + LIBRARY_OUTPUT_DIRECTORY "${SLIC3R_RESOURCES_DIR}/plugins" + RUNTIME_OUTPUT_DIRECTORY "${SLIC3R_RESOURCES_DIR}/plugins" PREFIX "" ) From 3f8979c95fd5ad7cf2371c8d6f7f0a874fda64ed Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 27 Jul 2022 14:09:34 +0200 Subject: [PATCH 04/10] Use prusa-slicer executable dir to search for occt shared lib --- .gitignore | 1 - src/libslic3r/Format/STEP.cpp | 3 ++- src/occt_wrapper/CMakeLists.txt | 6 ++++-- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index a14504ea11..e3a9db477f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,4 +18,3 @@ local-lib build-linux/* deps/build-linux/* **/.DS_Store -resources/plugins diff --git a/src/libslic3r/Format/STEP.cpp b/src/libslic3r/Format/STEP.cpp index 7730b835a0..b816619223 100644 --- a/src/libslic3r/Format/STEP.cpp +++ b/src/libslic3r/Format/STEP.cpp @@ -6,6 +6,7 @@ #include "libslic3r/Utils.hpp" #include +#include #include #include @@ -27,7 +28,7 @@ LoadStepFn get_load_step_fn() constexpr const char* fn_name = "load_step_internal"; if (!load_step_fn) { - auto libpath = boost::filesystem::path(resources_dir()) / "plugins"; + auto libpath = boost::dll::program_location().parent_path(); #ifdef _WIN32 libpath /= "OCCTWrapper.dll"; HMODULE module = LoadLibraryW(libpath.wstring().c_str()); diff --git a/src/occt_wrapper/CMakeLists.txt b/src/occt_wrapper/CMakeLists.txt index 4e78b83ebf..84f91f2f50 100644 --- a/src/occt_wrapper/CMakeLists.txt +++ b/src/occt_wrapper/CMakeLists.txt @@ -5,8 +5,8 @@ add_library(OCCTWrapper MODULE OCCTWrapper.cpp) set_target_properties(OCCTWrapper PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${SLIC3R_RESOURCES_DIR}/plugins" - RUNTIME_OUTPUT_DIRECTORY "${SLIC3R_RESOURCES_DIR}/plugins" + LIBRARY_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src" + RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/src" PREFIX "" ) @@ -49,3 +49,5 @@ target_include_directories(OCCTWrapper PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(OCCTWrapper PUBLIC ${OpenCASCADE_INCLUDE_DIR}) target_link_libraries(OCCTWrapper ${OCCT_LIBS}) +install(TARGETS OCCTWrapper DESTINATION "${CMAKE_INSTALL_BINDIR}") + From 5a4f12981200e438ccbd368da66af226856dd5cc Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 27 Jul 2022 15:40:30 +0200 Subject: [PATCH 05/10] Make occtwrapper buildable stand-alone --- src/occt_wrapper/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/occt_wrapper/CMakeLists.txt b/src/occt_wrapper/CMakeLists.txt index 84f91f2f50..9f4989b72f 100644 --- a/src/occt_wrapper/CMakeLists.txt +++ b/src/occt_wrapper/CMakeLists.txt @@ -49,5 +49,7 @@ target_include_directories(OCCTWrapper PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) target_include_directories(OCCTWrapper PUBLIC ${OpenCASCADE_INCLUDE_DIR}) target_link_libraries(OCCTWrapper ${OCCT_LIBS}) +include(GNUInstallDirs) + install(TARGETS OCCTWrapper DESTINATION "${CMAKE_INSTALL_BINDIR}") From 2486a2363c761eed18b1d0808916b34e7b8c6b8e Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Fri, 29 Jul 2022 13:45:57 +0200 Subject: [PATCH 06/10] Try to reduce number of build threads only for OCCT --- deps/CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index 0fe4e41c7e..2a83863cfe 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -75,6 +75,9 @@ function(prusaslicer_add_cmake_project projectname) if (MSVC) set(_gen CMAKE_GENERATOR "${DEP_MSVC_GEN}" CMAKE_GENERATOR_PLATFORM "${DEP_PLATFORM}") set(_build_j "/m") + if (${projectname} STREQUAL "OCCT") + set(_build_j "/m:1") + endif () endif () ExternalProject_Add( From aff337067b7114eacfb325640859593e84fe5a53 Mon Sep 17 00:00:00 2001 From: tamasmeszaros Date: Wed, 10 Aug 2022 15:31:17 +0200 Subject: [PATCH 07/10] Switch to statically linked OCCTWrapper on Apple. Due to dmg notarization issues --- src/libslic3r/CMakeLists.txt | 5 +++++ src/libslic3r/Format/STEP.cpp | 6 ++++++ src/occt_wrapper/CMakeLists.txt | 7 ++++++- 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 52ea9be081..5998dd6eb7 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -414,6 +414,11 @@ target_link_libraries(libslic3r qoi ) +if (APPLE) + # TODO: we need to fix notarization with the separate shared library + target_link_libraries(libslic3r OCCTWrapper) +endif () + if (TARGET OpenVDB::openvdb) target_link_libraries(libslic3r OpenVDB::openvdb) endif() diff --git a/src/libslic3r/Format/STEP.cpp b/src/libslic3r/Format/STEP.cpp index b816619223..8ac3e8adb2 100644 --- a/src/libslic3r/Format/STEP.cpp +++ b/src/libslic3r/Format/STEP.cpp @@ -21,6 +21,10 @@ namespace Slic3r { +#if __APPLE__ +extern "C" bool load_step_internal(const char *path, OCCTResult* res); +#endif + LoadStepFn get_load_step_fn() { static LoadStepFn load_step_fn = nullptr; @@ -47,6 +51,8 @@ LoadStepFn get_load_step_fn() FreeLibrary(module); throw; } +#elif __APPLE__ + load_step_fn = &load_step_internal; #else libpath /= "OCCTWrapper.so"; void *plugin_ptr = dlopen(libpath.c_str(), RTLD_NOW | RTLD_GLOBAL); diff --git a/src/occt_wrapper/CMakeLists.txt b/src/occt_wrapper/CMakeLists.txt index 9f4989b72f..ed75531a96 100644 --- a/src/occt_wrapper/CMakeLists.txt +++ b/src/occt_wrapper/CMakeLists.txt @@ -1,7 +1,12 @@ cmake_minimum_required(VERSION 3.13) project(OCCTWrapper) -add_library(OCCTWrapper MODULE OCCTWrapper.cpp) +if (APPLE) + # TODO: we need to fix notarization with the separate shared library + add_library(OCCTWrapper STATIC OCCTWrapper.cpp) +else () + add_library(OCCTWrapper MODULE OCCTWrapper.cpp) +endif () set_target_properties(OCCTWrapper PROPERTIES From 620b89d1c69fb3ebdff43a5d49fe0c0ab7904ab2 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 11 Aug 2022 10:31:19 +0200 Subject: [PATCH 08/10] Add STEP into menu label, enable drag and drop --- src/libslic3r/Model.cpp | 2 +- src/slic3r/GUI/KBShortcutsDialog.cpp | 2 +- src/slic3r/GUI/MainFrame.cpp | 2 +- src/slic3r/GUI/Plater.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 77011800dc..2c5200886a 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -123,7 +123,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c //FIXME options & LoadAttribute::CheckVersion ? result = load_3mf(input_file.c_str(), *config, *config_substitutions, &model, false); else - throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension."); + throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml), .prusa or .step/.stp extension."); if (! result) throw Slic3r::RuntimeError("Loading of a model file failed."); diff --git a/src/slic3r/GUI/KBShortcutsDialog.cpp b/src/slic3r/GUI/KBShortcutsDialog.cpp index c330f7cc92..3a4e757294 100644 --- a/src/slic3r/GUI/KBShortcutsDialog.cpp +++ b/src/slic3r/GUI/KBShortcutsDialog.cpp @@ -80,7 +80,7 @@ void KBShortcutsDialog::fill_shortcuts() { ctrl + alt + "S", L("Save project as (3mf)") }, { ctrl + "R", L("(Re)slice") }, // File>Import - { ctrl + "I", L("Import STL/OBJ/AMF/3MF without config, keep plater") }, + { ctrl + "I", L("Import STL/OBJ/AMF/3MF/STEP without config, keep plater") }, { ctrl + "L", L("Import Config from ini/amf/3mf/gcode") }, { ctrl + alt + "L", L("Load Config from ini/amf/3mf/gcode and merge") }, // File>Export diff --git a/src/slic3r/GUI/MainFrame.cpp b/src/slic3r/GUI/MainFrame.cpp index 478766df6a..adb657d4f6 100644 --- a/src/slic3r/GUI/MainFrame.cpp +++ b/src/slic3r/GUI/MainFrame.cpp @@ -1205,7 +1205,7 @@ void MainFrame::init_menubar_as_editor() fileMenu->AppendSeparator(); wxMenu* import_menu = new wxMenu(); - append_menu_item(import_menu, wxID_ANY, _L("Import STL/OBJ/AM&F/3MF") + dots + "\tCtrl+I", _L("Load a model"), + append_menu_item(import_menu, wxID_ANY, _L("Import STL/OBJ/AM&F/3MF/STEP") + dots + "\tCtrl+I", _L("Load a model"), [this](wxCommandEvent&) { if (m_plater) m_plater->add_model(); }, "import_plater", nullptr, [this](){return m_plater != nullptr; }, this); diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 81c57bae0b..20783f0fc5 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -5340,7 +5340,7 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect) bool Plater::load_files(const wxArrayString& filenames) { - const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa)", std::regex::icase); + const std::regex pattern_drop(".*[.](stl|obj|amf|3mf|prusa|step|stp)", std::regex::icase); const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase); std::vector paths; From 41f5bd006a94c944685e87d68c1634ff76d45ed5 Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 11 Aug 2022 11:32:06 +0200 Subject: [PATCH 09/10] STEP: Improved error reporting on Linux --- src/libslic3r/Format/STEP.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Format/STEP.cpp b/src/libslic3r/Format/STEP.cpp index 8ac3e8adb2..f63d56f1bb 100644 --- a/src/libslic3r/Format/STEP.cpp +++ b/src/libslic3r/Format/STEP.cpp @@ -7,6 +7,7 @@ #include #include +#include #include #include @@ -61,10 +62,11 @@ LoadStepFn get_load_step_fn() load_step_fn = reinterpret_cast(dlsym(plugin_ptr, fn_name)); if (!load_step_fn) { dlclose(plugin_ptr); - BOOST_LOG_TRIVIAL(error) << dlerror(); + throw Slic3r::RuntimeError(std::string("Cannot load function from OCCTWrapper.dll: ") + fn_name + + "\n\n" + dlerror()); } } else { - BOOST_LOG_TRIVIAL(error) << dlerror(); + throw Slic3r::RuntimeError(std::string("Cannot load OCCTWrapper.dll:\n\n") + dlerror()); } #endif } From f548f8540532f66d41fc003657d1f207ea4174ce Mon Sep 17 00:00:00 2001 From: Lukas Matena Date: Thu, 11 Aug 2022 14:45:12 +0200 Subject: [PATCH 10/10] STEP: Show STEP in model import file dialog title --- src/slic3r/GUI/GUI_App.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index 6362dc609c..c25f06ff61 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -1868,7 +1868,7 @@ void GUI_App::import_model(wxWindow *parent, wxArrayString& input_files) const { input_files.Clear(); wxFileDialog dialog(parent ? parent : GetTopWindow(), - _L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA):"), + _L("Choose one or more files (STL/OBJ/AMF/3MF/PRUSA/STEP):"), from_u8(app_config->get_last_dir()), "", file_wildcards(FT_MODEL), wxFD_OPEN | wxFD_MULTIPLE | wxFD_FILE_MUST_EXIST);