diff --git a/src/libslic3r/Format/PRUS.cpp b/src/libslic3r/Format/PRUS.cpp index 27095acef..bd6beeb21 100644 --- a/src/libslic3r/Format/PRUS.cpp +++ b/src/libslic3r/Format/PRUS.cpp @@ -1,12 +1,10 @@ -#ifdef SLIC3R_PRUS - #include <string.h> +#include <exception> +#include <boost/algorithm/string.hpp> #include <boost/nowide/convert.hpp> -#include <wx/string.h> -#include <wx/wfstream.h> -#include <wx/zipstrm.h> +#include <miniz/miniz_zip.h> #include <Eigen/Geometry> @@ -35,64 +33,28 @@ struct StlHeader static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct"); -// Buffered line reader for the wxInputStream. +// Buffered line reader to a string buffer. class LineReader { public: - LineReader(wxInputStream &input_stream, const char *initial_data, int initial_len) : - 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); - } + LineReader(std::vector<char> &data) : m_buffer(data), m_pos(0), m_len(data.size()) {} const char* next_line() { - for (;;) { - // Skip empty lines. - while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n')) - ++ m_pos; - if (m_pos == m_len) { - // Empty buffer, fill it from the input stream. - 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; + // Skip empty lines. + while (m_pos < m_len && (m_buffer[m_pos] == '\r' || m_buffer[m_pos] == '\n')) + ++ m_pos; + if (m_pos == m_len) { + // End of file. + return nullptr; } + // 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, ...) @@ -109,303 +71,314 @@ public: } private: - wxInputStream &m_input_stream; - static const int m_bufsize = 4096; - char m_buffer[m_bufsize]; - int m_pos = 0; - int m_len = 0; + std::vector<char> &m_buffer; + int m_pos; + int m_len; }; +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. bool load_prus(const char *path, Model *model) { - // To receive the content of the zipped 'scene.xml' file. - std::vector<char> scene_xml_data; - wxFFileInputStream in( -#ifdef WIN32 - // On Windows, convert to a 16bit unicode string. - boost::nowide::widen(path).c_str() -#else - path -#endif - ); - wxZipInputStream zip(in); - std::unique_ptr<wxZipEntry> entry; - size_t num_models = 0; - std::map<int, ModelObject*> group_to_model_object; - while (entry.reset(zip.GetNextEntry()), entry.get() != NULL) { - wxString name = entry->GetName(); - if (name == "scene.xml") { - if (! scene_xml_data.empty()) { - // scene.xml has been found more than once in the archive. - return false; - } - size_t size_last = 0; - size_t size_incr = 4096; - scene_xml_data.resize(size_incr); - while (! zip.Read(scene_xml_data.data() + size_last, size_incr).Eof()) { - size_last += zip.LastRead(); - if (scene_xml_data.size() < size_last + size_incr) - 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); - } - } + mz_zip_archive archive; + mz_zip_zero_struct(&archive); + mz_bool res = mz_zip_reader_init_file(&archive, path, 0); + size_t n_models_initial = model->objects.size(); + try { + if (res == MZ_FALSE) + throw std::runtime_error(std::string("Unable to init zip reader for ") + path); + std::vector<char> scene_xml_data; + // For grouping multiple STLs into a single ModelObject for multi-material prints. + std::map<int, ModelObject*> group_to_model_object; + mz_uint num_entries = mz_zip_reader_get_num_files(&archive); + for (mz_uint i = 0; i < num_entries; ++ i) { + mz_zip_archive_file_stat stat; + if (! mz_zip_reader_file_stat(&archive, i, &stat)) + continue; + std::vector<char> buffer; + buffer.assign((size_t)stat.m_uncomp_size + 1, 0); + res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, (char*)buffer.data(), (size_t)stat.m_uncomp_size, 0); + if (res == MZ_FALSE) + std::runtime_error(std::string("Error while extracting a file from ") + path); + if (strcmp(stat.m_filename, "scene.xml") == 0) { + if (! scene_xml_data.empty()) + throw std::runtime_error(std::string("Multiple scene.xml were found in the archive.") + path); + scene_xml_data = std::move(buffer); + } else if (boost::iends_with(stat.m_filename, ".stl")) { + // May throw std::exception + extract_model_from_archive(stat.m_filename, path, scene_xml_data, buffer, model, group_to_model_object); } } + } 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 - -#endif /* SLIC3R_PRUS */ diff --git a/src/libslic3r/Format/PRUS.hpp b/src/libslic3r/Format/PRUS.hpp index 8559a70d6..be5c5c61a 100644 --- a/src/libslic3r/Format/PRUS.hpp +++ b/src/libslic3r/Format/PRUS.hpp @@ -1,4 +1,3 @@ -#if defined(SLIC3R_PRUS) && ! defined(slic3r_Format_PRUS_hpp_) #define slic3r_Format_PRUS_hpp_ namespace Slic3r { @@ -10,5 +9,3 @@ class Model; extern bool load_prus(const char *path, Model *model); }; // namespace Slic3r - -#endif /* SLIC3R_PRUS && ! defined(slic3r_Format_PRUS_hpp_) */ diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index ae74ab18e..1128e57fc 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -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); else if (boost::algorithm::iends_with(input_file, ".3mf")) result = load_3mf(input_file.c_str(), config, &model); -#ifdef SLIC3R_PRUS else if (boost::algorithm::iends_with(input_file, ".prusa")) result = load_prus(input_file.c_str(), &model); -#endif /* SLIC3R_PRUS */ else throw std::runtime_error("Unknown file format. Input file must have .stl, .obj, .amf(.xml) or .prusa extension."); diff --git a/src/libslic3r/TriangleMesh.cpp b/src/libslic3r/TriangleMesh.cpp index 4bf13330f..c56b66de3 100644 --- a/src/libslic3r/TriangleMesh.cpp +++ b/src/libslic3r/TriangleMesh.cpp @@ -134,25 +134,31 @@ void TriangleMesh::repair() // remove_unconnected 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); } // fill_holes if (stl.stats.connected_facets_3_edge < stl.stats.number_of_facets) { + BOOST_LOG_TRIVIAL(trace) << "\tstl_fill_holes"; stl_fill_holes(&stl); stl_clear_error(&stl); } // normal_directions + BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_directions"; stl_fix_normal_directions(&stl); // normal_values + BOOST_LOG_TRIVIAL(trace) << "\tstl_fix_normal_values"; stl_fix_normal_values(&stl); // always calculate the volume and reverse all normals if volume is negative + BOOST_LOG_TRIVIAL(trace) << "\tstl_calculate_volume"; stl_calculate_volume(&stl); // neighbors + BOOST_LOG_TRIVIAL(trace) << "\tstl_verify_neighbors"; stl_verify_neighbors(&stl); this->repaired = true; diff --git a/src/libslic3r/Utils.hpp b/src/libslic3r/Utils.hpp index 8feb17747..863ca6b2f 100644 --- a/src/libslic3r/Utils.hpp +++ b/src/libslic3r/Utils.hpp @@ -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) { #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)); #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)); #endif }