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
This commit is contained in:
tamasmeszaros 2022-07-26 14:48:26 +02:00 committed by Lukas Matena
parent dc9e35d8ea
commit 88ba89dbbc
13 changed files with 443 additions and 4 deletions

2
deps/CMakeLists.txt vendored
View File

@ -189,6 +189,7 @@ endif ()
include(JPEG/JPEG.cmake) include(JPEG/JPEG.cmake)
include(TIFF/TIFF.cmake) include(TIFF/TIFF.cmake)
include(wxWidgets/wxWidgets.cmake) include(wxWidgets/wxWidgets.cmake)
include(OCCT/OCCT.cmake)
set(_dep_list set(_dep_list
dep_Boost dep_Boost
@ -200,6 +201,7 @@ set(_dep_list
dep_OpenVDB dep_OpenVDB
dep_OpenCSG dep_OpenCSG
dep_CGAL dep_CGAL
dep_OCCT
${PNG_PKG} ${PNG_PKG}
${ZLIB_PKG} ${ZLIB_PKG}
${EXPAT_PKG} ${EXPAT_PKG}

22
deps/OCCT/OCCT.cmake vendored Normal file
View File

@ -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
)

View File

@ -15,11 +15,9 @@ add_subdirectory(semver)
add_subdirectory(libigl) add_subdirectory(libigl)
add_subdirectory(hints) add_subdirectory(hints)
add_subdirectory(qoi) add_subdirectory(qoi)
# Adding libnest2d project for bin packing...
add_subdirectory(libnest2d) add_subdirectory(libnest2d)
add_subdirectory(libslic3r) add_subdirectory(libslic3r)
add_subdirectory(occt_wrapper)
if (SLIC3R_GUI) if (SLIC3R_GUI)
add_subdirectory(imgui) add_subdirectory(imgui)
@ -127,6 +125,7 @@ if (NOT WIN32 AND NOT APPLE)
endif () endif ()
target_link_libraries(PrusaSlicer libslic3r cereal) target_link_libraries(PrusaSlicer libslic3r cereal)
if (APPLE) if (APPLE)
# add_compile_options(-stdlib=libc++) # add_compile_options(-stdlib=libc++)
# add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE) # add_definitions(-DBOOST_THREAD_DONT_USE_CHRONO -DBOOST_NO_CXX11_RVALUE_REFERENCES -DBOOST_THREAD_USES_MOVE)

View File

@ -94,6 +94,8 @@ add_library(libslic3r STATIC
Format/STL.hpp Format/STL.hpp
Format/SL1.hpp Format/SL1.hpp
Format/SL1.cpp Format/SL1.cpp
Format/STEP.hpp
Format/STEP.cpp
GCode/ThumbnailData.cpp GCode/ThumbnailData.cpp
GCode/ThumbnailData.hpp GCode/ThumbnailData.hpp
GCode/Thumbnails.cpp GCode/Thumbnails.cpp

View File

@ -0,0 +1,110 @@
#include "STEP.hpp"
#include "occt_wrapper/OCCTWrapper.hpp"
#include "libslic3r/Model.hpp"
#include "libslic3r/TriangleMesh.hpp"
#include <string>
#include <functional>
#ifdef _WIN32
#include<windows.h>
#else
#include<occt_wrapper/OCCTWrapper.hpp>
#include <dlfcn.h>
#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<LoadStepFn>(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<LoadStepFn>(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; i<occt_object.volumes.size(); ++i) {
indexed_triangle_set its;
for (size_t j=0; j<occt_object.volumes[i].vertices.size(); ++j)
its.vertices.emplace_back(Vec3f(occt_object.volumes[i].vertices[j][0],
occt_object.volumes[i].vertices[j][1],
occt_object.volumes[i].vertices[j][2]));
for (size_t j=0; j<occt_object.volumes[i].indices.size(); ++j)
its.indices.emplace_back(Vec3i(occt_object.volumes[i].indices[j][0],
occt_object.volumes[i].indices[j][1],
occt_object.volumes[i].indices[j][2]));
its_merge_vertices(its, true);
TriangleMesh triangle_mesh(std::move(its));
ModelVolume* new_volume = new_object->add_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

View File

@ -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<void(int load_stage, int current, int total, bool& cancel)> 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_ */

View File

@ -13,6 +13,7 @@
#include "Format/OBJ.hpp" #include "Format/OBJ.hpp"
#include "Format/STL.hpp" #include "Format/STL.hpp"
#include "Format/3mf.hpp" #include "Format/3mf.hpp"
#include "Format/STEP.hpp"
#include <float.h> #include <float.h>
@ -114,6 +115,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c
result = load_stl(input_file.c_str(), &model); result = load_stl(input_file.c_str(), &model);
else if (boost::algorithm::iends_with(input_file, ".obj")) else if (boost::algorithm::iends_with(input_file, ".obj"))
result = load_obj(input_file.c_str(), &model); 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")) 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); result = load_amf(input_file.c_str(), config, config_substitutions, &model, options & LoadAttribute::CheckVersion);
else if (boost::algorithm::iends_with(input_file, ".3mf")) else if (boost::algorithm::iends_with(input_file, ".3mf"))

View File

@ -1370,4 +1370,5 @@ bool its_write_stl_binary(const char *file, const char *label, const std::vector
return true; return true;
} }
} // namespace Slic3r } // namespace Slic3r

View File

@ -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})

View File

@ -0,0 +1,201 @@
#include "OCCTWrapper.hpp"
#include "occtwrapper_export.h"
#include <cassert>
#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<NamedSolid>& 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<NamedSolid> 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

View File

@ -0,0 +1,27 @@
#ifndef occtwrapper_OCCTWrapper_hpp_
#define occtwrapper_OCCTWrapper_hpp_
#include <array>
#include <string>
#include <vector>
namespace Slic3r {
struct OCCTVolume {
std::string volume_name;
std::vector<std::array<float, 3>> vertices;
std::vector<std::array<int, 3>> indices;
};
struct OCCTResult {
std::string error_str;
std::string object_name;
std::vector<OCCTVolume> volumes;
};
using LoadStepFn = bool (*)(const char *path, OCCTResult* occt_result);
}; // namespace Slic3r
#endif // occtwrapper_OCCTWrapper_hpp_

View File

@ -482,10 +482,11 @@ struct FileWildcards {
static const FileWildcards file_wildcards_by_type[FT_SIZE] = { static const FileWildcards file_wildcards_by_type[FT_SIZE] = {
/* FT_STL */ { "STL files"sv, { ".stl"sv } }, /* FT_STL */ { "STL files"sv, { ".stl"sv } },
/* FT_OBJ */ { "OBJ files"sv, { ".obj"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_AMF */ { "AMF files"sv, { ".amf"sv, ".zip.amf"sv, ".xml"sv } },
/* FT_3MF */ { "3MF files"sv, { ".3mf"sv } }, /* FT_3MF */ { "3MF files"sv, { ".3mf"sv } },
/* FT_GCODE */ { "G-code files"sv, { ".gcode"sv, ".gco"sv, ".g"sv, ".ngc"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_PROJECT */ { "Project files"sv, { ".3mf"sv, ".amf"sv, ".zip.amf"sv } },
/* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } }, /* FT_GALLERY */ { "Known files"sv, { ".stl"sv, ".obj"sv } },

View File

@ -53,6 +53,7 @@ enum FileType
{ {
FT_STL, FT_STL,
FT_OBJ, FT_OBJ,
FT_STEP,
FT_AMF, FT_AMF,
FT_3MF, FT_3MF,
FT_GCODE, FT_GCODE,