Add read support for Google's Draco (.drc) format.

This commit is contained in:
Maeyanie
2025-09-09 01:46:32 -04:00
parent 0eef794824
commit fa22a3512b
9 changed files with 189 additions and 3 deletions

View File

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

3
deps/CMakeLists.txt vendored
View File

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

4
deps/Draco/Draco.cmake vendored Normal file
View File

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

View File

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

View File

@@ -0,0 +1,126 @@
#include <string>
#include <utility>
#include <cstring>
#include <draco/compression/decode.h>
#include <draco/io/mesh_io.h>
#include <draco/mesh/mesh.h>
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<float>(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

View File

@@ -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_ */

View File

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

View File

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

View File

@@ -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<fs::path> normal_paths;