mirror of
https://github.com/SoftFever/OrcaSlicer.git
synced 2025-07-18 04:08:02 -06:00
Rewrote .PRUSA file parser from wxWidgets zip to miniz.
Added tracing for mesh repair.
This commit is contained in:
parent
85bc3af88a
commit
8945763221
5 changed files with 327 additions and 353 deletions
|
@ -1,12 +1,10 @@
|
||||||
#ifdef SLIC3R_PRUS
|
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <exception>
|
||||||
|
|
||||||
|
#include <boost/algorithm/string.hpp>
|
||||||
#include <boost/nowide/convert.hpp>
|
#include <boost/nowide/convert.hpp>
|
||||||
|
|
||||||
#include <wx/string.h>
|
#include <miniz/miniz_zip.h>
|
||||||
#include <wx/wfstream.h>
|
|
||||||
#include <wx/zipstrm.h>
|
|
||||||
|
|
||||||
#include <Eigen/Geometry>
|
#include <Eigen/Geometry>
|
||||||
|
|
||||||
|
@ -35,64 +33,28 @@ struct StlHeader
|
||||||
|
|
||||||
static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct");
|
static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct");
|
||||||
|
|
||||||
// Buffered line reader for the wxInputStream.
|
// Buffered line reader to a string buffer.
|
||||||
class LineReader
|
class LineReader
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
LineReader(wxInputStream &input_stream, const char *initial_data, int initial_len) :
|
LineReader(std::vector<char> &data) : m_buffer(data), m_pos(0), m_len(data.size()) {}
|
||||||
m_input_stream(input_stream),
|
|
||||||
m_pos(0),
|
|
||||||
m_len(initial_len)
|
|
||||||
{
|
|
||||||
assert(initial_len >= 0 && initial_len < m_bufsize);
|
|
||||||
memcpy(m_buffer, initial_data, initial_len);
|
|
||||||
}
|
|
||||||
|
|
||||||
const char* next_line() {
|
const char* next_line() {
|
||||||
for (;;) {
|
// Skip empty lines.
|
||||||
// Skip empty lines.
|
while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n'))
|
||||||
while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n'))
|
++ m_pos;
|
||||||
++ m_pos;
|
if (m_pos == m_len) {
|
||||||
if (m_pos == m_len) {
|
// End of file.
|
||||||
// Empty buffer, fill it from the input stream.
|
return nullptr;
|
||||||
m_pos = 0;
|
|
||||||
m_input_stream.Read(m_buffer, m_bufsize - 1);
|
|
||||||
m_len = m_input_stream.LastRead();
|
|
||||||
assert(m_len >= 0 && m_len < m_bufsize);
|
|
||||||
if (m_len == 0)
|
|
||||||
// End of file.
|
|
||||||
return nullptr;
|
|
||||||
// Skip empty lines etc.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// The buffer is nonempty and it does not start with end of lines. Find the first end of line.
|
|
||||||
int end = m_pos + 1;
|
|
||||||
while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n')
|
|
||||||
++ end;
|
|
||||||
if (end == m_len && ! m_input_stream.Eof() && m_len < m_bufsize) {
|
|
||||||
// Move the buffer content to the buffer start and fill the rest of the buffer.
|
|
||||||
assert(m_pos > 0);
|
|
||||||
memmove(m_buffer, m_buffer + m_pos, m_len - m_pos);
|
|
||||||
m_len -= m_pos;
|
|
||||||
assert(m_len >= 0 && m_len < m_bufsize);
|
|
||||||
m_pos = 0;
|
|
||||||
m_input_stream.Read(m_buffer + m_len, m_bufsize - 1 - m_len);
|
|
||||||
int new_data = m_input_stream.LastRead();
|
|
||||||
if (new_data > 0) {
|
|
||||||
m_len += new_data;
|
|
||||||
assert(m_len >= 0 && m_len < m_bufsize);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
char *ptr_out = m_buffer + m_pos;
|
|
||||||
m_pos = end + 1;
|
|
||||||
m_buffer[end] = 0;
|
|
||||||
if (m_pos >= m_len) {
|
|
||||||
m_pos = 0;
|
|
||||||
m_len = 0;
|
|
||||||
}
|
|
||||||
return ptr_out;
|
|
||||||
}
|
}
|
||||||
|
// The buffer is nonempty and it does not start with end of lines. Find the first end of line.
|
||||||
|
int end = m_pos + 1;
|
||||||
|
while (end < m_len && m_buffer[end] != '\r' && m_buffer[end] != '\n')
|
||||||
|
++ end;
|
||||||
|
char *ptr_out = m_buffer.data() + m_pos;
|
||||||
|
m_pos = end + 1;
|
||||||
|
m_buffer[end] = 0;
|
||||||
|
return ptr_out;
|
||||||
}
|
}
|
||||||
|
|
||||||
int next_line_scanf(const char *format, ...)
|
int next_line_scanf(const char *format, ...)
|
||||||
|
@ -109,303 +71,314 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
wxInputStream &m_input_stream;
|
std::vector<char> &m_buffer;
|
||||||
static const int m_bufsize = 4096;
|
int m_pos;
|
||||||
char m_buffer[m_bufsize];
|
int m_len;
|
||||||
int m_pos = 0;
|
|
||||||
int m_len = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static void extract_model_from_archive(
|
||||||
|
// name of the model file
|
||||||
|
const char *name,
|
||||||
|
// path to the archive
|
||||||
|
const char *path,
|
||||||
|
// content of scene.xml
|
||||||
|
const std::vector<char> &scene_xml_data,
|
||||||
|
// loaded data of this STL
|
||||||
|
std::vector<char> &data,
|
||||||
|
// Model, to which the newly loaded objects will be added
|
||||||
|
Model *model,
|
||||||
|
// To map multiple STLs into a single model object for multi-material prints.
|
||||||
|
std::map<int, ModelObject*> &group_to_model_object)
|
||||||
|
{
|
||||||
|
// Find the model entry in the XML data.
|
||||||
|
char model_name_tag[1024];
|
||||||
|
sprintf(model_name_tag, "<model name=\"%s\">", name);
|
||||||
|
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
||||||
|
const char *zero_tag = "<zero>";
|
||||||
|
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
|
||||||
|
float trafo[3][4] = { 0 };
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_ROTATION
|
||||||
|
Vec3d instance_rotation = Vec3d::Zero();
|
||||||
|
#else
|
||||||
|
double instance_rotation = 0.;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
|
||||||
|
double instance_scaling_factor = 1.f;
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
Vec3d instance_offset = Vec3d::Zero();
|
||||||
|
#else
|
||||||
|
Vec2d instance_offset(0., 0.);
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
bool trafo_set = false;
|
||||||
|
unsigned int group_id = (unsigned int)-1;
|
||||||
|
unsigned int extruder_id = (unsigned int)-1;
|
||||||
|
ModelObject *model_object = nullptr;
|
||||||
|
if (model_xml != nullptr) {
|
||||||
|
model_xml += strlen(model_name_tag);
|
||||||
|
const char *position_tag = "<position>";
|
||||||
|
const char *position_xml = strstr(model_xml, position_tag);
|
||||||
|
const char *rotation_tag = "<rotation>";
|
||||||
|
const char *rotation_xml = strstr(model_xml, rotation_tag);
|
||||||
|
const char *scale_tag = "<scale>";
|
||||||
|
const char *scale_xml = strstr(model_xml, scale_tag);
|
||||||
|
float position[3], rotation[3], scale[3], zero[3];
|
||||||
|
if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr &&
|
||||||
|
sscanf(position_xml+strlen(position_tag),
|
||||||
|
"[%f, %f, %f]", position, position+1, position+2) == 3 &&
|
||||||
|
sscanf(rotation_xml+strlen(rotation_tag),
|
||||||
|
"[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 &&
|
||||||
|
sscanf(scale_xml+strlen(scale_tag),
|
||||||
|
"[%f, %f, %f]", scale, scale+1, scale+2) == 3 &&
|
||||||
|
sscanf(zero_xml+strlen(zero_tag),
|
||||||
|
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
|
||||||
|
if (scale[0] == scale[1] && scale[1] == scale[2]) {
|
||||||
|
instance_scaling_factor = scale[0];
|
||||||
|
scale[0] = scale[1] = scale[2] = 1.;
|
||||||
|
}
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_ROTATION
|
||||||
|
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
|
||||||
|
#else
|
||||||
|
if (rotation[0] == 0. && rotation[1] == 0.) {
|
||||||
|
instance_rotation = - rotation[2];
|
||||||
|
rotation[2] = 0.;
|
||||||
|
}
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
|
||||||
|
Eigen::Matrix3f mat_rot, mat_scale, mat_trafo;
|
||||||
|
mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) *
|
||||||
|
Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) *
|
||||||
|
Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX());
|
||||||
|
mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]);
|
||||||
|
mat_trafo = mat_rot * mat_scale;
|
||||||
|
for (size_t r = 0; r < 3; ++ r) {
|
||||||
|
for (size_t c = 0; c < 3; ++ c)
|
||||||
|
trafo[r][c] += mat_trafo(r, c);
|
||||||
|
}
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
|
||||||
|
#else
|
||||||
|
instance_offset(0) = position[0] - zero[0];
|
||||||
|
instance_offset(1) = position[1] - zero[1];
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
trafo[2][3] = position[2] / instance_scaling_factor;
|
||||||
|
trafo_set = true;
|
||||||
|
}
|
||||||
|
const char *group_tag = "<group>";
|
||||||
|
const char *group_xml = strstr(model_xml, group_tag);
|
||||||
|
const char *extruder_tag = "<extruder>";
|
||||||
|
const char *extruder_xml = strstr(model_xml, extruder_tag);
|
||||||
|
if (group_xml != nullptr) {
|
||||||
|
int group = atoi(group_xml + strlen(group_tag));
|
||||||
|
if (group > 0) {
|
||||||
|
group_id = group;
|
||||||
|
auto it = group_to_model_object.find(group_id);
|
||||||
|
if (it != group_to_model_object.end())
|
||||||
|
model_object = it->second;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (extruder_xml != nullptr) {
|
||||||
|
int e = atoi(extruder_xml + strlen(extruder_tag));
|
||||||
|
if (e > 0)
|
||||||
|
extruder_id = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (! trafo_set)
|
||||||
|
throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid entry in scene.xml for " + name);
|
||||||
|
|
||||||
|
// Extract the STL.
|
||||||
|
StlHeader header;
|
||||||
|
TriangleMesh mesh;
|
||||||
|
bool mesh_valid = false;
|
||||||
|
bool stl_ascii = false;
|
||||||
|
if (data.size() > sizeof(StlHeader)) {
|
||||||
|
memcpy((char*)&header, data.data(), sizeof(StlHeader));
|
||||||
|
if (strncmp(header.comment, "solid ", 6) == 0)
|
||||||
|
stl_ascii = true;
|
||||||
|
else {
|
||||||
|
// Header has been extracted. Now read the faces.
|
||||||
|
stl_file &stl = mesh.stl;
|
||||||
|
stl.error = 0;
|
||||||
|
stl.stats.type = inmemory;
|
||||||
|
stl.stats.number_of_facets = header.nTriangles;
|
||||||
|
stl.stats.original_num_facets = header.nTriangles;
|
||||||
|
stl_allocate(&stl);
|
||||||
|
if (header.nTriangles > 0 && data.size() == 50 * header.nTriangles + sizeof(StlHeader)) {
|
||||||
|
memcpy((char*)stl.facet_start, data.data() + sizeof(StlHeader), 50 * header.nTriangles);
|
||||||
|
if (sizeof(stl_facet) > SIZEOF_STL_FACET) {
|
||||||
|
// The stl.facet_start is not packed tightly. Unpack the array of stl_facets.
|
||||||
|
unsigned char *data = (unsigned char*)stl.facet_start;
|
||||||
|
for (size_t i = header.nTriangles - 1; i > 0; -- i)
|
||||||
|
memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET);
|
||||||
|
}
|
||||||
|
// All the faces have been read.
|
||||||
|
stl_get_size(&stl);
|
||||||
|
mesh.repair();
|
||||||
|
// Transform the model.
|
||||||
|
stl_transform(&stl, &trafo[0][0]);
|
||||||
|
if (std::abs(stl.stats.min(2)) < EPSILON)
|
||||||
|
stl.stats.min(2) = 0.;
|
||||||
|
// Add a mesh to a model.
|
||||||
|
if (mesh.facets_count() > 0)
|
||||||
|
mesh_valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
stl_ascii = true;
|
||||||
|
|
||||||
|
if (stl_ascii) {
|
||||||
|
// Try to parse ASCII STL.
|
||||||
|
char normal_buf[3][32];
|
||||||
|
stl_facet facet;
|
||||||
|
std::vector<stl_facet> facets;
|
||||||
|
LineReader line_reader(data);
|
||||||
|
std::string solid_name;
|
||||||
|
facet.extra[0] = facet.extra[1] = 0;
|
||||||
|
for (;;) {
|
||||||
|
const char *line = line_reader.next_line();
|
||||||
|
if (line == nullptr)
|
||||||
|
// End of file.
|
||||||
|
break;
|
||||||
|
if (strncmp(line, "solid", 5) == 0) {
|
||||||
|
// Opening the "solid" block.
|
||||||
|
if (! solid_name.empty()) {
|
||||||
|
// Error, solid block is already open.
|
||||||
|
facets.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
solid_name = line + 5;
|
||||||
|
if (solid_name.empty())
|
||||||
|
solid_name = "unknown";
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (strncmp(line, "endsolid", 8) == 0) {
|
||||||
|
// Closing the "solid" block.
|
||||||
|
if (solid_name.empty()) {
|
||||||
|
// Error, no solid block is open.
|
||||||
|
facets.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
solid_name.clear();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// Line has to start with the word solid.
|
||||||
|
int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
|
||||||
|
assert(res_normal == 3);
|
||||||
|
int res_outer_loop = line_reader.next_line_scanf(" outer loop");
|
||||||
|
assert(res_outer_loop == 0);
|
||||||
|
int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
|
||||||
|
assert(res_vertex1 == 3);
|
||||||
|
int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
|
||||||
|
assert(res_vertex2 == 3);
|
||||||
|
int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
|
||||||
|
assert(res_vertex3 == 3);
|
||||||
|
int res_endloop = line_reader.next_line_scanf(" endloop");
|
||||||
|
assert(res_endloop == 0);
|
||||||
|
int res_endfacet = line_reader.next_line_scanf(" endfacet");
|
||||||
|
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
|
||||||
|
// perror("Something is syntactically very wrong with this ASCII STL!");
|
||||||
|
facets.clear();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
|
||||||
|
if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
|
||||||
|
sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
|
||||||
|
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
|
||||||
|
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
||||||
|
// Just reset the normal and silently ignore it.
|
||||||
|
memset(&facet.normal, 0, sizeof(facet.normal));
|
||||||
|
}
|
||||||
|
facets.emplace_back(facet);
|
||||||
|
}
|
||||||
|
if (! facets.empty() && solid_name.empty()) {
|
||||||
|
stl_file &stl = mesh.stl;
|
||||||
|
stl.stats.type = inmemory;
|
||||||
|
stl.stats.number_of_facets = facets.size();
|
||||||
|
stl.stats.original_num_facets = facets.size();
|
||||||
|
stl_allocate(&stl);
|
||||||
|
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50);
|
||||||
|
stl_get_size(&stl);
|
||||||
|
mesh.repair();
|
||||||
|
// Transform the model.
|
||||||
|
stl_transform(&stl, &trafo[0][0]);
|
||||||
|
// Add a mesh to a model.
|
||||||
|
if (mesh.facets_count() > 0)
|
||||||
|
mesh_valid = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (! mesh_valid)
|
||||||
|
throw std::runtime_error(std::string("Archive ") + path + " does not contain a valid mesh for " + name);
|
||||||
|
|
||||||
|
// Add this mesh to the model.
|
||||||
|
ModelVolume *volume = nullptr;
|
||||||
|
if (model_object == nullptr) {
|
||||||
|
// This is a first mesh of a group. Create a new object & volume.
|
||||||
|
model_object = model->add_object(name, path, std::move(mesh));
|
||||||
|
volume = model_object->volumes.front();
|
||||||
|
ModelInstance *instance = model_object->add_instance();
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_ROTATION
|
||||||
|
instance->set_rotation(instance_rotation);
|
||||||
|
#else
|
||||||
|
instance->rotation = instance_rotation;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
|
||||||
|
instance->scaling_factor = instance_scaling_factor;
|
||||||
|
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
instance->set_offset(instance_offset);
|
||||||
|
#else
|
||||||
|
instance->offset = instance_offset;
|
||||||
|
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
||||||
|
if (group_id != (size_t)-1)
|
||||||
|
group_to_model_object[group_id] = model_object;
|
||||||
|
} else {
|
||||||
|
// This is not the 1st mesh of a group. Add it to the ModelObject.
|
||||||
|
volume = model_object->add_volume(std::move(mesh));
|
||||||
|
volume->name = name;
|
||||||
|
}
|
||||||
|
// Set the extruder to the volume.
|
||||||
|
if (extruder_id != (unsigned int)-1) {
|
||||||
|
char str_extruder[64];
|
||||||
|
sprintf(str_extruder, "%ud", extruder_id);
|
||||||
|
volume->config.set_deserialize("extruder", str_extruder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Load a PrusaControl project file into a provided model.
|
// Load a PrusaControl project file into a provided model.
|
||||||
bool load_prus(const char *path, Model *model)
|
bool load_prus(const char *path, Model *model)
|
||||||
{
|
{
|
||||||
// To receive the content of the zipped 'scene.xml' file.
|
mz_zip_archive archive;
|
||||||
std::vector<char> scene_xml_data;
|
mz_zip_zero_struct(&archive);
|
||||||
wxFFileInputStream in(
|
mz_bool res = mz_zip_reader_init_file(&archive, path, 0);
|
||||||
#ifdef WIN32
|
size_t n_models_initial = model->objects.size();
|
||||||
// On Windows, convert to a 16bit unicode string.
|
try {
|
||||||
boost::nowide::widen(path).c_str()
|
if (res == MZ_FALSE)
|
||||||
#else
|
throw std::runtime_error(std::string("Unable to init zip reader for ") + path);
|
||||||
path
|
std::vector<char> scene_xml_data;
|
||||||
#endif
|
// For grouping multiple STLs into a single ModelObject for multi-material prints.
|
||||||
);
|
std::map<int, ModelObject*> group_to_model_object;
|
||||||
wxZipInputStream zip(in);
|
mz_uint num_entries = mz_zip_reader_get_num_files(&archive);
|
||||||
std::unique_ptr<wxZipEntry> entry;
|
for (mz_uint i = 0; i < num_entries; ++ i) {
|
||||||
size_t num_models = 0;
|
mz_zip_archive_file_stat stat;
|
||||||
std::map<int, ModelObject*> group_to_model_object;
|
if (! mz_zip_reader_file_stat(&archive, i, &stat))
|
||||||
while (entry.reset(zip.GetNextEntry()), entry.get() != NULL) {
|
continue;
|
||||||
wxString name = entry->GetName();
|
std::vector<char> buffer;
|
||||||
if (name == "scene.xml") {
|
buffer.assign((size_t)stat.m_uncomp_size + 1, 0);
|
||||||
if (! scene_xml_data.empty()) {
|
res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0);
|
||||||
// scene.xml has been found more than once in the archive.
|
if (res == MZ_FALSE)
|
||||||
return false;
|
std::runtime_error(std::string("Error while extracting a file from ") + path);
|
||||||
}
|
if (strcmp(stat.m_filename, "scene.xml") == 0) {
|
||||||
size_t size_last = 0;
|
if (! scene_xml_data.empty())
|
||||||
size_t size_incr = 4096;
|
throw std::runtime_error(std::string("Multiple scene.xml were found in the archive.") + path);
|
||||||
scene_xml_data.resize(size_incr);
|
scene_xml_data = std::move(buffer);
|
||||||
while (! zip.Read(scene_xml_data.data() + size_last, size_incr).Eof()) {
|
} else if (boost::iends_with(stat.m_filename, ".stl")) {
|
||||||
size_last += zip.LastRead();
|
// May throw std::exception
|
||||||
if (scene_xml_data.size() < size_last + size_incr)
|
extract_model_from_archive(stat.m_filename, path, scene_xml_data, buffer, model, group_to_model_object);
|
||||||
scene_xml_data.resize(size_last + size_incr);
|
|
||||||
}
|
|
||||||
size_last += zip.LastRead();
|
|
||||||
if (scene_xml_data.size() == size_last)
|
|
||||||
scene_xml_data.resize(size_last + 1);
|
|
||||||
else if (scene_xml_data.size() > size_last + 1)
|
|
||||||
scene_xml_data.erase(scene_xml_data.begin() + size_last + 1, scene_xml_data.end());
|
|
||||||
scene_xml_data[size_last] = 0;
|
|
||||||
}
|
|
||||||
else if (name.EndsWith(".stl") || name.EndsWith(".STL")) {
|
|
||||||
// Find the model entry in the XML data.
|
|
||||||
const wxScopedCharBuffer name_utf8 = name.ToUTF8();
|
|
||||||
char model_name_tag[1024];
|
|
||||||
sprintf(model_name_tag, "<model name=\"%s\">", name_utf8.data());
|
|
||||||
const char *model_xml = strstr(scene_xml_data.data(), model_name_tag);
|
|
||||||
const char *zero_tag = "<zero>";
|
|
||||||
const char *zero_xml = strstr(scene_xml_data.data(), zero_tag);
|
|
||||||
float trafo[3][4] = { 0 };
|
|
||||||
#if ENABLE_MODELINSTANCE_3D_ROTATION
|
|
||||||
Vec3d instance_rotation = Vec3d::Zero();
|
|
||||||
#else
|
|
||||||
double instance_rotation = 0.;
|
|
||||||
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
|
|
||||||
double instance_scaling_factor = 1.f;
|
|
||||||
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
|
||||||
Vec3d instance_offset = Vec3d::Zero();
|
|
||||||
#else
|
|
||||||
Vec2d instance_offset(0., 0.);
|
|
||||||
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
|
||||||
bool trafo_set = false;
|
|
||||||
unsigned int group_id = (unsigned int)-1;
|
|
||||||
unsigned int extruder_id = (unsigned int)-1;
|
|
||||||
ModelObject *model_object = nullptr;
|
|
||||||
if (model_xml != nullptr) {
|
|
||||||
model_xml += strlen(model_name_tag);
|
|
||||||
const char *position_tag = "<position>";
|
|
||||||
const char *position_xml = strstr(model_xml, position_tag);
|
|
||||||
const char *rotation_tag = "<rotation>";
|
|
||||||
const char *rotation_xml = strstr(model_xml, rotation_tag);
|
|
||||||
const char *scale_tag = "<scale>";
|
|
||||||
const char *scale_xml = strstr(model_xml, scale_tag);
|
|
||||||
float position[3], rotation[3], scale[3], zero[3];
|
|
||||||
if (position_xml != nullptr && rotation_xml != nullptr && scale_xml != nullptr && zero_xml != nullptr &&
|
|
||||||
sscanf(position_xml+strlen(position_tag),
|
|
||||||
"[%f, %f, %f]", position, position+1, position+2) == 3 &&
|
|
||||||
sscanf(rotation_xml+strlen(rotation_tag),
|
|
||||||
"[%f, %f, %f]", rotation, rotation+1, rotation+2) == 3 &&
|
|
||||||
sscanf(scale_xml+strlen(scale_tag),
|
|
||||||
"[%f, %f, %f]", scale, scale+1, scale+2) == 3 &&
|
|
||||||
sscanf(zero_xml+strlen(zero_tag),
|
|
||||||
"[%f, %f, %f]", zero, zero+1, zero+2) == 3) {
|
|
||||||
if (scale[0] == scale[1] && scale[1] == scale[2]) {
|
|
||||||
instance_scaling_factor = scale[0];
|
|
||||||
scale[0] = scale[1] = scale[2] = 1.;
|
|
||||||
}
|
|
||||||
#if ENABLE_MODELINSTANCE_3D_ROTATION
|
|
||||||
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
|
|
||||||
#else
|
|
||||||
if (rotation[0] == 0. && rotation[1] == 0.) {
|
|
||||||
instance_rotation = - rotation[2];
|
|
||||||
rotation[2] = 0.;
|
|
||||||
}
|
|
||||||
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
|
|
||||||
Eigen::Matrix3f mat_rot, mat_scale, mat_trafo;
|
|
||||||
mat_rot = Eigen::AngleAxisf(-rotation[2], Eigen::Vector3f::UnitZ()) *
|
|
||||||
Eigen::AngleAxisf(-rotation[1], Eigen::Vector3f::UnitY()) *
|
|
||||||
Eigen::AngleAxisf(-rotation[0], Eigen::Vector3f::UnitX());
|
|
||||||
mat_scale = Eigen::Scaling(scale[0], scale[1], scale[2]);
|
|
||||||
mat_trafo = mat_rot * mat_scale;
|
|
||||||
for (size_t r = 0; r < 3; ++ r) {
|
|
||||||
for (size_t c = 0; c < 3; ++ c)
|
|
||||||
trafo[r][c] += mat_trafo(r, c);
|
|
||||||
}
|
|
||||||
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
|
||||||
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
|
|
||||||
#else
|
|
||||||
instance_offset(0) = position[0] - zero[0];
|
|
||||||
instance_offset(1) = position[1] - zero[1];
|
|
||||||
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
|
||||||
trafo[2][3] = position[2] / instance_scaling_factor;
|
|
||||||
trafo_set = true;
|
|
||||||
}
|
|
||||||
const char *group_tag = "<group>";
|
|
||||||
const char *group_xml = strstr(model_xml, group_tag);
|
|
||||||
const char *extruder_tag = "<extruder>";
|
|
||||||
const char *extruder_xml = strstr(model_xml, extruder_tag);
|
|
||||||
if (group_xml != nullptr) {
|
|
||||||
int group = atoi(group_xml + strlen(group_tag));
|
|
||||||
if (group > 0) {
|
|
||||||
group_id = group;
|
|
||||||
auto it = group_to_model_object.find(group_id);
|
|
||||||
if (it != group_to_model_object.end())
|
|
||||||
model_object = it->second;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (extruder_xml != nullptr) {
|
|
||||||
int e = atoi(extruder_xml + strlen(extruder_tag));
|
|
||||||
if (e > 0)
|
|
||||||
extruder_id = e;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (trafo_set) {
|
|
||||||
// Extract the STL.
|
|
||||||
StlHeader header;
|
|
||||||
TriangleMesh mesh;
|
|
||||||
bool mesh_valid = false;
|
|
||||||
bool stl_ascii = false;
|
|
||||||
if (!zip.Read((void*)&header, sizeof(StlHeader)).Eof()) {
|
|
||||||
if (strncmp(header.comment, "solid ", 6) == 0)
|
|
||||||
stl_ascii = true;
|
|
||||||
else {
|
|
||||||
// Header has been extracted. Now read the faces.
|
|
||||||
stl_file &stl = mesh.stl;
|
|
||||||
stl.error = 0;
|
|
||||||
stl.stats.type = inmemory;
|
|
||||||
stl.stats.number_of_facets = header.nTriangles;
|
|
||||||
stl.stats.original_num_facets = header.nTriangles;
|
|
||||||
stl_allocate(&stl);
|
|
||||||
if (header.nTriangles > 0 && zip.ReadAll((void*)stl.facet_start, 50 * header.nTriangles)) {
|
|
||||||
if (sizeof(stl_facet) > SIZEOF_STL_FACET) {
|
|
||||||
// The stl.facet_start is not packed tightly. Unpack the array of stl_facets.
|
|
||||||
unsigned char *data = (unsigned char*)stl.facet_start;
|
|
||||||
for (size_t i = header.nTriangles - 1; i > 0; -- i)
|
|
||||||
memmove(data + i * sizeof(stl_facet), data + i * SIZEOF_STL_FACET, SIZEOF_STL_FACET);
|
|
||||||
}
|
|
||||||
// All the faces have been read.
|
|
||||||
stl_get_size(&stl);
|
|
||||||
mesh.repair();
|
|
||||||
// Transform the model.
|
|
||||||
stl_transform(&stl, &trafo[0][0]);
|
|
||||||
if (std::abs(stl.stats.min(2)) < EPSILON)
|
|
||||||
stl.stats.min(2) = 0.;
|
|
||||||
// Add a mesh to a model.
|
|
||||||
if (mesh.facets_count() > 0)
|
|
||||||
mesh_valid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
stl_ascii = true;
|
|
||||||
if (stl_ascii) {
|
|
||||||
// Try to parse ASCII STL.
|
|
||||||
char normal_buf[3][32];
|
|
||||||
stl_facet facet;
|
|
||||||
std::vector<stl_facet> facets;
|
|
||||||
LineReader line_reader(zip, (char*)&header, zip.LastRead());
|
|
||||||
std::string solid_name;
|
|
||||||
facet.extra[0] = facet.extra[1] = 0;
|
|
||||||
for (;;) {
|
|
||||||
const char *line = line_reader.next_line();
|
|
||||||
if (line == nullptr)
|
|
||||||
// End of file.
|
|
||||||
break;
|
|
||||||
if (strncmp(line, "solid", 5) == 0) {
|
|
||||||
// Opening the "solid" block.
|
|
||||||
if (! solid_name.empty()) {
|
|
||||||
// Error, solid block is already open.
|
|
||||||
facets.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
solid_name = line + 5;
|
|
||||||
if (solid_name.empty())
|
|
||||||
solid_name = "unknown";
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
if (strncmp(line, "endsolid", 8) == 0) {
|
|
||||||
// Closing the "solid" block.
|
|
||||||
if (solid_name.empty()) {
|
|
||||||
// Error, no solid block is open.
|
|
||||||
facets.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
solid_name.clear();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
// Line has to start with the word solid.
|
|
||||||
int res_normal = sscanf(line, " facet normal %31s %31s %31s", normal_buf[0], normal_buf[1], normal_buf[2]);
|
|
||||||
assert(res_normal == 3);
|
|
||||||
int res_outer_loop = line_reader.next_line_scanf(" outer loop");
|
|
||||||
assert(res_outer_loop == 0);
|
|
||||||
int res_vertex1 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[0](0), &facet.vertex[0](1), &facet.vertex[0](2));
|
|
||||||
assert(res_vertex1 == 3);
|
|
||||||
int res_vertex2 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[1](0), &facet.vertex[1](1), &facet.vertex[1](2));
|
|
||||||
assert(res_vertex2 == 3);
|
|
||||||
int res_vertex3 = line_reader.next_line_scanf(" vertex %f %f %f", &facet.vertex[2](0), &facet.vertex[2](1), &facet.vertex[2](2));
|
|
||||||
assert(res_vertex3 == 3);
|
|
||||||
int res_endloop = line_reader.next_line_scanf(" endloop");
|
|
||||||
assert(res_endloop == 0);
|
|
||||||
int res_endfacet = line_reader.next_line_scanf(" endfacet");
|
|
||||||
if (res_normal != 3 || res_outer_loop != 0 || res_vertex1 != 3 || res_vertex2 != 3 || res_vertex3 != 3 || res_endloop != 0 || res_endfacet != 0) {
|
|
||||||
// perror("Something is syntactically very wrong with this ASCII STL!");
|
|
||||||
facets.clear();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
// The facet normal has been parsed as a single string as to workaround for not a numbers in the normal definition.
|
|
||||||
if (sscanf(normal_buf[0], "%f", &facet.normal(0)) != 1 ||
|
|
||||||
sscanf(normal_buf[1], "%f", &facet.normal(1)) != 1 ||
|
|
||||||
sscanf(normal_buf[2], "%f", &facet.normal(2)) != 1) {
|
|
||||||
// Normal was mangled. Maybe denormals or "not a number" were stored?
|
|
||||||
// Just reset the normal and silently ignore it.
|
|
||||||
memset(&facet.normal, 0, sizeof(facet.normal));
|
|
||||||
}
|
|
||||||
facets.emplace_back(facet);
|
|
||||||
}
|
|
||||||
if (! facets.empty() && solid_name.empty()) {
|
|
||||||
stl_file &stl = mesh.stl;
|
|
||||||
stl.stats.type = inmemory;
|
|
||||||
stl.stats.number_of_facets = facets.size();
|
|
||||||
stl.stats.original_num_facets = facets.size();
|
|
||||||
stl_allocate(&stl);
|
|
||||||
memcpy((void*)stl.facet_start, facets.data(), facets.size() * 50);
|
|
||||||
stl_get_size(&stl);
|
|
||||||
mesh.repair();
|
|
||||||
// Transform the model.
|
|
||||||
stl_transform(&stl, &trafo[0][0]);
|
|
||||||
// Add a mesh to a model.
|
|
||||||
if (mesh.facets_count() > 0)
|
|
||||||
mesh_valid = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mesh_valid) {
|
|
||||||
// Add this mesh to the model.
|
|
||||||
ModelVolume *volume = nullptr;
|
|
||||||
if (model_object == nullptr) {
|
|
||||||
// This is a first mesh of a group. Create a new object & volume.
|
|
||||||
model_object = model->add_object(name_utf8.data(), path, std::move(mesh));
|
|
||||||
volume = model_object->volumes.front();
|
|
||||||
ModelInstance *instance = model_object->add_instance();
|
|
||||||
#if ENABLE_MODELINSTANCE_3D_ROTATION
|
|
||||||
instance->set_rotation(instance_rotation);
|
|
||||||
#else
|
|
||||||
instance->rotation = instance_rotation;
|
|
||||||
#endif // ENABLE_MODELINSTANCE_3D_ROTATION
|
|
||||||
instance->scaling_factor = instance_scaling_factor;
|
|
||||||
#if ENABLE_MODELINSTANCE_3D_OFFSET
|
|
||||||
instance->set_offset(instance_offset);
|
|
||||||
#else
|
|
||||||
instance->offset = instance_offset;
|
|
||||||
#endif // ENABLE_MODELINSTANCE_3D_OFFSET
|
|
||||||
++num_models;
|
|
||||||
if (group_id != (size_t)-1)
|
|
||||||
group_to_model_object[group_id] = model_object;
|
|
||||||
} else {
|
|
||||||
// This is not the 1st mesh of a group. Add it to the ModelObject.
|
|
||||||
volume = model_object->add_volume(std::move(mesh));
|
|
||||||
volume->name = name_utf8.data();
|
|
||||||
}
|
|
||||||
// Set the extruder to the volume.
|
|
||||||
if (extruder_id != (unsigned int)-1) {
|
|
||||||
char str_extruder[64];
|
|
||||||
sprintf(str_extruder, "%ud", extruder_id);
|
|
||||||
volume->config.set_deserialize("extruder", str_extruder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
} catch (std::exception &ex) {
|
||||||
|
mz_zip_reader_end(&archive);
|
||||||
|
throw ex;
|
||||||
}
|
}
|
||||||
return num_models > 0;
|
|
||||||
|
mz_zip_reader_end(&archive);
|
||||||
|
return model->objects.size() > n_models_initial;
|
||||||
}
|
}
|
||||||
|
|
||||||
}; // namespace Slic3r
|
}; // namespace Slic3r
|
||||||
|
|
||||||
#endif /* SLIC3R_PRUS */
|
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
#if defined(SLIC3R_PRUS) && ! defined(slic3r_Format_PRUS_hpp_)
|
|
||||||
#define slic3r_Format_PRUS_hpp_
|
#define slic3r_Format_PRUS_hpp_
|
||||||
|
|
||||||
namespace Slic3r {
|
namespace Slic3r {
|
||||||
|
@ -10,5 +9,3 @@ class Model;
|
||||||
extern bool load_prus(const char *path, Model *model);
|
extern bool load_prus(const char *path, Model *model);
|
||||||
|
|
||||||
}; // namespace Slic3r
|
}; // namespace Slic3r
|
||||||
|
|
||||||
#endif /* SLIC3R_PRUS && ! defined(slic3r_Format_PRUS_hpp_) */
|
|
||||||
|
|
|
@ -62,10 +62,8 @@ Model Model::read_from_file(const std::string &input_file, DynamicPrintConfig *c
|
||||||
result = load_amf(input_file.c_str(), config, &model);
|
result = load_amf(input_file.c_str(), config, &model);
|
||||||
else if (boost::algorithm::iends_with(input_file, ".3mf"))
|
else if (boost::algorithm::iends_with(input_file, ".3mf"))
|
||||||
result = load_3mf(input_file.c_str(), config, &model);
|
result = load_3mf(input_file.c_str(), config, &model);
|
||||||
#ifdef SLIC3R_PRUS
|
|
||||||
else if (boost::algorithm::iends_with(input_file, ".prusa"))
|
else if (boost::algorithm::iends_with(input_file, ".prusa"))
|
||||||
result = load_prus(input_file.c_str(), &model);
|
result = load_prus(input_file.c_str(), &model);
|
||||||
#endif /* SLIC3R_PRUS */
|
|
||||||
else
|
else
|
||||||
throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
|
throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension.");
|
||||||
|
|
||||||
|
|
|
@ -134,25 +134,31 @@ void TriangleMesh::repair()
|
||||||
|
|
||||||
// remove_unconnected
|
// remove_unconnected
|
||||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_remove_unconnected_facets";
|
||||||
stl_remove_unconnected_facets(&stl);
|
stl_remove_unconnected_facets(&stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// fill_holes
|
// fill_holes
|
||||||
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) {
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_fill_holes";
|
||||||
stl_fill_holes(&stl);
|
stl_fill_holes(&stl);
|
||||||
stl_clear_error(&stl);
|
stl_clear_error(&stl);
|
||||||
}
|
}
|
||||||
|
|
||||||
// normal_directions
|
// normal_directions
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions";
|
||||||
stl_fix_normal_directions(&stl);
|
stl_fix_normal_directions(&stl);
|
||||||
|
|
||||||
// normal_values
|
// normal_values
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values";
|
||||||
stl_fix_normal_values(&stl);
|
stl_fix_normal_values(&stl);
|
||||||
|
|
||||||
// always calculate the volume and reverse all normals if volume is negative
|
// always calculate the volume and reverse all normals if volume is negative
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume";
|
||||||
stl_calculate_volume(&stl);
|
stl_calculate_volume(&stl);
|
||||||
|
|
||||||
// neighbors
|
// neighbors
|
||||||
|
BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors";
|
||||||
stl_verify_neighbors(&stl);
|
stl_verify_neighbors(&stl);
|
||||||
|
|
||||||
this->repaired = true;
|
this->repaired = true;
|
||||||
|
|
|
@ -117,10 +117,10 @@ inline uint64_t next_highest_power_of_2(uint64_t v)
|
||||||
inline size_t next_highest_power_of_2(size_t v)
|
inline size_t next_highest_power_of_2(size_t v)
|
||||||
{
|
{
|
||||||
#if SSIZE_MAX == 9223372036854775807
|
#if SSIZE_MAX == 9223372036854775807
|
||||||
static_assert(sizeof(size_t) == sizeof(uint64_t));
|
static_assert(sizeof(size_t) == sizeof(uint64_t), "sizeof(size_t) == sizeof(uint64_t)");
|
||||||
return next_highest_power_of_2(uint64_t(v));
|
return next_highest_power_of_2(uint64_t(v));
|
||||||
#else
|
#else
|
||||||
static_assert(sizeof(size_t) == sizeof(uint32_t));
|
static_assert(sizeof(size_t) == sizeof(uint32_t), "sizeof(size_t) == sizeof(uint32_t)");
|
||||||
return next_highest_power_of_2(uint32_t(v));
|
return next_highest_power_of_2(uint32_t(v));
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue