mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-21 05:37:52 -06:00
Add the full source of BambuStudio
using version 1.0.10
This commit is contained in:
parent
30bcadab3e
commit
1555904bef
3771 changed files with 1251328 additions and 0 deletions
3142
src/libslic3r/Format/3mf.cpp
Normal file
3142
src/libslic3r/Format/3mf.cpp
Normal file
File diff suppressed because it is too large
Load diff
41
src/libslic3r/Format/3mf.hpp
Normal file
41
src/libslic3r/Format/3mf.hpp
Normal file
|
@ -0,0 +1,41 @@
|
|||
#ifndef slic3r_Format_3mf_hpp_
|
||||
#define slic3r_Format_3mf_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
/* The format for saving the SLA points was changing in the past. This enum holds the latest version that is being currently used.
|
||||
* Examples of the Slic3r_PE_sla_support_points.txt for historically used versions:
|
||||
|
||||
* version 0 : object_id=1|-12.055421 -2.658771 10.000000
|
||||
object_id=2|-14.051745 -3.570338 5.000000
|
||||
// no header and x,y,z positions of the points)
|
||||
|
||||
* version 1 : ThreeMF_support_points_version=1
|
||||
object_id=1|-12.055421 -2.658771 10.000000 0.4 0.0
|
||||
object_id=2|-14.051745 -3.570338 5.000000 0.6 1.0
|
||||
// introduced header with version number; x,y,z,head_size,is_new_island)
|
||||
*/
|
||||
|
||||
enum {
|
||||
support_points_format_version = 1
|
||||
};
|
||||
|
||||
enum {
|
||||
drain_holes_format_version = 1
|
||||
};
|
||||
|
||||
class Model;
|
||||
struct ConfigSubstitutionContext;
|
||||
class DynamicPrintConfig;
|
||||
struct ThumbnailData;
|
||||
|
||||
// Load the content of a 3mf file into the given model and preset bundle.
|
||||
extern bool load_3mf(const char* path, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Model* model, bool check_version);
|
||||
|
||||
// Save the given model and the config data contained in the given Print into a 3mf file.
|
||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
extern bool store_3mf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources, const ThumbnailData* thumbnail_data = nullptr, bool zip64 = true);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_3mf_hpp_ */
|
1394
src/libslic3r/Format/AMF.cpp
Normal file
1394
src/libslic3r/Format/AMF.cpp
Normal file
File diff suppressed because it is too large
Load diff
19
src/libslic3r/Format/AMF.hpp
Normal file
19
src/libslic3r/Format/AMF.hpp
Normal file
|
@ -0,0 +1,19 @@
|
|||
#ifndef slic3r_Format_AMF_hpp_
|
||||
#define slic3r_Format_AMF_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class Model;
|
||||
class DynamicPrintConfig;
|
||||
|
||||
// Load the content of an amf file into the given model and configuration.
|
||||
extern bool load_amf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, bool* use_inches);
|
||||
|
||||
//BBS: remove amf export
|
||||
// Save the given model and the config data into an amf file.
|
||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
//extern bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config, bool fullpath_sources);
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_AMF_hpp_ */
|
140
src/libslic3r/Format/OBJ.cpp
Normal file
140
src/libslic3r/Format/OBJ.cpp
Normal file
|
@ -0,0 +1,140 @@
|
|||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../TriangleMesh.hpp"
|
||||
|
||||
#include "OBJ.hpp"
|
||||
#include "objparser.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#else
|
||||
#define DIR_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool load_obj(const char *path, TriangleMesh *meshptr)
|
||||
{
|
||||
if (meshptr == nullptr)
|
||||
return false;
|
||||
|
||||
// Parse the OBJ file.
|
||||
ObjParser::ObjData data;
|
||||
if (! ObjParser::objparse(path, data)) {
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Count the faces and verify, that all faces are triangular.
|
||||
size_t num_faces = 0;
|
||||
size_t num_quads = 0;
|
||||
for (size_t i = 0; i < data.vertices.size(); ++ i) {
|
||||
// Find the end of face.
|
||||
size_t j = i;
|
||||
for (; j < data.vertices.size() && data.vertices[j].coordIdx != -1; ++ j) ;
|
||||
if (size_t num_face_vertices = j - i; num_face_vertices > 0) {
|
||||
if (num_face_vertices > 4) {
|
||||
// Non-triangular and non-quad faces are not supported as of now.
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with more than 4 vertices.";
|
||||
return false;
|
||||
} else if (num_face_vertices < 3) {
|
||||
// Non-triangular and non-quad faces are not supported as of now.
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains polygons with less than 2 vertices.";
|
||||
return false;
|
||||
}
|
||||
if (num_face_vertices == 4)
|
||||
++ num_quads;
|
||||
++ num_faces;
|
||||
i = j;
|
||||
}
|
||||
}
|
||||
|
||||
// Convert ObjData into indexed triangle set.
|
||||
indexed_triangle_set its;
|
||||
size_t num_vertices = data.coordinates.size() / 4;
|
||||
its.vertices.reserve(num_vertices);
|
||||
its.indices.reserve(num_faces + num_quads);
|
||||
for (size_t i = 0; i < num_vertices; ++ i) {
|
||||
size_t j = i << 2;
|
||||
its.vertices.emplace_back(data.coordinates[j], data.coordinates[j + 1], data.coordinates[j + 2]);
|
||||
}
|
||||
int indices[4];
|
||||
for (size_t i = 0; i < data.vertices.size();)
|
||||
if (data.vertices[i].coordIdx == -1)
|
||||
++ i;
|
||||
else {
|
||||
int cnt = 0;
|
||||
while (i < data.vertices.size())
|
||||
if (const ObjParser::ObjVertex &vertex = data.vertices[i ++]; vertex.coordIdx == -1) {
|
||||
break;
|
||||
} else {
|
||||
assert(cnt < 4);
|
||||
if (vertex.coordIdx < 0 || vertex.coordIdx >= int(its.vertices.size())) {
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: failed to parse " << path << ". The file contains invalid vertex index.";
|
||||
return false;
|
||||
}
|
||||
indices[cnt ++] = vertex.coordIdx;
|
||||
}
|
||||
if (cnt) {
|
||||
assert(cnt == 3 || cnt == 4);
|
||||
// Insert one or two faces (triangulate a quad).
|
||||
its.indices.emplace_back(indices[0], indices[1], indices[2]);
|
||||
if (cnt == 4)
|
||||
its.indices.emplace_back(indices[0], indices[2], indices[3]);
|
||||
}
|
||||
}
|
||||
|
||||
*meshptr = TriangleMesh(std::move(its));
|
||||
if (meshptr->empty()) {
|
||||
BOOST_LOG_TRIVIAL(error) << "load_obj: This OBJ file couldn't be read because it's empty. " << path;
|
||||
return false;
|
||||
}
|
||||
if (meshptr->volume() < 0)
|
||||
meshptr->flip_triangles();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool load_obj(const char *path, Model *model, const char *object_name_in)
|
||||
{
|
||||
TriangleMesh mesh;
|
||||
|
||||
bool ret = load_obj(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_obj(const char *path, TriangleMesh *mesh)
|
||||
{
|
||||
//FIXME returning false even if write failed.
|
||||
mesh->WriteOBJFile(path);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool store_obj(const char *path, ModelObject *model_object)
|
||||
{
|
||||
TriangleMesh mesh = model_object->mesh();
|
||||
return store_obj(path, &mesh);
|
||||
}
|
||||
|
||||
bool store_obj(const char *path, Model *model)
|
||||
{
|
||||
TriangleMesh mesh = model->mesh();
|
||||
return store_obj(path, &mesh);
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
20
src/libslic3r/Format/OBJ.hpp
Normal file
20
src/libslic3r/Format/OBJ.hpp
Normal file
|
@ -0,0 +1,20 @@
|
|||
#ifndef slic3r_Format_OBJ_hpp_
|
||||
#define slic3r_Format_OBJ_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class Model;
|
||||
class ModelObject;
|
||||
|
||||
// Load an OBJ file into a provided model.
|
||||
extern bool load_obj(const char *path, TriangleMesh *mesh);
|
||||
extern bool load_obj(const char *path, Model *model, const char *object_name = nullptr);
|
||||
|
||||
extern bool store_obj(const char *path, TriangleMesh *mesh);
|
||||
extern bool store_obj(const char *path, ModelObject *model);
|
||||
extern bool store_obj(const char *path, Model *model);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_OBJ_hpp_ */
|
518
src/libslic3r/Format/SL1.cpp
Normal file
518
src/libslic3r/Format/SL1.cpp
Normal file
|
@ -0,0 +1,518 @@
|
|||
#include "SL1.hpp"
|
||||
#include "GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/Time.hpp"
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/filesystem.hpp>
|
||||
|
||||
#include "libslic3r/Zipper.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include "libslic3r/Exception.hpp"
|
||||
#include "libslic3r/SlicesToTriangleMesh.hpp"
|
||||
#include "libslic3r/MarchingSquares.hpp"
|
||||
#include "libslic3r/ClipperUtils.hpp"
|
||||
#include "libslic3r/MTUtils.hpp"
|
||||
#include "libslic3r/PrintConfig.hpp"
|
||||
#include "libslic3r/SLA/RasterBase.hpp"
|
||||
#include "libslic3r/miniz_extension.hpp"
|
||||
#include "libslic3r/PNGReadWrite.hpp"
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
#include <boost/property_tree/ini_parser.hpp>
|
||||
#include <boost/filesystem/path.hpp>
|
||||
#include <boost/algorithm/string.hpp>
|
||||
|
||||
namespace marchsq {
|
||||
|
||||
template<> struct _RasterTraits<Slic3r::png::ImageGreyscale> {
|
||||
using Rst = Slic3r::png::ImageGreyscale;
|
||||
|
||||
// The type of pixel cell in the raster
|
||||
using ValueType = uint8_t;
|
||||
|
||||
// Value at a given position
|
||||
static uint8_t get(const Rst &rst, size_t row, size_t col)
|
||||
{
|
||||
return rst.get(row, col);
|
||||
}
|
||||
|
||||
// Number of rows and cols of the raster
|
||||
static size_t rows(const Rst &rst) { return rst.rows; }
|
||||
static size_t cols(const Rst &rst) { return rst.cols; }
|
||||
};
|
||||
|
||||
} // namespace marchsq
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
namespace {
|
||||
|
||||
struct PNGBuffer { std::vector<uint8_t> buf; std::string fname; };
|
||||
struct ArchiveData {
|
||||
boost::property_tree::ptree profile, config;
|
||||
std::vector<PNGBuffer> images;
|
||||
};
|
||||
|
||||
static const constexpr char *CONFIG_FNAME = "config.ini";
|
||||
static const constexpr char *PROFILE_FNAME = "prusaslicer.ini";
|
||||
|
||||
boost::property_tree::ptree read_ini(const mz_zip_archive_file_stat &entry,
|
||||
MZ_Archive & zip)
|
||||
{
|
||||
std::string buf(size_t(entry.m_uncomp_size), '\0');
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
|
||||
buf.data(), buf.size(), 0))
|
||||
throw Slic3r::FileIOError(zip.get_errorstr());
|
||||
|
||||
boost::property_tree::ptree tree;
|
||||
std::stringstream ss(buf);
|
||||
boost::property_tree::read_ini(ss, tree);
|
||||
return tree;
|
||||
}
|
||||
|
||||
PNGBuffer read_png(const mz_zip_archive_file_stat &entry,
|
||||
MZ_Archive & zip,
|
||||
const std::string & name)
|
||||
{
|
||||
std::vector<uint8_t> buf(entry.m_uncomp_size);
|
||||
|
||||
if (!mz_zip_reader_extract_file_to_mem(&zip.arch, entry.m_filename,
|
||||
buf.data(), buf.size(), 0))
|
||||
throw Slic3r::FileIOError(zip.get_errorstr());
|
||||
|
||||
return {std::move(buf), (name.empty() ? entry.m_filename : name)};
|
||||
}
|
||||
|
||||
ArchiveData extract_sla_archive(const std::string &zipfname,
|
||||
const std::string &exclude)
|
||||
{
|
||||
ArchiveData arch;
|
||||
|
||||
// Little RAII
|
||||
struct Arch: public MZ_Archive {
|
||||
Arch(const std::string &fname) {
|
||||
if (!open_zip_reader(&arch, fname))
|
||||
throw Slic3r::FileIOError(get_errorstr());
|
||||
}
|
||||
|
||||
~Arch() { close_zip_reader(&arch); }
|
||||
} zip (zipfname);
|
||||
|
||||
mz_uint num_entries = mz_zip_reader_get_num_files(&zip.arch);
|
||||
|
||||
for (mz_uint i = 0; i < num_entries; ++i)
|
||||
{
|
||||
mz_zip_archive_file_stat entry;
|
||||
|
||||
if (mz_zip_reader_file_stat(&zip.arch, i, &entry))
|
||||
{
|
||||
std::string name = entry.m_filename;
|
||||
boost::algorithm::to_lower(name);
|
||||
|
||||
if (boost::algorithm::contains(name, exclude)) continue;
|
||||
|
||||
if (name == CONFIG_FNAME) arch.config = read_ini(entry, zip);
|
||||
if (name == PROFILE_FNAME) arch.profile = read_ini(entry, zip);
|
||||
|
||||
if (boost::filesystem::path(name).extension().string() == ".png") {
|
||||
auto it = std::lower_bound(
|
||||
arch.images.begin(), arch.images.end(), PNGBuffer{{}, name},
|
||||
[](const PNGBuffer &r1, const PNGBuffer &r2) {
|
||||
return std::less<std::string>()(r1.fname, r2.fname);
|
||||
});
|
||||
|
||||
arch.images.insert(it, read_png(entry, zip, name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return arch;
|
||||
}
|
||||
|
||||
ExPolygons rings_to_expolygons(const std::vector<marchsq::Ring> &rings,
|
||||
double px_w, double px_h)
|
||||
{
|
||||
auto polys = reserve_vector<ExPolygon>(rings.size());
|
||||
|
||||
for (const marchsq::Ring &ring : rings) {
|
||||
Polygon poly; Points &pts = poly.points;
|
||||
pts.reserve(ring.size());
|
||||
|
||||
for (const marchsq::Coord &crd : ring)
|
||||
pts.emplace_back(scaled(crd.c * px_w), scaled(crd.r * px_h));
|
||||
|
||||
polys.emplace_back(poly);
|
||||
}
|
||||
|
||||
// TODO: Is a union necessary?
|
||||
return union_ex(polys);
|
||||
}
|
||||
|
||||
template<class Fn> void foreach_vertex(ExPolygon &poly, Fn &&fn)
|
||||
{
|
||||
for (auto &p : poly.contour.points) fn(p);
|
||||
for (auto &h : poly.holes)
|
||||
for (auto &p : h.points) fn(p);
|
||||
}
|
||||
|
||||
void invert_raster_trafo(ExPolygons & expolys,
|
||||
const sla::RasterBase::Trafo &trafo,
|
||||
coord_t width,
|
||||
coord_t height)
|
||||
{
|
||||
if (trafo.flipXY) std::swap(height, width);
|
||||
|
||||
for (auto &expoly : expolys) {
|
||||
if (trafo.mirror_y)
|
||||
foreach_vertex(expoly, [height](Point &p) {p.y() = height - p.y(); });
|
||||
|
||||
if (trafo.mirror_x)
|
||||
foreach_vertex(expoly, [width](Point &p) {p.x() = width - p.x(); });
|
||||
|
||||
expoly.translate(-trafo.center_x, -trafo.center_y);
|
||||
|
||||
if (trafo.flipXY)
|
||||
foreach_vertex(expoly, [](Point &p) { std::swap(p.x(), p.y()); });
|
||||
|
||||
if ((trafo.mirror_x + trafo.mirror_y + trafo.flipXY) % 2) {
|
||||
expoly.contour.reverse();
|
||||
for (auto &h : expoly.holes) h.reverse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct RasterParams {
|
||||
sla::RasterBase::Trafo trafo; // Raster transformations
|
||||
coord_t width, height; // scaled raster dimensions (not resolution)
|
||||
double px_h, px_w; // pixel dimesions
|
||||
marchsq::Coord win; // marching squares window size
|
||||
};
|
||||
|
||||
RasterParams get_raster_params(const DynamicPrintConfig &cfg)
|
||||
{
|
||||
auto *opt_disp_cols = cfg.option<ConfigOptionInt>("display_pixels_x");
|
||||
auto *opt_disp_rows = cfg.option<ConfigOptionInt>("display_pixels_y");
|
||||
auto *opt_disp_w = cfg.option<ConfigOptionFloat>("display_width");
|
||||
auto *opt_disp_h = cfg.option<ConfigOptionFloat>("display_height");
|
||||
auto *opt_mirror_x = cfg.option<ConfigOptionBool>("display_mirror_x");
|
||||
auto *opt_mirror_y = cfg.option<ConfigOptionBool>("display_mirror_y");
|
||||
auto *opt_orient = cfg.option<ConfigOptionEnum<SLADisplayOrientation>>("display_orientation");
|
||||
|
||||
if (!opt_disp_cols || !opt_disp_rows || !opt_disp_w || !opt_disp_h ||
|
||||
!opt_mirror_x || !opt_mirror_y || !opt_orient)
|
||||
throw MissingProfileError("Invalid SL1 / SL1S file");
|
||||
|
||||
RasterParams rstp;
|
||||
|
||||
rstp.px_w = opt_disp_w->value / (opt_disp_cols->value - 1);
|
||||
rstp.px_h = opt_disp_h->value / (opt_disp_rows->value - 1);
|
||||
|
||||
rstp.trafo = sla::RasterBase::Trafo{opt_orient->value == sladoLandscape ?
|
||||
sla::RasterBase::roLandscape :
|
||||
sla::RasterBase::roPortrait,
|
||||
{opt_mirror_x->value, opt_mirror_y->value}};
|
||||
|
||||
rstp.height = scaled(opt_disp_h->value);
|
||||
rstp.width = scaled(opt_disp_w->value);
|
||||
|
||||
return rstp;
|
||||
}
|
||||
|
||||
struct SliceParams { double layerh = 0., initial_layerh = 0.; };
|
||||
|
||||
SliceParams get_slice_params(const DynamicPrintConfig &cfg)
|
||||
{
|
||||
auto *opt_layerh = cfg.option<ConfigOptionFloat>("layer_height");
|
||||
auto *opt_init_layerh = cfg.option<ConfigOptionFloat>("initial_layer_height");
|
||||
|
||||
if (!opt_layerh || !opt_init_layerh)
|
||||
throw MissingProfileError("Invalid SL1 / SL1S file");
|
||||
|
||||
return SliceParams{opt_layerh->getFloat(), opt_init_layerh->getFloat()};
|
||||
}
|
||||
|
||||
std::vector<ExPolygons> extract_slices_from_sla_archive(
|
||||
ArchiveData & arch,
|
||||
const RasterParams & rstp,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
auto jobdir = arch.config.get<std::string>("jobDir");
|
||||
for (auto &c : jobdir) c = std::tolower(c);
|
||||
|
||||
std::vector<ExPolygons> slices(arch.images.size());
|
||||
|
||||
struct Status
|
||||
{
|
||||
double incr, val, prev;
|
||||
bool stop = false;
|
||||
tbb::spin_mutex mutex = {};
|
||||
} st {100. / slices.size(), 0., 0.};
|
||||
|
||||
tbb::parallel_for(size_t(0), arch.images.size(),
|
||||
[&arch, &slices, &st, &rstp, progr](size_t i) {
|
||||
// Status indication guarded with the spinlock
|
||||
{
|
||||
std::lock_guard<tbb::spin_mutex> lck(st.mutex);
|
||||
if (st.stop) return;
|
||||
|
||||
st.val += st.incr;
|
||||
double curr = std::round(st.val);
|
||||
if (curr > st.prev) {
|
||||
st.prev = curr;
|
||||
st.stop = !progr(int(curr));
|
||||
}
|
||||
}
|
||||
|
||||
png::ImageGreyscale img;
|
||||
png::ReadBuf rb{arch.images[i].buf.data(), arch.images[i].buf.size()};
|
||||
if (!png::decode_png(rb, img)) return;
|
||||
|
||||
uint8_t isoval = 128;
|
||||
auto rings = marchsq::execute(img, isoval, rstp.win);
|
||||
ExPolygons expolys = rings_to_expolygons(rings, rstp.px_w, rstp.px_h);
|
||||
|
||||
// Invert the raster transformations indicated in the profile metadata
|
||||
invert_raster_trafo(expolys, rstp.trafo, rstp.width, rstp.height);
|
||||
|
||||
slices[i] = std::move(expolys);
|
||||
});
|
||||
|
||||
if (st.stop) slices = {};
|
||||
|
||||
return slices;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out)
|
||||
{
|
||||
ArchiveData arch = extract_sla_archive(zipfname, "png");
|
||||
return out.load(arch.profile, ForwardCompatibilitySubstitutionRule::Enable);
|
||||
}
|
||||
|
||||
// If the profile is missing from the archive (older PS versions did not have
|
||||
// it), profile_out's initial value will be used as fallback. profile_out will be empty on
|
||||
// function return if the archive did not contain any profile.
|
||||
ConfigSubstitutions import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
indexed_triangle_set & out,
|
||||
DynamicPrintConfig & profile_out,
|
||||
std::function<bool(int)> progr)
|
||||
{
|
||||
// Ensure minimum window size for marching squares
|
||||
windowsize.x() = std::max(2, windowsize.x());
|
||||
windowsize.y() = std::max(2, windowsize.y());
|
||||
|
||||
std::string exclude_entries{"thumbnail"};
|
||||
ArchiveData arch = extract_sla_archive(zipfname, exclude_entries);
|
||||
DynamicPrintConfig profile_in, profile_use;
|
||||
ConfigSubstitutions config_substitutions =
|
||||
profile_in.load(arch.profile,
|
||||
ForwardCompatibilitySubstitutionRule::Enable);
|
||||
|
||||
if (profile_in.empty()) { // missing profile... do guess work
|
||||
// try to recover the layer height from the config.ini which was
|
||||
// present in all versions of sl1 files.
|
||||
if (auto lh_opt = arch.config.find("layerHeight");
|
||||
lh_opt != arch.config.not_found())
|
||||
{
|
||||
auto lh_str = lh_opt->second.data();
|
||||
|
||||
size_t pos;
|
||||
double lh = string_to_double_decimal_point(lh_str, &pos);
|
||||
if (pos) { // TODO: verify that pos is 0 when parsing fails
|
||||
profile_out.set("layer_height", lh);
|
||||
profile_out.set("initial_layer_height", lh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If the archive contains an empty profile, use the one that was passed as output argument
|
||||
// then replace it with the readed profile to report that it was empty.
|
||||
profile_use = profile_in.empty() ? profile_out : profile_in;
|
||||
profile_out = profile_in;
|
||||
|
||||
RasterParams rstp = get_raster_params(profile_use);
|
||||
rstp.win = {windowsize.y(), windowsize.x()};
|
||||
|
||||
SliceParams slicp = get_slice_params(profile_use);
|
||||
|
||||
std::vector<ExPolygons> slices =
|
||||
extract_slices_from_sla_archive(arch, rstp, progr);
|
||||
|
||||
if (!slices.empty())
|
||||
out = slices_to_mesh(slices, 0, slicp.layerh, slicp.initial_layerh);
|
||||
|
||||
return config_substitutions;
|
||||
}
|
||||
|
||||
using ConfMap = std::map<std::string, std::string>;
|
||||
|
||||
namespace {
|
||||
|
||||
std::string to_ini(const ConfMap &m)
|
||||
{
|
||||
std::string ret;
|
||||
for (auto ¶m : m) ret += param.first + " = " + param.second + "\n";
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
std::string get_cfg_value(const DynamicPrintConfig &cfg, const std::string &key)
|
||||
{
|
||||
std::string ret;
|
||||
|
||||
if (cfg.has(key)) {
|
||||
auto opt = cfg.option(key);
|
||||
if (opt) ret = opt->serialize();
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fill_iniconf(ConfMap &m, const SLAPrint &print)
|
||||
{
|
||||
CNumericLocalesSetter locales_setter; // for to_string
|
||||
auto &cfg = print.full_print_config();
|
||||
m["layerHeight"] = get_cfg_value(cfg, "layer_height");
|
||||
m["expTime"] = get_cfg_value(cfg, "exposure_time");
|
||||
m["expTimeFirst"] = get_cfg_value(cfg, "initial_exposure_time");
|
||||
m["expUserProfile"] = get_cfg_value(cfg, "material_print_speed") == "slow" ? "1" : "0";
|
||||
m["materialName"] = get_cfg_value(cfg, "sla_material_settings_id");
|
||||
m["printerModel"] = get_cfg_value(cfg, "printer_model");
|
||||
m["printerVariant"] = get_cfg_value(cfg, "printer_variant");
|
||||
m["printerProfile"] = get_cfg_value(cfg, "printer_settings_id");
|
||||
m["printProfile"] = get_cfg_value(cfg, "sla_print_settings_id");
|
||||
m["fileCreationTimestamp"] = Utils::utc_timestamp();
|
||||
m["prusaSlicerVersion"] = SLIC3R_BUILD_ID;
|
||||
|
||||
SLAPrintStatistics stats = print.print_statistics();
|
||||
// Set statistics values to the printer
|
||||
|
||||
double used_material = (stats.objects_used_material +
|
||||
stats.support_used_material) / 1000;
|
||||
|
||||
int num_fade = print.default_object_config().faded_layers.getInt();
|
||||
num_fade = num_fade >= 0 ? num_fade : 0;
|
||||
|
||||
m["usedMaterial"] = std::to_string(used_material);
|
||||
m["numFade"] = std::to_string(num_fade);
|
||||
m["numSlow"] = std::to_string(stats.slow_layers_count);
|
||||
m["numFast"] = std::to_string(stats.fast_layers_count);
|
||||
m["printTime"] = std::to_string(stats.estimated_print_time);
|
||||
|
||||
bool hollow_en = false;
|
||||
auto it = print.objects().begin();
|
||||
while (!hollow_en && it != print.objects().end())
|
||||
hollow_en = (*it++)->config().hollowing_enable;
|
||||
|
||||
m["hollow"] = hollow_en ? "1" : "0";
|
||||
|
||||
m["action"] = "print";
|
||||
}
|
||||
|
||||
void fill_slicerconf(ConfMap &m, const SLAPrint &print)
|
||||
{
|
||||
using namespace std::literals::string_view_literals;
|
||||
|
||||
// Sorted list of config keys, which shall not be stored into the ini.
|
||||
static constexpr auto banned_keys = {
|
||||
"compatible_printers"sv,
|
||||
"compatible_prints"sv
|
||||
};
|
||||
|
||||
assert(std::is_sorted(banned_keys.begin(), banned_keys.end()));
|
||||
auto is_banned = [](const std::string &key) {
|
||||
return std::binary_search(banned_keys.begin(), banned_keys.end(), key);
|
||||
};
|
||||
|
||||
auto &cfg = print.full_print_config();
|
||||
for (const std::string &key : cfg.keys())
|
||||
if (! is_banned(key) && ! cfg.option(key)->is_nil())
|
||||
m[key] = cfg.opt_serialize(key);
|
||||
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<sla::RasterBase> SL1Archive::create_raster() const
|
||||
{
|
||||
sla::RasterBase::Resolution res;
|
||||
sla::RasterBase::PixelDim pxdim;
|
||||
std::array<bool, 2> mirror;
|
||||
|
||||
double w = m_cfg.display_width.getFloat();
|
||||
double h = m_cfg.display_height.getFloat();
|
||||
auto pw = size_t(m_cfg.display_pixels_x.getInt());
|
||||
auto ph = size_t(m_cfg.display_pixels_y.getInt());
|
||||
|
||||
mirror[X] = m_cfg.display_mirror_x.getBool();
|
||||
mirror[Y] = m_cfg.display_mirror_y.getBool();
|
||||
|
||||
auto ro = m_cfg.display_orientation.getInt();
|
||||
sla::RasterBase::Orientation orientation =
|
||||
ro == sla::RasterBase::roPortrait ? sla::RasterBase::roPortrait :
|
||||
sla::RasterBase::roLandscape;
|
||||
|
||||
if (orientation == sla::RasterBase::roPortrait) {
|
||||
std::swap(w, h);
|
||||
std::swap(pw, ph);
|
||||
}
|
||||
|
||||
res = sla::RasterBase::Resolution{pw, ph};
|
||||
pxdim = sla::RasterBase::PixelDim{w / pw, h / ph};
|
||||
sla::RasterBase::Trafo tr{orientation, mirror};
|
||||
|
||||
double gamma = m_cfg.gamma_correction.getFloat();
|
||||
|
||||
return sla::create_raster_grayscale_aa(res, pxdim, gamma, tr);
|
||||
}
|
||||
|
||||
sla::RasterEncoder SL1Archive::get_encoder() const
|
||||
{
|
||||
return sla::PNGRasterEncoder{};
|
||||
}
|
||||
|
||||
void SL1Archive::export_print(Zipper& zipper,
|
||||
const SLAPrint &print,
|
||||
const std::string &prjname)
|
||||
{
|
||||
std::string project =
|
||||
prjname.empty() ?
|
||||
boost::filesystem::path(zipper.get_filename()).stem().string() :
|
||||
prjname;
|
||||
|
||||
ConfMap iniconf, slicerconf;
|
||||
fill_iniconf(iniconf, print);
|
||||
|
||||
iniconf["jobDir"] = project;
|
||||
|
||||
fill_slicerconf(slicerconf, print);
|
||||
|
||||
try {
|
||||
zipper.add_entry("config.ini");
|
||||
zipper << to_ini(iniconf);
|
||||
zipper.add_entry("prusaslicer.ini");
|
||||
zipper << to_ini(slicerconf);
|
||||
|
||||
size_t i = 0;
|
||||
for (const sla::EncodedRaster &rst : m_layers) {
|
||||
|
||||
std::string imgname = project + string_printf("%.5d", i++) + "." +
|
||||
rst.extension();
|
||||
|
||||
zipper.add_entry(imgname.c_str(), rst.data(), rst.size());
|
||||
}
|
||||
} catch(std::exception& e) {
|
||||
BOOST_LOG_TRIVIAL(error) << e.what();
|
||||
// Rethrow the exception
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
64
src/libslic3r/Format/SL1.hpp
Normal file
64
src/libslic3r/Format/SL1.hpp
Normal file
|
@ -0,0 +1,64 @@
|
|||
#ifndef ARCHIVETRAITS_HPP
|
||||
#define ARCHIVETRAITS_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "libslic3r/Zipper.hpp"
|
||||
#include "libslic3r/SLAPrint.hpp"
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class SL1Archive: public SLAArchive {
|
||||
SLAPrinterConfig m_cfg;
|
||||
|
||||
protected:
|
||||
std::unique_ptr<sla::RasterBase> create_raster() const override;
|
||||
sla::RasterEncoder get_encoder() const override;
|
||||
|
||||
public:
|
||||
|
||||
SL1Archive() = default;
|
||||
explicit SL1Archive(const SLAPrinterConfig &cfg): m_cfg(cfg) {}
|
||||
explicit SL1Archive(SLAPrinterConfig &&cfg): m_cfg(std::move(cfg)) {}
|
||||
|
||||
void export_print(Zipper &zipper, const SLAPrint &print, const std::string &projectname = "");
|
||||
void export_print(const std::string &fname, const SLAPrint &print, const std::string &projectname = "")
|
||||
{
|
||||
Zipper zipper(fname);
|
||||
export_print(zipper, print, projectname);
|
||||
}
|
||||
|
||||
void apply(const SLAPrinterConfig &cfg) override
|
||||
{
|
||||
auto diff = m_cfg.diff(cfg);
|
||||
if (!diff.empty()) {
|
||||
m_cfg.apply_only(cfg, diff);
|
||||
m_layers = {};
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ConfigSubstitutions import_sla_archive(const std::string &zipfname, DynamicPrintConfig &out);
|
||||
|
||||
ConfigSubstitutions import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
indexed_triangle_set & out,
|
||||
DynamicPrintConfig & profile,
|
||||
std::function<bool(int)> progr = [](int) { return true; });
|
||||
|
||||
inline ConfigSubstitutions import_sla_archive(
|
||||
const std::string & zipfname,
|
||||
Vec2i windowsize,
|
||||
indexed_triangle_set & out,
|
||||
std::function<bool(int)> progr = [](int) { return true; })
|
||||
{
|
||||
DynamicPrintConfig profile;
|
||||
return import_sla_archive(zipfname, windowsize, out, profile, progr);
|
||||
}
|
||||
|
||||
class MissingProfileError : public RuntimeError { using RuntimeError::RuntimeError; };
|
||||
|
||||
} // namespace Slic3r::sla
|
||||
|
||||
#endif // ARCHIVETRAITS_HPP
|
378
src/libslic3r/Format/STEP.cpp
Normal file
378
src/libslic3r/Format/STEP.cpp
Normal file
|
@ -0,0 +1,378 @@
|
|||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../TriangleMesh.hpp"
|
||||
|
||||
#include "STEP.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#else
|
||||
#define DIR_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
#include "STEPCAFControl_Reader.hxx"
|
||||
#include "BRepMesh_IncrementalMesh.hxx"
|
||||
#include "Interface_Static.hxx"
|
||||
#include "XCAFDoc_DocumentTool.hxx"
|
||||
#include "XCAFDoc_ShapeTool.hxx"
|
||||
#include "XCAFApp_Application.hxx"
|
||||
#include "TopoDS_Solid.hxx"
|
||||
#include "TopoDS_Compound.hxx"
|
||||
#include "TopoDS_Builder.hxx"
|
||||
#include "TopoDS.hxx"
|
||||
#include "TDataStd_Name.hxx"
|
||||
#include "BRepBuilderAPI_Transform.hxx"
|
||||
#include "TopExp_Explorer.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 {
|
||||
|
||||
bool StepPreProcessor::preprocess(const char* path, std::string &output_path)
|
||||
{
|
||||
boost::nowide::ifstream infile(path);
|
||||
if (!infile.good()) {
|
||||
throw Slic3r::RuntimeError(std::string("Load step file failed.\nCannot open file for reading.\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
boost::filesystem::path temp_path(temporary_dir());
|
||||
std::string temp_step_path = temp_path.string() + "/temp.step";
|
||||
boost::nowide::remove(temp_step_path.c_str());
|
||||
boost::nowide::ofstream temp_file(temp_step_path, std::ios::app);
|
||||
std::string temp_line;
|
||||
while (std::getline(infile, temp_line)) {
|
||||
if (m_encode_type == EncodedType::UTF8) {
|
||||
//BBS: continue to judge whether is other type
|
||||
if (isUtf8(temp_line)) {
|
||||
//BBS: do nothing, but must be checked before checking whether is GBK
|
||||
}
|
||||
//BBS: not utf8, then maybe GBK
|
||||
else if (isGBK(temp_line)) {
|
||||
m_encode_type = EncodedType::GBK;
|
||||
}
|
||||
//BBS: not UTF8 and not GBK, then maybe some kind of special encoded type which we can't handle
|
||||
// Load the step as UTF and user will see garbage characters in slicer but we have no solution at the moment
|
||||
else {
|
||||
m_encode_type = EncodedType::OTHER;
|
||||
}
|
||||
}
|
||||
if (m_encode_type == EncodedType::GBK)
|
||||
//BBS: transform to UTF8 format if is GBK
|
||||
//todo: use gbkToUtf8 function to replace
|
||||
temp_file << decode_path(temp_line.c_str()) << std::endl;
|
||||
else
|
||||
temp_file << temp_line.c_str() << std::endl;
|
||||
}
|
||||
temp_file.close();
|
||||
infile.close();
|
||||
if (m_encode_type == EncodedType::GBK) {
|
||||
output_path = temp_step_path;
|
||||
} else {
|
||||
boost::nowide::remove(temp_step_path.c_str());
|
||||
output_path = std::string(path);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StepPreProcessor::isUtf8File(const char* path)
|
||||
{
|
||||
boost::nowide::ifstream infile(path);
|
||||
if (!infile.good()) {
|
||||
throw Slic3r::RuntimeError(std::string("Load step file failed.\nCannot open file for reading.\n"));
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string temp_line;
|
||||
while (std::getline(infile, temp_line)) {
|
||||
if (!isUtf8(temp_line)) {
|
||||
infile.close();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
infile.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StepPreProcessor::isUtf8(const std::string str)
|
||||
{
|
||||
size_t num = 0;
|
||||
int i = 0;
|
||||
while (i < str.length()) {
|
||||
if ((str[i] & 0x80) == 0x00) {
|
||||
i++;
|
||||
} else if ((num = preNum(str[i])) > 2) {
|
||||
i++;
|
||||
for (int j = 0; j < num - 1; j++) {
|
||||
if ((str[i] & 0xc0) != 0x80)
|
||||
return false;
|
||||
i++;
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StepPreProcessor::isGBK(const std::string str) {
|
||||
size_t i = 0;
|
||||
while (i < str.length()) {
|
||||
if (str[i] <= 0x7f) {
|
||||
i++;
|
||||
continue;
|
||||
} else {
|
||||
if (str[i] >= 0x81 &&
|
||||
str[i] <= 0xfe &&
|
||||
str[i + 1] >= 0x40 &&
|
||||
str[i + 1] <= 0xfe &&
|
||||
str[i + 1] != 0xf7) {
|
||||
i += 2;
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int StepPreProcessor::preNum(const unsigned char byte) {
|
||||
unsigned char mask = 0x80;
|
||||
int num = 0;
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if ((byte & mask) == mask) {
|
||||
mask = mask >> 1;
|
||||
num++;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return num;
|
||||
}
|
||||
|
||||
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 std::string& prefix,
|
||||
unsigned int& id, 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();
|
||||
|
||||
if (name == "")
|
||||
name = std::to_string(id++);
|
||||
std::string fullName{name};
|
||||
|
||||
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, fullName, id, 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()), fullName);
|
||||
break;
|
||||
case TopAbs_COMPSOLID:
|
||||
namedSolids.emplace_back(TopoDS::CompSolid(transform.Shape()), fullName);
|
||||
break;
|
||||
case TopAbs_SOLID:
|
||||
namedSolids.emplace_back(TopoDS::Solid(transform.Shape()), fullName);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool load_step(const char *path, Model *model, ImportStepProgressFn proFn, StepIsUtf8Fn isUtf8Fn)
|
||||
{
|
||||
bool cb_cancel = false;
|
||||
if (proFn) {
|
||||
proFn(LOAD_STEP_STAGE_READ_FILE, 0, 1, cb_cancel);
|
||||
if (cb_cancel)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!StepPreProcessor::isUtf8File(path) && isUtf8Fn)
|
||||
isUtf8Fn(false);
|
||||
std::string file_after_preprocess = std::string(path);
|
||||
|
||||
std::vector<NamedSolid> namedSolids;
|
||||
Handle(TDocStd_Document) document;
|
||||
Handle(XCAFApp_Application) application = XCAFApp_Application::GetApplication();
|
||||
application->NewDocument(file_after_preprocess.c_str(), 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(file_after_preprocess.c_str());
|
||||
if (stat != IFSelect_RetDone || !reader.Transfer(document)) {
|
||||
application->Close(document);
|
||||
throw std::logic_error{ std::string{"Could not read '"} + path + "'" };
|
||||
return false;
|
||||
}
|
||||
Handle(XCAFDoc_ShapeTool) shapeTool = XCAFDoc_DocumentTool::ShapeTool(document->Main());
|
||||
TDF_LabelSequence topLevelShapes;
|
||||
shapeTool->GetFreeShapes(topLevelShapes);
|
||||
|
||||
unsigned int id{1};
|
||||
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{}, "", id, shapeTool, topLevelShapes.Value(iLabel), namedSolids);
|
||||
}
|
||||
|
||||
ModelObject* new_object = model->add_object();
|
||||
const char *last_slash = strrchr(path, DIR_SEPARATOR);
|
||||
new_object->name.assign((last_slash == nullptr) ? path : last_slash + 1);
|
||||
new_object->input_file = path;
|
||||
|
||||
for (size_t i = 0; i < namedSolids.size(); ++i) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
BRepMesh_IncrementalMesh mesh(namedSolids[i].solid, STEP_TRANS_CHORD_ERROR, false, STEP_TRANS_ANGLE_RES, true);
|
||||
//BBS: calculate total number of the nodes and triangles
|
||||
int aNbNodes = 0;
|
||||
int aNbTriangles = 0;
|
||||
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
|
||||
TopLoc_Location aLoc;
|
||||
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(anExpSF.Current()), aLoc);
|
||||
if (!aTriangulation.IsNull()) {
|
||||
aNbNodes += aTriangulation->NbNodes();
|
||||
aNbTriangles += aTriangulation->NbTriangles();
|
||||
}
|
||||
}
|
||||
|
||||
if (aNbTriangles == 0) {
|
||||
//BBS: No triangulation on the shape.
|
||||
continue;
|
||||
}
|
||||
|
||||
stl_file stl;
|
||||
stl.stats.type = inmemory;
|
||||
stl.stats.number_of_facets = (uint32_t)aNbTriangles;
|
||||
stl.stats.original_num_facets = stl.stats.number_of_facets;
|
||||
stl_allocate(&stl);
|
||||
|
||||
std::vector<Vec3f> points;
|
||||
points.reserve(aNbNodes);
|
||||
//BBS: count faces missing triangulation
|
||||
Standard_Integer aNbFacesNoTri = 0;
|
||||
//BBS: fill temporary triangulation
|
||||
Standard_Integer aNodeOffset = 0;
|
||||
Standard_Integer aTriangleOffet = 0;
|
||||
for (TopExp_Explorer anExpSF(namedSolids[i].solid, TopAbs_FACE); anExpSF.More(); anExpSF.Next()) {
|
||||
const TopoDS_Shape& aFace = anExpSF.Current();
|
||||
TopLoc_Location aLoc;
|
||||
Handle(Poly_Triangulation) aTriangulation = BRep_Tool::Triangulation(TopoDS::Face(aFace), aLoc);
|
||||
if (aTriangulation.IsNull()) {
|
||||
++aNbFacesNoTri;
|
||||
continue;
|
||||
}
|
||||
//BBS: copy nodes
|
||||
gp_Trsf aTrsf = aLoc.Transformation();
|
||||
for (Standard_Integer aNodeIter = 1; aNodeIter <= aTriangulation->NbNodes(); ++aNodeIter) {
|
||||
gp_Pnt aPnt = aTriangulation->Node(aNodeIter);
|
||||
aPnt.Transform(aTrsf);
|
||||
points.emplace_back(std::move(Vec3f(aPnt.X(), aPnt.Y(), aPnt.Z())));
|
||||
}
|
||||
//BBS: copy triangles
|
||||
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) {
|
||||
//BBS: swap 1, 2.
|
||||
Standard_Integer aTmpIdx = anId[1];
|
||||
anId[1] = anId[2];
|
||||
anId[2] = aTmpIdx;
|
||||
}
|
||||
//BBS: Update nodes according to the offset.
|
||||
anId[0] += aNodeOffset;
|
||||
anId[1] += aNodeOffset;
|
||||
anId[2] += aNodeOffset;
|
||||
//BBS: save triangles facets
|
||||
stl_facet facet;
|
||||
facet.vertex[0] = points[anId[0] - 1].cast<float>();
|
||||
facet.vertex[1] = points[anId[1] - 1].cast<float>();
|
||||
facet.vertex[2] = points[anId[2] - 1].cast<float>();
|
||||
facet.extra[0] = 0;
|
||||
facet.extra[1] = 0;
|
||||
stl_normal normal;
|
||||
stl_calculate_normal(normal, &facet);
|
||||
stl_normalize_vector(normal);
|
||||
facet.normal = normal;
|
||||
stl.facet_start[aTriangleOffet + aTriIter - 1] = facet;
|
||||
}
|
||||
|
||||
aNodeOffset += aTriangulation->NbNodes();
|
||||
aTriangleOffet += aTriangulation->NbTriangles();
|
||||
}
|
||||
|
||||
TriangleMesh triangle_mesh;
|
||||
triangle_mesh.from_stl(stl);
|
||||
ModelVolume* new_volume = new_object->add_volume(std::move(triangle_mesh));
|
||||
new_volume->name = namedSolids[i].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;
|
||||
}
|
||||
|
||||
shapeTool.reset(nullptr);
|
||||
application->Close(document);
|
||||
|
||||
//BBS: no valid shape from the step, delete the new object as well
|
||||
if (new_object->volumes.size() == 0) {
|
||||
model->delete_object(new_object);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
42
src/libslic3r/Format/STEP.hpp
Normal file
42
src/libslic3r/Format/STEP.hpp
Normal file
|
@ -0,0 +1,42 @@
|
|||
#ifndef slic3r_Format_STEP_hpp_
|
||||
#define slic3r_Format_STEP_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class ModelObject;
|
||||
|
||||
typedef std::function<void(int load_stage, int current, int total, bool& cancel)> ImportStepProgressFn;
|
||||
typedef std::function<void(bool isUtf8)> StepIsUtf8Fn;
|
||||
|
||||
//BBS: Load an step file into a provided model.
|
||||
extern bool load_step(const char *path, Model *model, ImportStepProgressFn proFn = nullptr, StepIsUtf8Fn isUtf8Fn = nullptr);
|
||||
|
||||
//BBS: Used to detect what kind of encoded type is used in name field of step
|
||||
// If is encoded in UTF8, the file don't need to be handled, then return the original path directly.
|
||||
// If is encoded in GBK, then translate to UTF8 and generate a new temporary step file.
|
||||
// If is encoded in Other type, we can't handled, then treat as UTF8. In this case, the name is garbage
|
||||
// characters.
|
||||
// By preprocessing, at least we can avoid garbage characters if the name field is encoded by GBK.
|
||||
class StepPreProcessor {
|
||||
enum class EncodedType : unsigned char
|
||||
{
|
||||
UTF8,
|
||||
GBK,
|
||||
OTHER
|
||||
};
|
||||
|
||||
public:
|
||||
bool preprocess(const char* path, std::string &output_path);
|
||||
static bool isUtf8File(const char* path);
|
||||
private:
|
||||
static bool isUtf8(const std::string str);
|
||||
static bool isGBK(const std::string str);
|
||||
static int preNum(const unsigned char byte);
|
||||
//BBS: default is UTF8 for most step file.
|
||||
EncodedType m_encode_type = EncodedType::UTF8;
|
||||
};
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_STEP_hpp_ */
|
62
src/libslic3r/Format/STL.cpp
Normal file
62
src/libslic3r/Format/STL.cpp
Normal file
|
@ -0,0 +1,62 @@
|
|||
#include "../libslic3r.h"
|
||||
#include "../Model.hpp"
|
||||
#include "../TriangleMesh.hpp"
|
||||
|
||||
#include "STL.hpp"
|
||||
|
||||
#include <string>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define DIR_SEPARATOR '\\'
|
||||
#else
|
||||
#define DIR_SEPARATOR '/'
|
||||
#endif
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
bool load_stl(const char *path, Model *model, const char *object_name_in)
|
||||
{
|
||||
TriangleMesh mesh;
|
||||
if (! mesh.ReadSTLFile(path)) {
|
||||
// die "Failed to open $file\n" if !-e $path;
|
||||
return false;
|
||||
}
|
||||
if (mesh.empty()) {
|
||||
// die "This STL file couldn't be read because it's empty.\n"
|
||||
return false;
|
||||
}
|
||||
|
||||
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 true;
|
||||
}
|
||||
|
||||
bool store_stl(const char *path, TriangleMesh *mesh, bool binary)
|
||||
{
|
||||
if (binary)
|
||||
mesh->write_binary(path);
|
||||
else
|
||||
mesh->write_ascii(path);
|
||||
//FIXME returning false even if write failed.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool store_stl(const char *path, ModelObject *model_object, bool binary)
|
||||
{
|
||||
TriangleMesh mesh = model_object->mesh();
|
||||
return store_stl(path, &mesh, binary);
|
||||
}
|
||||
|
||||
bool store_stl(const char *path, Model *model, bool binary)
|
||||
{
|
||||
TriangleMesh mesh = model->mesh();
|
||||
return store_stl(path, &mesh, binary);
|
||||
}
|
||||
|
||||
}; // namespace Slic3r
|
18
src/libslic3r/Format/STL.hpp
Normal file
18
src/libslic3r/Format/STL.hpp
Normal file
|
@ -0,0 +1,18 @@
|
|||
#ifndef slic3r_Format_STL_hpp_
|
||||
#define slic3r_Format_STL_hpp_
|
||||
|
||||
namespace Slic3r {
|
||||
|
||||
class TriangleMesh;
|
||||
class ModelObject;
|
||||
|
||||
// Load an STL file into a provided model.
|
||||
extern bool load_stl(const char *path, Model *model, const char *object_name = nullptr);
|
||||
|
||||
extern bool store_stl(const char *path, TriangleMesh *mesh, bool binary);
|
||||
extern bool store_stl(const char *path, ModelObject *model_object, bool binary);
|
||||
extern bool store_stl(const char *path, Model *model, bool binary);
|
||||
|
||||
}; // namespace Slic3r
|
||||
|
||||
#endif /* slic3r_Format_STL_hpp_ */
|
6161
src/libslic3r/Format/bbs_3mf.cpp
Normal file
6161
src/libslic3r/Format/bbs_3mf.cpp
Normal file
File diff suppressed because it is too large
Load diff
258
src/libslic3r/Format/bbs_3mf.hpp
Normal file
258
src/libslic3r/Format/bbs_3mf.hpp
Normal file
|
@ -0,0 +1,258 @@
|
|||
#ifndef BBS_3MF_hpp_
|
||||
#define BBS_3MF_hpp_
|
||||
|
||||
#include "../GCode/ThumbnailData.hpp"
|
||||
#include "libslic3r/ProjectTask.hpp"
|
||||
#include "libslic3r/GCode/GCodeProcessor.hpp"
|
||||
#include <functional>
|
||||
|
||||
namespace Slic3r {
|
||||
class Model;
|
||||
struct ConfigSubstitutionContext;
|
||||
class DynamicPrintConfig;
|
||||
class Preset;
|
||||
struct FilamentInfo;
|
||||
struct ThumbnailData;
|
||||
|
||||
#define GCODE_FILE_FORMAT "Metadata/plate_%1%.gcode"
|
||||
#define THUMBNAIL_FILE_FORMAT "Metadata/plate_%1%.png"
|
||||
#define PATTERN_FILE_FORMAT "Metadata/plate_%1%_pattern_layer_0.png"
|
||||
#define PATTERN_CONFIG_FILE_FORMAT "Metadata/plate_%1%.json"
|
||||
#define EMBEDDED_PRINT_FILE_FORMAT "Metadata/process_settings_%1%.config"
|
||||
#define EMBEDDED_FILAMENT_FILE_FORMAT "Metadata/filament_settings_%1%.config"
|
||||
#define EMBEDDED_PRINTER_FILE_FORMAT "Metadata/machine_settings_%1%.config"
|
||||
|
||||
|
||||
//BBS: define assistant struct to store temporary variable during exporting 3mf
|
||||
class PackingTemporaryData
|
||||
{
|
||||
public:
|
||||
std::string _3mf_thumbnail;
|
||||
std::string _3mf_printer_thumbnail_middle;
|
||||
std::string _3mf_printer_thumbnail_small;
|
||||
|
||||
PackingTemporaryData() {}
|
||||
};
|
||||
|
||||
|
||||
//BBS: define plate data list related structures
|
||||
struct PlateData
|
||||
{
|
||||
PlateData(int plate_id, std::set<std::pair<int, int>> &obj_to_inst_list, bool lock_state) : plate_index(plate_id), locked(lock_state)
|
||||
{
|
||||
objects_and_instances.clear();
|
||||
for (std::set<std::pair<int, int>>::iterator it = obj_to_inst_list.begin(); it != obj_to_inst_list.end(); ++it)
|
||||
objects_and_instances.emplace_back(it->first, it->second);
|
||||
}
|
||||
PlateData() : plate_index(-1), locked(false)
|
||||
{
|
||||
objects_and_instances.clear();
|
||||
}
|
||||
~PlateData()
|
||||
{
|
||||
objects_and_instances.clear();
|
||||
}
|
||||
|
||||
void parse_filament_info(GCodeProcessorResult *result);
|
||||
|
||||
int plate_index;
|
||||
std::vector<std::pair<int, int>> objects_and_instances;
|
||||
std::string gcode_file;
|
||||
std::string gcode_file_md5;
|
||||
std::string thumbnail_file;
|
||||
ThumbnailData plate_thumbnail;
|
||||
ThumbnailData pattern_thumbnail;
|
||||
std::string pattern_file;
|
||||
std::string gcode_prediction;
|
||||
std::string gcode_weight;
|
||||
std::vector<FilamentInfo> slice_flaments_info;
|
||||
bool is_sliced_valid = false;
|
||||
bool toolpath_outside {false};
|
||||
|
||||
std::string get_gcode_prediction_str() {
|
||||
return gcode_prediction;
|
||||
}
|
||||
|
||||
std::string get_gcode_weight_str() {
|
||||
return gcode_weight;
|
||||
}
|
||||
bool locked;
|
||||
};
|
||||
|
||||
// BBS: encrypt
|
||||
enum class SaveStrategy
|
||||
{
|
||||
Default = 0,
|
||||
FullPathSources = 1,
|
||||
Zip64 = 1 << 1,
|
||||
ProductionExt = 1 << 2,
|
||||
SecureContentExt = 1 << 3,
|
||||
WithGcode = 1 << 4,
|
||||
Silence = 1 << 5,
|
||||
SkipStatic = 1 << 6,
|
||||
SkipModel = 1 << 7,
|
||||
WithSliceInfo = 1 << 8,
|
||||
|
||||
SplitModel = 0x1000 | ProductionExt,
|
||||
Encrypted = SecureContentExt | SplitModel,
|
||||
Backup = 0x10000 | WithGcode | Silence | SkipStatic | SplitModel,
|
||||
};
|
||||
|
||||
inline SaveStrategy operator | (SaveStrategy lhs, SaveStrategy rhs)
|
||||
{
|
||||
using T = std::underlying_type_t <SaveStrategy>;
|
||||
return static_cast<SaveStrategy>(static_cast<T>(lhs) | static_cast<T>(rhs));
|
||||
}
|
||||
|
||||
inline bool operator & (SaveStrategy & lhs, SaveStrategy rhs)
|
||||
{
|
||||
using T = std::underlying_type_t <SaveStrategy>;
|
||||
return ((static_cast<T>(lhs) & static_cast<T>(rhs))) == static_cast<T>(rhs);
|
||||
}
|
||||
|
||||
enum class LoadStrategy
|
||||
{
|
||||
Default = 0,
|
||||
AddDefaultInstances = 1,
|
||||
CheckVersion = 2,
|
||||
LoadModel = 4,
|
||||
LoadConfig = 8,
|
||||
LoadAuxiliary = 16,
|
||||
Silence = 32,
|
||||
ImperialUnits = 64,
|
||||
|
||||
Restore = 0x10000 | LoadModel | LoadConfig | LoadAuxiliary | Silence,
|
||||
};
|
||||
|
||||
inline LoadStrategy operator | (LoadStrategy lhs, LoadStrategy rhs)
|
||||
{
|
||||
using T = std::underlying_type_t <LoadStrategy>;
|
||||
return static_cast<LoadStrategy>(static_cast<T>(lhs) | static_cast<T>(rhs));
|
||||
}
|
||||
|
||||
inline bool operator & (LoadStrategy & lhs, LoadStrategy rhs)
|
||||
{
|
||||
using T = std::underlying_type_t <LoadStrategy>;
|
||||
return (static_cast<T>(lhs) & static_cast<T>(rhs)) == static_cast<T>(rhs);
|
||||
}
|
||||
|
||||
const int EXPORT_STAGE_OPEN_3MF = 0;
|
||||
const int EXPORT_STAGE_CONTENT_TYPES = 1;
|
||||
const int EXPORT_STAGE_ADD_THUMBNAILS = 2;
|
||||
const int EXPORT_STAGE_ADD_RELATIONS = 3;
|
||||
const int EXPORT_STAGE_ADD_MODELS = 4;
|
||||
const int EXPORT_STAGE_ADD_LAYER_RANGE = 5;
|
||||
const int EXPORT_STAGE_ADD_SUPPORT = 6;
|
||||
const int EXPORT_STAGE_ADD_CUSTOM_GCODE = 7;
|
||||
const int EXPORT_STAGE_ADD_PRINT_CONFIG = 8;
|
||||
const int EXPORT_STAGE_ADD_PROJECT_CONFIG = 9;
|
||||
const int EXPORT_STAGE_ADD_CONFIG_FILE = 10;
|
||||
const int EXPORT_STAGE_ADD_SLICE_INFO = 11;
|
||||
const int EXPORT_STAGE_ADD_GCODE = 12;
|
||||
const int EXPORT_STAGE_ADD_AUXILIARIES = 13;
|
||||
const int EXPORT_STAGE_FINISH = 14;
|
||||
|
||||
const int IMPORT_STAGE_RESTORE = 0;
|
||||
const int IMPORT_STAGE_OPEN = 1;
|
||||
const int IMPORT_STAGE_READ_FILES = 2;
|
||||
const int IMPORT_STAGE_EXTRACT = 3;
|
||||
const int IMPORT_STAGE_LOADING_OBJECTS = 4;
|
||||
const int IMPORT_STAGE_LOADING_PLATES = 5;
|
||||
const int IMPORT_STAGE_FINISH = 6;
|
||||
const int IMPORT_STAGE_ADD_INSTANCE = 7;
|
||||
const int IMPORT_STAGE_UPDATE_GCODE = 8;
|
||||
const int IMPORT_STAGE_CHECK_MODE_GCODE = 9;
|
||||
const int UPDATE_GCODE_RESULT = 10;
|
||||
const int IMPORT_LOAD_CONFIG = 11;
|
||||
const int IMPORT_LOAD_MODEL_OBJECTS = 12;
|
||||
|
||||
//BBS export 3mf progress
|
||||
typedef std::function<void(int export_stage, int current, int total, bool& cancel)> Export3mfProgressFn;
|
||||
typedef std::function<void(int import_stage, int current, int total, bool& cancel)> Import3mfProgressFn;
|
||||
|
||||
typedef std::vector<PlateData*> PlateDataPtrs;
|
||||
|
||||
typedef std::map<int, PlateData*> PlateDataMaps;
|
||||
|
||||
struct StoreParams
|
||||
{
|
||||
const char* path;
|
||||
Model* model = nullptr;
|
||||
PlateDataPtrs plate_data_list;
|
||||
int export_plate_idx = -1;
|
||||
std::vector<Preset*> project_presets;
|
||||
DynamicPrintConfig* config;
|
||||
std::vector<ThumbnailData*> thumbnail_data;
|
||||
std::vector<ThumbnailData*> calibration_thumbnail_data;
|
||||
SaveStrategy strategy = SaveStrategy::Zip64;
|
||||
Export3mfProgressFn proFn = nullptr;
|
||||
std::vector<PlateBBoxData*> id_bboxes;
|
||||
BBLProject* project = nullptr;
|
||||
BBLProfile* profile = nullptr;
|
||||
|
||||
StoreParams() {}
|
||||
};
|
||||
|
||||
|
||||
//BBS: add plate data list related logic
|
||||
// add restore logic
|
||||
// Load the content of a 3mf file into the given model and preset bundle.
|
||||
extern bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector<Preset*>* project_presets, bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn = nullptr, LoadStrategy strategy = LoadStrategy::Default, BBLProject *project = nullptr);
|
||||
|
||||
extern std::string bbs_3mf_get_thumbnail(const char * path);
|
||||
|
||||
//BBS: add plate data list related logic
|
||||
// add backup logic
|
||||
// Save the given model and the config data contained in the given Print into a 3mf file.
|
||||
// The model could be modified during the export process if meshes are not repaired or have no shared vertices
|
||||
/*
|
||||
extern bool store_bbs_3mf(const char* path,
|
||||
Model* model,
|
||||
PlateDataPtrs& plate_data_list,
|
||||
std::vector<Preset*>& project_presets,
|
||||
const DynamicPrintConfig* config,
|
||||
bool fullpath_sources,
|
||||
const std::vector<ThumbnailData*>& thumbnail_data,
|
||||
bool zip64 = true,
|
||||
bool skip_static = false,
|
||||
Export3mfProgressFn proFn = nullptr,
|
||||
bool silence = true);
|
||||
*/
|
||||
|
||||
extern bool store_bbs_3mf(StoreParams& store_params);
|
||||
|
||||
extern void release_PlateData_list(PlateDataPtrs& plate_data_list);
|
||||
|
||||
// backup & restore project
|
||||
|
||||
extern void save_object_mesh(ModelObject& object);
|
||||
|
||||
extern void delete_object_mesh(ModelObject& object);
|
||||
|
||||
extern void backup_soon();
|
||||
|
||||
extern void remove_backup(Model& model, bool removeAll);
|
||||
|
||||
extern void set_backup_interval(long interval);
|
||||
|
||||
extern void set_backup_callback(std::function<void(int)> callback);
|
||||
|
||||
extern void run_backup_ui_tasks();
|
||||
|
||||
extern bool has_restore_data(std::string & path, std::string & origin);
|
||||
|
||||
extern void put_other_changes();
|
||||
|
||||
extern void clear_other_changes(bool backup);
|
||||
|
||||
extern bool has_other_changes(bool backup);
|
||||
|
||||
class SaveObjectGaurd {
|
||||
public:
|
||||
SaveObjectGaurd(ModelObject& object);
|
||||
~SaveObjectGaurd();
|
||||
};
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
#endif /* BBS_3MF_hpp_ */
|
588
src/libslic3r/Format/objparser.cpp
Normal file
588
src/libslic3r/Format/objparser.cpp
Normal file
|
@ -0,0 +1,588 @@
|
|||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <boost/nowide/cstdio.hpp>
|
||||
|
||||
#include "objparser.hpp"
|
||||
|
||||
#include "libslic3r/LocalesUtils.hpp"
|
||||
|
||||
namespace ObjParser {
|
||||
|
||||
static bool obj_parseline(const char *line, ObjData &data)
|
||||
{
|
||||
#define EATWS() while (*line == ' ' || *line == '\t') ++ line
|
||||
|
||||
if (*line == 0)
|
||||
return true;
|
||||
|
||||
assert(Slic3r::is_decimal_separator_point());
|
||||
|
||||
// Ignore whitespaces at the beginning of the line.
|
||||
//FIXME is this a good idea?
|
||||
EATWS();
|
||||
|
||||
char c1 = *line ++;
|
||||
switch (c1) {
|
||||
case '#':
|
||||
// Comment, ignore the rest of the line.
|
||||
break;
|
||||
case 'v':
|
||||
{
|
||||
// Parse vertex geometry (position, normal, texture coordinates)
|
||||
char c2 = *line ++;
|
||||
switch (c2) {
|
||||
case 't':
|
||||
{
|
||||
// vt - vertex texture parameter
|
||||
// u v [w], w == 0 (or w == 1)
|
||||
char c2 = *line ++;
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double u = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double v = 0;
|
||||
if (*line != 0) {
|
||||
v = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
}
|
||||
double w = 0;
|
||||
if (*line != 0) {
|
||||
w = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
}
|
||||
if (*line != 0)
|
||||
return false;
|
||||
data.textureCoordinates.push_back((float)u);
|
||||
data.textureCoordinates.push_back((float)v);
|
||||
data.textureCoordinates.push_back((float)w);
|
||||
break;
|
||||
}
|
||||
case 'n':
|
||||
{
|
||||
// vn - vertex normal
|
||||
// x y z
|
||||
char c2 = *line ++;
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double x = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double y = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double z = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
if (*line != 0)
|
||||
return false;
|
||||
data.normals.push_back((float)x);
|
||||
data.normals.push_back((float)y);
|
||||
data.normals.push_back((float)z);
|
||||
break;
|
||||
}
|
||||
case 'p':
|
||||
{
|
||||
// vp - vertex parameter
|
||||
char c2 = *line ++;
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double u = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double v = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double w = 0;
|
||||
if (*line != 0) {
|
||||
w = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
}
|
||||
if (*line != 0)
|
||||
return false;
|
||||
data.parameters.push_back((float)u);
|
||||
data.parameters.push_back((float)v);
|
||||
data.parameters.push_back((float)w);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
// v - vertex geometry
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
double x = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double y = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t'))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double z = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
double w = 1.0;
|
||||
if (*line != 0) {
|
||||
w = strtod(line, &endptr);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
}
|
||||
// the following check is commented out because there may be obj files containing extra data, as those generated by Meshlab,
|
||||
// see https://dev.prusa3d.com/browse/SPE-1019 for an example,
|
||||
// and this would lead to a crash because no vertex would be stored
|
||||
// if (*line != 0)
|
||||
// return false;
|
||||
data.coordinates.push_back((float)x);
|
||||
data.coordinates.push_back((float)y);
|
||||
data.coordinates.push_back((float)z);
|
||||
data.coordinates.push_back((float)w);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'f':
|
||||
{
|
||||
// face
|
||||
EATWS();
|
||||
if (*line == 0)
|
||||
return false;
|
||||
|
||||
// current vertex to be parsed
|
||||
ObjVertex vertex;
|
||||
char *endptr = 0;
|
||||
while (*line != 0) {
|
||||
// Parse a single vertex reference.
|
||||
vertex.coordIdx = 0;
|
||||
vertex.normalIdx = 0;
|
||||
vertex.textureCoordIdx = 0;
|
||||
vertex.coordIdx = strtol(line, &endptr, 10);
|
||||
// Coordinate has to be defined
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != '/' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
if (*line == '/') {
|
||||
++ line;
|
||||
// Texture coordinate index may be missing after a 1st slash, but then the normal index has to be present.
|
||||
if (*line != '/') {
|
||||
// Parse the texture coordinate index.
|
||||
vertex.textureCoordIdx = strtol(line, &endptr, 10);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != '/' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
}
|
||||
if (*line == '/') {
|
||||
// Parse normal index.
|
||||
++ line;
|
||||
vertex.normalIdx = strtol(line, &endptr, 10);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
}
|
||||
}
|
||||
if (vertex.coordIdx < 0)
|
||||
vertex.coordIdx += (int)data.coordinates.size() / 4;
|
||||
else
|
||||
-- vertex.coordIdx;
|
||||
if (vertex.normalIdx < 0)
|
||||
vertex.normalIdx += (int)data.normals.size() / 3;
|
||||
else
|
||||
-- vertex.normalIdx;
|
||||
if (vertex.textureCoordIdx < 0)
|
||||
vertex.textureCoordIdx += (int)data.textureCoordinates.size() / 3;
|
||||
else
|
||||
-- vertex.textureCoordIdx;
|
||||
data.vertices.push_back(vertex);
|
||||
EATWS();
|
||||
}
|
||||
vertex.coordIdx = -1;
|
||||
vertex.normalIdx = -1;
|
||||
vertex.textureCoordIdx = -1;
|
||||
data.vertices.push_back(vertex);
|
||||
break;
|
||||
}
|
||||
case 'm':
|
||||
{
|
||||
if (*(line ++) != 't' ||
|
||||
*(line ++) != 'l' ||
|
||||
*(line ++) != 'l' ||
|
||||
*(line ++) != 'i' ||
|
||||
*(line ++) != 'b')
|
||||
return false;
|
||||
// mtllib [external .mtl file name]
|
||||
// printf("mtllib %s\r\n", line);
|
||||
EATWS();
|
||||
data.mtllibs.push_back(std::string(line));
|
||||
break;
|
||||
}
|
||||
case 'u':
|
||||
{
|
||||
if (*(line ++) != 's' ||
|
||||
*(line ++) != 'e' ||
|
||||
*(line ++) != 'm' ||
|
||||
*(line ++) != 't' ||
|
||||
*(line ++) != 'l')
|
||||
return false;
|
||||
// usemtl [material name]
|
||||
// printf("usemtl %s\r\n", line);
|
||||
EATWS();
|
||||
ObjUseMtl usemtl;
|
||||
usemtl.vertexIdxFirst = (int)data.vertices.size();
|
||||
usemtl.name = line;
|
||||
data.usemtls.push_back(usemtl);
|
||||
break;
|
||||
}
|
||||
case 'o':
|
||||
{
|
||||
// o [object name]
|
||||
EATWS();
|
||||
while (*line != ' ' && *line != '\t' && *line != 0)
|
||||
++ line;
|
||||
// copy name to line.
|
||||
EATWS();
|
||||
if (*line != 0)
|
||||
return false;
|
||||
ObjObject object;
|
||||
object.vertexIdxFirst = (int)data.vertices.size();
|
||||
object.name = line;
|
||||
data.objects.push_back(object);
|
||||
break;
|
||||
}
|
||||
case 'g':
|
||||
{
|
||||
// g [group name]
|
||||
// printf("group %s\r\n", line);
|
||||
ObjGroup group;
|
||||
group.vertexIdxFirst = (int)data.vertices.size();
|
||||
group.name = line;
|
||||
data.groups.push_back(group);
|
||||
break;
|
||||
}
|
||||
case 's':
|
||||
{
|
||||
// s 1 / off
|
||||
char c2 = *line ++;
|
||||
if (c2 != ' ' && c2 != '\t')
|
||||
return false;
|
||||
EATWS();
|
||||
char *endptr = 0;
|
||||
long g = strtol(line, &endptr, 10);
|
||||
if (endptr == 0 || (*endptr != ' ' && *endptr != '\t' && *endptr != 0))
|
||||
return false;
|
||||
line = endptr;
|
||||
EATWS();
|
||||
if (*line != 0)
|
||||
return false;
|
||||
ObjSmoothingGroup group;
|
||||
group.vertexIdxFirst = (int)data.vertices.size();
|
||||
group.smoothingGroupID = g;
|
||||
data.smoothingGroups.push_back(group);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
BOOST_LOG_TRIVIAL(error) << "ObjParser: Unknown command: " << c1;
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool objparse(const char *path, ObjData &data)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
|
||||
FILE *pFile = boost::nowide::fopen(path, "rt");
|
||||
if (pFile == 0)
|
||||
return false;
|
||||
|
||||
try {
|
||||
char buf[65536 * 2];
|
||||
size_t len = 0;
|
||||
size_t lenPrev = 0;
|
||||
while ((len = ::fread(buf + lenPrev, 1, 65536, pFile)) != 0) {
|
||||
len += lenPrev;
|
||||
size_t lastLine = 0;
|
||||
for (size_t i = 0; i < len; ++ i)
|
||||
if (buf[i] == '\r' || buf[i] == '\n') {
|
||||
buf[i] = 0;
|
||||
char *c = buf + lastLine;
|
||||
while (*c == ' ' || *c == '\t')
|
||||
++ c;
|
||||
//FIXME check the return value and exit on error?
|
||||
// Will it break parsing of some obj files?
|
||||
obj_parseline(c, data);
|
||||
lastLine = i + 1;
|
||||
}
|
||||
lenPrev = len - lastLine;
|
||||
if (lenPrev > 65536) {
|
||||
BOOST_LOG_TRIVIAL(error) << "ObjParser: Excessive line length";
|
||||
::fclose(pFile);
|
||||
return false;
|
||||
}
|
||||
memmove(buf, buf + lastLine, lenPrev);
|
||||
}
|
||||
}
|
||||
catch (std::bad_alloc&) {
|
||||
BOOST_LOG_TRIVIAL(error) << "ObjParser: Out of memory";
|
||||
}
|
||||
::fclose(pFile);
|
||||
|
||||
// printf("vertices: %d\r\n", data.vertices.size() / 4);
|
||||
// printf("coords: %d\r\n", data.coordinates.size());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool objparse(std::istream &stream, ObjData &data)
|
||||
{
|
||||
Slic3r::CNumericLocalesSetter locales_setter;
|
||||
|
||||
try {
|
||||
char buf[65536 * 2];
|
||||
size_t len = 0;
|
||||
size_t lenPrev = 0;
|
||||
while ((len = size_t(stream.read(buf + lenPrev, 65536).gcount())) != 0) {
|
||||
len += lenPrev;
|
||||
size_t lastLine = 0;
|
||||
for (size_t i = 0; i < len; ++ i)
|
||||
if (buf[i] == '\r' || buf[i] == '\n') {
|
||||
buf[i] = 0;
|
||||
char *c = buf + lastLine;
|
||||
while (*c == ' ' || *c == '\t')
|
||||
++ c;
|
||||
obj_parseline(c, data);
|
||||
lastLine = i + 1;
|
||||
}
|
||||
lenPrev = len - lastLine;
|
||||
memmove(buf, buf + lastLine, lenPrev);
|
||||
}
|
||||
}
|
||||
catch (std::bad_alloc&) {
|
||||
BOOST_LOG_TRIVIAL(error) << "ObjParser: Out of memory";
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool savevector(FILE *pFile, const std::vector<T> &v)
|
||||
{
|
||||
size_t cnt = v.size();
|
||||
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
||||
//FIXME sizeof(T) works for data types leaving no gaps in the allocated vector because of alignment of the T type.
|
||||
if (! v.empty())
|
||||
::fwrite(&v.front(), 1, sizeof(T) * cnt, pFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool savevector(FILE *pFile, const std::vector<std::string> &v)
|
||||
{
|
||||
size_t cnt = v.size();
|
||||
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
||||
for (size_t i = 0; i < cnt; ++ i) {
|
||||
size_t len = v[i].size();
|
||||
::fwrite(&len, 1, sizeof(cnt), pFile);
|
||||
::fwrite(v[i].c_str(), 1, len, pFile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool savevectornameidx(FILE *pFile, const std::vector<T> &v)
|
||||
{
|
||||
size_t cnt = v.size();
|
||||
::fwrite(&cnt, 1, sizeof(cnt), pFile);
|
||||
for (size_t i = 0; i < cnt; ++ i) {
|
||||
::fwrite(&v[i].vertexIdxFirst, 1, sizeof(int), pFile);
|
||||
size_t len = v[i].name.size();
|
||||
::fwrite(&len, 1, sizeof(cnt), pFile);
|
||||
::fwrite(v[i].name.c_str(), 1, len, pFile);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool loadvector(FILE *pFile, std::vector<T> &v)
|
||||
{
|
||||
v.clear();
|
||||
size_t cnt = 0;
|
||||
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
||||
return false;
|
||||
//FIXME sizeof(T) works for data types leaving no gaps in the allocated vector because of alignment of the T type.
|
||||
if (cnt != 0) {
|
||||
v.assign(cnt, T());
|
||||
if (::fread(&v.front(), sizeof(T), cnt, pFile) != cnt)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool loadvector(FILE *pFile, std::vector<std::string> &v)
|
||||
{
|
||||
v.clear();
|
||||
size_t cnt = 0;
|
||||
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
||||
return false;
|
||||
v.reserve(cnt);
|
||||
for (size_t i = 0; i < cnt; ++ i) {
|
||||
size_t len = 0;
|
||||
if (::fread(&len, sizeof(len), 1, pFile) != 1)
|
||||
return false;
|
||||
std::string s(" ", len);
|
||||
if (::fread(s.data(), 1, len, pFile) != len)
|
||||
return false;
|
||||
v.push_back(std::move(s));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool loadvectornameidx(FILE *pFile, std::vector<T> &v)
|
||||
{
|
||||
v.clear();
|
||||
size_t cnt = 0;
|
||||
if (::fread(&cnt, sizeof(cnt), 1, pFile) != 1)
|
||||
return false;
|
||||
v.assign(cnt, T());
|
||||
for (size_t i = 0; i < cnt; ++ i) {
|
||||
if (::fread(&v[i].vertexIdxFirst, sizeof(int), 1, pFile) != 1)
|
||||
return false;
|
||||
size_t len = 0;
|
||||
if (::fread(&len, sizeof(len), 1, pFile) != 1)
|
||||
return false;
|
||||
v[i].name.assign(" ", len);
|
||||
if (::fread(v[i].name.data(), 1, len, pFile) != len)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool objbinsave(const char *path, const ObjData &data)
|
||||
{
|
||||
FILE *pFile = boost::nowide::fopen(path, "wb");
|
||||
if (pFile == 0)
|
||||
return false;
|
||||
|
||||
size_t version = 1;
|
||||
::fwrite(&version, 1, sizeof(version), pFile);
|
||||
|
||||
bool result =
|
||||
savevector(pFile, data.coordinates) &&
|
||||
savevector(pFile, data.textureCoordinates) &&
|
||||
savevector(pFile, data.normals) &&
|
||||
savevector(pFile, data.parameters) &&
|
||||
savevector(pFile, data.mtllibs) &&
|
||||
savevectornameidx(pFile, data.usemtls) &&
|
||||
savevectornameidx(pFile, data.objects) &&
|
||||
savevectornameidx(pFile, data.groups) &&
|
||||
savevector(pFile, data.smoothingGroups) &&
|
||||
savevector(pFile, data.vertices);
|
||||
|
||||
::fclose(pFile);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool objbinload(const char *path, ObjData &data)
|
||||
{
|
||||
FILE *pFile = boost::nowide::fopen(path, "rb");
|
||||
if (pFile == 0)
|
||||
return false;
|
||||
|
||||
data.version = 0;
|
||||
if (::fread(&data.version, sizeof(data.version), 1, pFile) != 1)
|
||||
return false;
|
||||
if (data.version != 1)
|
||||
return false;
|
||||
|
||||
bool result =
|
||||
loadvector(pFile, data.coordinates) &&
|
||||
loadvector(pFile, data.textureCoordinates) &&
|
||||
loadvector(pFile, data.normals) &&
|
||||
loadvector(pFile, data.parameters) &&
|
||||
loadvector(pFile, data.mtllibs) &&
|
||||
loadvectornameidx(pFile, data.usemtls) &&
|
||||
loadvectornameidx(pFile, data.objects) &&
|
||||
loadvectornameidx(pFile, data.groups) &&
|
||||
loadvector(pFile, data.smoothingGroups) &&
|
||||
loadvector(pFile, data.vertices);
|
||||
|
||||
::fclose(pFile);
|
||||
return result;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
bool vectorequal(const std::vector<T> &v1, const std::vector<T> &v2)
|
||||
{
|
||||
if (v1.size() != v2.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < v1.size(); ++ i)
|
||||
if (! (v1[i] == v2[i]))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool vectorequal(const std::vector<std::string> &v1, const std::vector<std::string> &v2)
|
||||
{
|
||||
if (v1.size() != v2.size())
|
||||
return false;
|
||||
for (size_t i = 0; i < v1.size(); ++ i)
|
||||
if (v1[i].compare(v2[i]) != 0)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
extern bool objequal(const ObjData &data1, const ObjData &data2)
|
||||
{
|
||||
//FIXME ignore version number
|
||||
// version;
|
||||
|
||||
return
|
||||
vectorequal(data1.coordinates, data2.coordinates) &&
|
||||
vectorequal(data1.textureCoordinates, data2.textureCoordinates) &&
|
||||
vectorequal(data1.normals, data2.normals) &&
|
||||
vectorequal(data1.parameters, data2.parameters) &&
|
||||
vectorequal(data1.mtllibs, data2.mtllibs) &&
|
||||
vectorequal(data1.usemtls, data2.usemtls) &&
|
||||
vectorequal(data1.objects, data2.objects) &&
|
||||
vectorequal(data1.groups, data2.groups) &&
|
||||
vectorequal(data1.vertices, data2.vertices);
|
||||
}
|
||||
|
||||
} // namespace ObjParser
|
111
src/libslic3r/Format/objparser.hpp
Normal file
111
src/libslic3r/Format/objparser.hpp
Normal file
|
@ -0,0 +1,111 @@
|
|||
#ifndef slic3r_Format_objparser_hpp_
|
||||
#define slic3r_Format_objparser_hpp_
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <istream>
|
||||
|
||||
namespace ObjParser {
|
||||
|
||||
struct ObjVertex
|
||||
{
|
||||
int coordIdx;
|
||||
int textureCoordIdx;
|
||||
int normalIdx;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjVertex &v1, const ObjVertex &v2)
|
||||
{
|
||||
return
|
||||
v1.coordIdx == v2.coordIdx &&
|
||||
v1.textureCoordIdx == v2.textureCoordIdx &&
|
||||
v1.normalIdx == v2.normalIdx;
|
||||
}
|
||||
|
||||
struct ObjUseMtl
|
||||
{
|
||||
int vertexIdxFirst;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjUseMtl &v1, const ObjUseMtl &v2)
|
||||
{
|
||||
return
|
||||
v1.vertexIdxFirst == v2.vertexIdxFirst &&
|
||||
v1.name.compare(v2.name) == 0;
|
||||
}
|
||||
|
||||
struct ObjObject
|
||||
{
|
||||
int vertexIdxFirst;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjObject &v1, const ObjObject &v2)
|
||||
{
|
||||
return
|
||||
v1.vertexIdxFirst == v2.vertexIdxFirst &&
|
||||
v1.name.compare(v2.name) == 0;
|
||||
}
|
||||
|
||||
struct ObjGroup
|
||||
{
|
||||
int vertexIdxFirst;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjGroup &v1, const ObjGroup &v2)
|
||||
{
|
||||
return
|
||||
v1.vertexIdxFirst == v2.vertexIdxFirst &&
|
||||
v1.name.compare(v2.name) == 0;
|
||||
}
|
||||
|
||||
struct ObjSmoothingGroup
|
||||
{
|
||||
int vertexIdxFirst;
|
||||
int smoothingGroupID;
|
||||
};
|
||||
|
||||
inline bool operator==(const ObjSmoothingGroup &v1, const ObjSmoothingGroup &v2)
|
||||
{
|
||||
return
|
||||
v1.vertexIdxFirst == v2.vertexIdxFirst &&
|
||||
v1.smoothingGroupID == v2.smoothingGroupID;
|
||||
}
|
||||
|
||||
struct ObjData {
|
||||
// Version of the data structure for load / store in the private binary format.
|
||||
int version;
|
||||
|
||||
// x, y, z, w
|
||||
std::vector<float> coordinates;
|
||||
// u, v, w
|
||||
std::vector<float> textureCoordinates;
|
||||
// x, y, z
|
||||
std::vector<float> normals;
|
||||
// u, v, w
|
||||
std::vector<float> parameters;
|
||||
|
||||
std::vector<std::string> mtllibs;
|
||||
std::vector<ObjUseMtl> usemtls;
|
||||
std::vector<ObjObject> objects;
|
||||
std::vector<ObjGroup> groups;
|
||||
std::vector<ObjSmoothingGroup> smoothingGroups;
|
||||
|
||||
// List of faces, delimited by an ObjVertex with all members set to -1.
|
||||
std::vector<ObjVertex> vertices;
|
||||
};
|
||||
|
||||
extern bool objparse(const char *path, ObjData &data);
|
||||
extern bool objparse(std::istream &stream, ObjData &data);
|
||||
|
||||
extern bool objbinsave(const char *path, const ObjData &data);
|
||||
|
||||
extern bool objbinload(const char *path, ObjData &data);
|
||||
|
||||
extern bool objequal(const ObjData &data1, const ObjData &data2);
|
||||
|
||||
} // namespace ObjParser
|
||||
|
||||
#endif /* slic3r_Format_objparser_hpp_ */
|
Loading…
Add table
Add a link
Reference in a new issue