Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/prusa3d/PrusaSlicer.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authortamasmeszaros <meszaros.q@gmail.com>2022-07-26 15:48:26 +0300
committerLukas Matena <lukasmatena@seznam.cz>2022-08-11 17:49:13 +0300
commit88ba89dbbc8ccf62ae7a9a27a68da4ab9bc585ea (patch)
tree5cf537d106d6fa72fe242600f94bfa01eb4cd526
parentdc9e35d8eacaea82b3d6cb0f2e16a57c64ad8398 (diff)
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
-rw-r--r--deps/CMakeLists.txt2
-rw-r--r--deps/OCCT/OCCT.cmake22
-rw-r--r--src/CMakeLists.txt5
-rw-r--r--src/libslic3r/CMakeLists.txt2
-rw-r--r--src/libslic3r/Format/STEP.cpp110
-rw-r--r--src/libslic3r/Format/STEP.hpp19
-rw-r--r--src/libslic3r/Model.cpp3
-rw-r--r--src/libslic3r/TriangleMesh.cpp1
-rw-r--r--src/occt_wrapper/CMakeLists.txt51
-rw-r--r--src/occt_wrapper/OCCTWrapper.cpp201
-rw-r--r--src/occt_wrapper/OCCTWrapper.hpp27
-rw-r--r--src/slic3r/GUI/GUI_App.cpp3
-rw-r--r--src/slic3r/GUI/GUI_App.hpp1
13 files changed, 443 insertions, 4 deletions
diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt
index 56a8fa429..0fe4e41c7 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 000000000..9981ac15e
--- /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 cfee06342..2ea8eaa04 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 41ad68db5..52ea9be08 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 000000000..9b4d2c9ea
--- /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 <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
diff --git a/src/libslic3r/Format/STEP.hpp b/src/libslic3r/Format/STEP.hpp
new file mode 100644
index 000000000..8fbc604d8
--- /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<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_ */
diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp
index efc66f478..77011800d 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 <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);
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 93a09a0d9..807ebcc9a 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 000000000..2629e1d59
--- /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 000000000..87f15e7f9
--- /dev/null
+++ b/src/occt_wrapper/OCCTWrapper.cpp
@@ -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
diff --git a/src/occt_wrapper/OCCTWrapper.hpp b/src/occt_wrapper/OCCTWrapper.hpp
new file mode 100644
index 000000000..e87becb70
--- /dev/null
+++ b/src/occt_wrapper/OCCTWrapper.hpp
@@ -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_
diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp
index 5bc770b2c..6362dc609 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 c60dc8d6f..8775a5f31 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,