diff --git a/cmake/modules/FindDraco.cmake b/cmake/modules/FindDraco.cmake new file mode 100644 index 0000000000..3b41890a4a --- /dev/null +++ b/cmake/modules/FindDraco.cmake @@ -0,0 +1,26 @@ +set(_q "") +if(${CMAKE_FIND_PACKAGE_NAME}_FIND_QUIETLY) + set(_q QUIET) + set(_quietly TRUE) +endif() +find_package(${CMAKE_FIND_PACKAGE_NAME} ${${CMAKE_FIND_PACKAGE_NAME}_FIND_VERSION} CONFIG ${_q}) + +if (NOT ${CMAKE_FIND_PACKAGE_NAME}_FOUND) + include(CheckIncludeFileCXX) + add_library(draco INTERFACE) + target_include_directories(draco INTERFACE include) + + if (_quietly) + set(CMAKE_REQUIRED_QUIET ON) + endif() + CHECK_INCLUDE_FILE_CXX("draco/draco_features.h" HAVE_DRACO_H) + + if (NOT HAVE_DRACO_H) + if (${CMAKE_FIND_PACKAGE_NAME}_FIND_REQUIRED) + message(FATAL_ERROR "Draco library not found. Please install the dependency.") + elseif(NOT _quietly) + message(WARNING "Draco library not found.") + endif() + endif () +endif() + diff --git a/deps/CMakeLists.txt b/deps/CMakeLists.txt index df099e7db4..4a6ab88a71 100644 --- a/deps/CMakeLists.txt +++ b/deps/CMakeLists.txt @@ -294,6 +294,8 @@ include(CGAL/CGAL.cmake) include(NLopt/NLopt.cmake) include(libnoise/libnoise.cmake) +include(Draco/Draco.cmake) + # I *think* 1.1 is used for *just* md5 hashing? # 3.1 has everything in the right place, but the md5 funcs used are deprecated @@ -357,6 +359,7 @@ set(_dep_list ${CURL_PKG} ${WXWIDGETS_PKG} dep_Cereal + dep_Draco dep_NLopt dep_OpenVDB dep_OpenCSG diff --git a/deps/Draco/Draco.cmake b/deps/Draco/Draco.cmake new file mode 100644 index 0000000000..45e349cb19 --- /dev/null +++ b/deps/Draco/Draco.cmake @@ -0,0 +1,4 @@ +orcaslicer_add_cmake_project(Draco + URL https://github.com/google/draco/archive/refs/tags/1.5.7.zip + URL_HASH SHA256=27b72ba2d5ff3d0a9814ad40d4cb88f8dc89a35491c0866d952473f8f9416b77 +) \ No newline at end of file diff --git a/src/libslic3r/CMakeLists.txt b/src/libslic3r/CMakeLists.txt index 52edbda0ed..e64f0d3e1d 100644 --- a/src/libslic3r/CMakeLists.txt +++ b/src/libslic3r/CMakeLists.txt @@ -133,6 +133,8 @@ set(lisbslic3r_sources Format/bbs_3mf.hpp Format/AMF.cpp Format/AMF.hpp + Format/DRC.cpp + Format/DRC.hpp Format/OBJ.cpp Format/OBJ.hpp Format/objparser.cpp @@ -522,6 +524,7 @@ find_package(OpenCASCADE REQUIRED) target_include_directories(libslic3r PUBLIC ${OpenCASCADE_INCLUDE_DIR}) find_package(JPEG REQUIRED) +find_package(draco REQUIRED) set(OCCT_LIBS TKXDESTEP @@ -578,6 +581,7 @@ target_link_libraries(libslic3r qoi opencv_world noise::noise + draco::draco ) if(NOT WIN32) diff --git a/src/libslic3r/Format/DRC.cpp b/src/libslic3r/Format/DRC.cpp new file mode 100644 index 0000000000..fd6d269965 --- /dev/null +++ b/src/libslic3r/Format/DRC.cpp @@ -0,0 +1,126 @@ +#include +#include +#include + +#include +#include +#include +using namespace draco; + +#include "libslic3r/Model.hpp" +#include "libslic3r/TriangleMesh.hpp" +#include "DRC.hpp" + +#ifdef _WIN32 +#define DIR_SEPARATOR '\\' +#else +#define DIR_SEPARATOR '/' +#endif + +namespace Slic3r { + +bool load_drc(const char *path, TriangleMesh *meshptr) +{ + struct stat st; + if (stat(path, &st)) { + return false; + } + + FILE *fp = fopen(path, "rb"); + if (fp == nullptr) { + return false; + } + + char *data = new char[st.st_size]; + if (fread(data, 1, st.st_size, fp) != st.st_size) { + fclose(fp); + delete[] data; + return false; + } + fclose(fp); + + DecoderBuffer buffer; + buffer.Init(data, st.st_size); + + auto geotype = Decoder::GetEncodedGeometryType(&buffer); + if ((!geotype.ok()) || geotype.value() != TRIANGULAR_MESH) { + delete[] data; + return false; + } + + Decoder decoder; + Mesh dracoMesh; + Status status = decoder.DecodeBufferToGeometry(&buffer, &dracoMesh); + delete[] data; + if (!status.ok()) { + return false; + } + + indexed_triangle_set its; + + const PointAttribute *const positions = dracoMesh.GetNamedAttribute(GeometryAttribute::POSITION); + size_t num_vertices = positions->size(); + its.vertices.reserve(num_vertices); + for (AttributeValueIndex i(0); i < num_vertices; ++ i) { + float pos[3]; + positions->ConvertValue(i, 3, pos); + its.vertices.emplace_back(pos[0], pos[1], pos[2]); + } + + size_t num_faces = dracoMesh.num_faces(); + its.indices.reserve(num_faces); + for (FaceIndex i(0); i < num_faces; ++ i) { + Mesh::Face face = dracoMesh.face(i); + + its.indices.emplace_back( + positions->mapped_index(face[0]).value(), + positions->mapped_index(face[1]).value(), + positions->mapped_index(face[2]).value() + ); + } + + *meshptr = TriangleMesh(std::move(its)); + if (meshptr->volume() < 0) + meshptr->flip_triangles(); + return true; +} + +bool load_drc(const char *path, Model *model, const char *object_name_in) +{ + TriangleMesh mesh; + + bool ret = load_drc(path, &mesh); + + if (ret) { + std::string object_name; + if (object_name_in == nullptr) { + const char *last_slash = strrchr(path, DIR_SEPARATOR); + object_name.assign((last_slash == nullptr) ? path : last_slash + 1); + } else + object_name.assign(object_name_in); + + model->add_object(object_name.c_str(), path, std::move(mesh)); + } + + return ret; +} + +bool store_drc(const char *path, TriangleMesh *mesh, bool binary) +{ + // TODO + return false; +} + +bool store_drc(const char *path, ModelObject *model_object, bool binary) +{ + TriangleMesh mesh = model_object->mesh(); + return store_drc(path, &mesh, binary); +} + +bool store_drc(const char *path, Model *model, bool binary) +{ + TriangleMesh mesh = model->mesh(); + return store_drc(path, &mesh, binary); +} + +}; // namespace Slic3r diff --git a/src/libslic3r/Format/DRC.hpp b/src/libslic3r/Format/DRC.hpp new file mode 100644 index 0000000000..9178f12526 --- /dev/null +++ b/src/libslic3r/Format/DRC.hpp @@ -0,0 +1,20 @@ +#ifndef slic3r_Format_DRC_hpp_ +#define slic3r_Format_DRC_hpp_ + +namespace Slic3r { + +class TriangleMesh; +class ModelObject; +class Model; + +// Load a Draco file into a provided model. +extern bool load_drc(const char *path, TriangleMesh *meshptr); +extern bool load_drc(const char *path, Model *model, const char *object_name = nullptr); + +extern bool store_drc(const char *path, TriangleMesh *mesh, bool binary); +extern bool store_drc(const char *path, ModelObject *model_object, bool binary); +extern bool store_drc(const char *path, Model *model, bool binary); + +}; // namespace Slic3r + +#endif /* slic3r_Format_DRC_hpp_ */ diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 4edec076af..bf9dabfb8b 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -12,6 +12,7 @@ #include "Format/AMF.hpp" #include "Format/svg.hpp" +#include "Format/DRC.hpp" // BBS #include "FaceDetector.hpp" @@ -300,6 +301,8 @@ Model Model::read_from_file(const std::string& result = load_svg(input_file.c_str(), &model, message); //BBS: remove the old .amf.xml files //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, ".drc")) + result = load_drc(input_file.c_str(), &model); else if (boost::algorithm::iends_with(input_file, ".amf")) //BBS: is_xxx is used for is_inches when load amf result = load_amf(input_file.c_str(), config, config_substitutions, &model, is_xxx); diff --git a/src/slic3r/GUI/GUI_App.cpp b/src/slic3r/GUI/GUI_App.cpp index f27d9dbd39..3c1a03b45e 100644 --- a/src/slic3r/GUI/GUI_App.cpp +++ b/src/slic3r/GUI/GUI_App.cpp @@ -516,10 +516,10 @@ static const FileWildcards file_wildcards_by_type[FT_SIZE] = { /* FT_GCODE */ { "G-code files"sv, { ".gcode"sv} }, #ifdef __APPLE__ /* FT_MODEL */ - {"Supported files"sv, {".3mf"sv, ".stl"sv, ".oltp"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv, ".usd"sv, ".usda"sv, ".usdc"sv, ".usdz"sv, ".abc"sv, ".ply"sv}}, + {"Supported files"sv, {".3mf"sv, ".stl"sv, ".oltp"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv, ".usd"sv, ".usda"sv, ".usdc"sv, ".usdz"sv, ".abc"sv, ".ply"sv, ".drc"sv}}, #else /* FT_MODEL */ - {"Supported files"sv, {".3mf"sv, ".stl"sv, ".oltp"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv}}, + {"Supported files"sv, {".3mf"sv, ".stl"sv, ".oltp"sv, ".stp"sv, ".step"sv, ".svg"sv, ".amf"sv, ".obj"sv, ".drc"sv}}, #endif /* FT_ZIP */ { "ZIP files"sv, { ".zip"sv } }, /* FT_PROJECT */ { "Project files"sv, { ".3mf"sv} }, diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5a9a7ecf65..efdac5e4d4 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -10946,7 +10946,7 @@ void ProjectDropDialog::on_dpi_changed(const wxRect& suggested_rect) //BBS: remove GCodeViewer as seperate APP logic bool Plater::load_files(const wxArrayString& filenames) { - const std::regex pattern_drop(".*[.](stp|step|stl|oltp|obj|amf|3mf|svg|zip)", std::regex::icase); + const std::regex pattern_drop(".*[.](stp|step|stl|oltp|obj|amf|3mf|svg|zip|drc)", std::regex::icase); const std::regex pattern_gcode_drop(".*[.](gcode|g)", std::regex::icase); std::vector normal_paths;