mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-09-09 18:27:00 +00:00
Add support for Draco (.drc) format (#10681)
* Add read support for Google's Draco (.drc) format. * Fix build on Linux * Use boost instead of fstat. * Switch to boost memory-mapped file to save RAM and potentially improve performance. * Trim trailing whitespace. * Initial Draco write support. Currently always exports with 16-bit precision and speed 0 (best compression). The back-end function does have arguments to specify them, it's just not hooked into the GUI. * Add Draco to the About dialogue. * Fix Linux compile (hopefully) * Add an option to associate DRC files on Windows. * Implement a Preferences option to set Draco position quantization bits * Update src/slic3r/GUI/Preferences.cpp Co-authored-by: Ian Bassi <ian.bassi@outlook.com> * Some slight changes to ianalexis's suggestion. * Implement a create_item_spinctrl() function for numeric inputs, and use that instead of create_item_input(). * Move "bits" to inside the spinctrl box. * Refactor following yw4z's feedback * Update src/slic3r/GUI/Preferences.cpp Co-authored-by: Ian Bassi <ian.bassi@outlook.com> * Change to 0 bits as the default setting for Draco export precision. * Change to a lossy checkbox and a bits field with a range of 8-30. * Proper SpinInput code from yw4z * Revert "Proper SpinInput code from yw4z" This reverts commit7e9c85f31a. * Revert "Change to a lossy checkbox and a bits field with a range of 8-30." This reverts commitd642c9bcc0. * Redo preferences based on SoftFever's feedback * Refactor to minimize code duplication * Fix padding * Improve Draco export quality level tooltip clarity Clarify that 0 means lossless compression (not uncompressed), document the valid lossy range (8-30), and better explain the tradeoff between file size and geometric detail. --------- Co-authored-by: SoftFever <softfeverever@gmail.com> Co-authored-by: Noisyfox <timemanager.rick@gmail.com> Co-authored-by: Ian Bassi <ian.bassi@outlook.com>
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
#include "libslic3r/libslic3r.h"
|
||||
#include "libslic3r/Utils.hpp"
|
||||
#include "libslic3r/Format/DRC.hpp"
|
||||
#include "AppConfig.hpp"
|
||||
//BBS
|
||||
#include "Preset.hpp"
|
||||
@@ -124,6 +125,8 @@ void AppConfig::set_defaults()
|
||||
#ifdef _WIN32
|
||||
if (get("associate_3mf").empty())
|
||||
set_bool("associate_3mf", false);
|
||||
if (get("associate_drc").empty())
|
||||
set_bool("associate_drc", false);
|
||||
if (get("associate_stl").empty())
|
||||
set_bool("associate_stl", false);
|
||||
if (get("associate_step").empty())
|
||||
@@ -234,6 +237,9 @@ void AppConfig::set_defaults()
|
||||
if (get("enable_multi_machine").empty())
|
||||
set_bool("enable_multi_machine", false);
|
||||
|
||||
if (get("drc_bits").empty())
|
||||
set("drc_bits", DRC_BITS_DEFAULT_STR);
|
||||
|
||||
if (get("show_gcode_window").empty())
|
||||
set_bool("show_gcode_window", true);
|
||||
|
||||
|
||||
@@ -183,6 +183,8 @@ set(lisbslic3r_sources
|
||||
Format/3mf.hpp
|
||||
Format/AMF.cpp
|
||||
Format/AMF.hpp
|
||||
Format/DRC.cpp
|
||||
Format/DRC.hpp
|
||||
Format/bbs_3mf.cpp
|
||||
Format/bbs_3mf.hpp
|
||||
format.hpp
|
||||
@@ -538,6 +540,7 @@ find_package(OpenCASCADE REQUIRED)
|
||||
target_include_directories(libslic3r SYSTEM PUBLIC ${OpenCASCADE_INCLUDE_DIR})
|
||||
|
||||
find_package(JPEG REQUIRED)
|
||||
find_package(draco REQUIRED)
|
||||
|
||||
set(OCCT_LIBS
|
||||
TKXDESTEP
|
||||
@@ -583,6 +586,7 @@ target_link_libraries(libslic3r
|
||||
cereal::cereal
|
||||
clipper
|
||||
Clipper2
|
||||
draco::draco
|
||||
eigen
|
||||
glu-libtess
|
||||
JPEG::JPEG
|
||||
|
||||
166
src/libslic3r/Format/DRC.cpp
Normal file
166
src/libslic3r/Format/DRC.cpp
Normal file
@@ -0,0 +1,166 @@
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <cstring>
|
||||
|
||||
#include <boost/iostreams/device/mapped_file.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
#include <draco/compression/encode.h>
|
||||
#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)
|
||||
{
|
||||
try {
|
||||
boost::iostreams::mapped_file_source file(path);
|
||||
|
||||
DecoderBuffer buffer;
|
||||
buffer.Init(file.data(), file.size());
|
||||
|
||||
auto geotype = Decoder::GetEncodedGeometryType(&buffer);
|
||||
if ((!geotype.ok()) || geotype.value() != TRIANGULAR_MESH) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Decoder decoder;
|
||||
Mesh dracoMesh;
|
||||
Status status = decoder.DecodeBufferToGeometry(&buffer, &dracoMesh);
|
||||
if (!status.ok()) {
|
||||
return false;
|
||||
}
|
||||
file.close();
|
||||
|
||||
|
||||
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();
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "load_drc: " << e.what();
|
||||
return false;
|
||||
}
|
||||
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, int bits, int speed)
|
||||
{
|
||||
try {
|
||||
const std::vector<stl_triangle_vertex_indices>* indices = &(mesh->its.indices);
|
||||
const std::vector<stl_vertex>* vertices = &(mesh->its.vertices);
|
||||
|
||||
Mesh dracoMesh;
|
||||
|
||||
dracoMesh.set_num_points(vertices->size());
|
||||
|
||||
GeometryAttribute gaPos;
|
||||
gaPos.Init(GeometryAttribute::POSITION, nullptr, 3, DT_FLOAT32, false, sizeof(float)*3, 0);
|
||||
int32_t idPos = dracoMesh.AddAttribute(gaPos, true, indices->size() * 3);
|
||||
|
||||
dracoMesh.attribute(idPos)->Resize(vertices->size());
|
||||
|
||||
for (size_t i = 0; i < vertices->size(); ++ i) {
|
||||
float vertex[3];
|
||||
vertex[0] = vertices->at(i)(0);
|
||||
vertex[1] = vertices->at(i)(1);
|
||||
vertex[2] = vertices->at(i)(2);
|
||||
dracoMesh.attribute(idPos)->SetAttributeValue(AttributeValueIndex(i), vertex);
|
||||
}
|
||||
|
||||
dracoMesh.SetNumFaces(indices->size());
|
||||
for (size_t i = 0; i < indices->size(); ++ i) {
|
||||
Mesh::Face face;
|
||||
face[0] = PointIndex(indices->at(i)[0]);
|
||||
face[1] = PointIndex(indices->at(i)[1]);
|
||||
face[2] = PointIndex(indices->at(i)[2]);
|
||||
dracoMesh.SetFace(FaceIndex(i), face);
|
||||
}
|
||||
|
||||
Encoder encoder;
|
||||
encoder.SetSpeedOptions(speed, speed);
|
||||
encoder.SetAttributeQuantization(GeometryAttribute::POSITION, bits);
|
||||
|
||||
EncoderBuffer buffer;
|
||||
encoder.EncodeMeshToBuffer(dracoMesh, &buffer);
|
||||
|
||||
FILE* fp = boost::nowide::fopen(path, "wb");
|
||||
if (!fp) return false;
|
||||
size_t written = fwrite(buffer.data(), 1, buffer.size(), fp);
|
||||
fclose(fp);
|
||||
|
||||
if (written != buffer.size()) return false;
|
||||
} catch (const std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << "store_drc: " << e.what();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool store_drc(const char *path, ModelObject *model_object, int bits, int speed)
|
||||
{
|
||||
TriangleMesh mesh = model_object->mesh();
|
||||
return store_drc(path, &mesh, bits, speed);
|
||||
}
|
||||
|
||||
bool store_drc(const char *path, Model *model, int bits, int speed)
|
||||
{
|
||||
TriangleMesh mesh = model->mesh();
|
||||
return store_drc(path, &mesh, bits, speed);
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
||||
26
src/libslic3r/Format/DRC.hpp
Normal file
26
src/libslic3r/Format/DRC.hpp
Normal file
@@ -0,0 +1,26 @@
|
||||
#ifndef slic3r_Format_DRC_hpp_
|
||||
#define slic3r_Format_DRC_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
#define DRC_BITS_MIN 8
|
||||
#define DRC_BITS_MAX 30
|
||||
#define DRC_BITS_DEFAULT 0
|
||||
#define DRC_BITS_DEFAULT_STR "0"
|
||||
#define DRC_SPEED_DEFAULT 0
|
||||
|
||||
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, int bits, int speed = DRC_SPEED_DEFAULT);
|
||||
extern bool store_drc(const char* path, ModelObject* model_object, int bits, int speed = DRC_SPEED_DEFAULT);
|
||||
extern bool store_drc(const char* path, Model* model, int bits, int speed = DRC_SPEED_DEFAULT);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_DRC_hpp_ */
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#include "Format/AMF.hpp"
|
||||
#include "Format/svg.hpp"
|
||||
#include "Format/DRC.hpp"
|
||||
// BBS
|
||||
#include "FaceDetector.hpp"
|
||||
|
||||
@@ -311,6 +312,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);
|
||||
|
||||
Reference in New Issue
Block a user