Eradicated admesh from TriangleMesh:

TriangleMesh newly only holds indexed_triangle_set and
TriangleMeshStats. TriangleMeshStats contains an excerpt of stl_stats.
TriangleMeshStats are updated when initializing with indexed_triangle_set.

Admesh triangle mesh fixing is newly only used when loading an STL.
AMF / 3MF / OBJ file formats are already indexed triangle sets, thus
they are no more converted to admesh stl_file format, nor fixed
through admesh repair machinery. When importing AMF / 3MF / OBJ files,
volume is calculated and if negative, all faces are flipped. Also
a bounding box and number of open edges is calculated.

Implemented its_number_of_patches(), its_num_open_edges()
Optimized its_split(), its_is_splittable() using a visitor pattern.

Reworked QHull integration into TriangleMesh:
    1) Face normals were not right.
    2) Indexed triangle set is newly emitted instead of duplicating
       vertices for each face.

Fixed cut_mesh(): Orient the triangulated faces correctly.
This commit is contained in:
Vojtech Bubnik 2021-09-20 17:12:22 +02:00
parent f484953a5a
commit 8a2a9dba2f
59 changed files with 1056 additions and 1758 deletions

View file

@ -305,8 +305,8 @@ namespace Slic3r {
struct Geometry
{
std::vector<float> vertices;
std::vector<unsigned int> triangles;
std::vector<Vec3f> vertices;
std::vector<Vec3i> triangles;
std::vector<std::string> custom_supports;
std::vector<std::string> custom_seam;
std::vector<std::string> mmu_segmentation;
@ -720,7 +720,7 @@ namespace Slic3r {
}
// use the geometry to create the volumes in the new model objects
ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() / 3 - 1 });
ObjectMetadata::VolumeMetadataList volumes(1, { 0, (unsigned int)geometry->triangles.size() - 1 });
// for each instance after the 1st, create a new model object containing only that instance
// and copy into it the geometry
@ -793,7 +793,7 @@ namespace Slic3r {
// config data not found, this model was not saved using slic3r pe
// add the entire geometry as the single volume to generate
volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() / 3 - 1);
volumes.emplace_back(0, (int)obj_geometry->second.triangles.size() - 1);
// select as volumes
volumes_ptr = &volumes;
@ -1559,9 +1559,10 @@ namespace Slic3r {
{
// appends the vertex coordinates
// missing values are set equal to ZERO
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR));
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR));
m_curr_object.geometry.vertices.push_back(m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
m_curr_object.geometry.vertices.emplace_back(
m_unit_factor * get_attribute_value_float(attributes, num_attributes, X_ATTR),
m_unit_factor * get_attribute_value_float(attributes, num_attributes, Y_ATTR),
m_unit_factor * get_attribute_value_float(attributes, num_attributes, Z_ATTR));
return true;
}
@ -1595,9 +1596,10 @@ namespace Slic3r {
// appends the triangle's vertices indices
// missing values are set equal to ZERO
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V1_ATTR));
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V2_ATTR));
m_curr_object.geometry.triangles.push_back((unsigned int)get_attribute_value_int(attributes, num_attributes, V3_ATTR));
m_curr_object.geometry.triangles.emplace_back(
get_attribute_value_int(attributes, num_attributes, V1_ATTR),
get_attribute_value_int(attributes, num_attributes, V2_ATTR),
get_attribute_value_int(attributes, num_attributes, V3_ATTR));
m_curr_object.geometry.custom_supports.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR));
m_curr_object.geometry.custom_seam.push_back(get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR));
@ -1886,7 +1888,7 @@ namespace Slic3r {
return false;
}
unsigned int geo_tri_count = (unsigned int)geometry.triangles.size() / 3;
unsigned int geo_tri_count = (unsigned int)geometry.triangles.size();
unsigned int renamed_volumes_count = 0;
for (const ObjectMetadata::VolumeMetadata& volume_data : volumes) {
@ -1897,77 +1899,50 @@ namespace Slic3r {
Transform3d volume_matrix_to_object = Transform3d::Identity();
bool has_transform = false;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = false;
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
// extract the volume transformation from the volume's metadata, if present
for (const Metadata& metadata : volume_data.metadata) {
if (metadata.key == MATRIX_KEY) {
volume_matrix_to_object = Slic3r::Geometry::transform3d_from_string(metadata.value);
has_transform = ! volume_matrix_to_object.isApprox(Transform3d::Identity(), 1e-10);
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
is_left_handed = Slic3r::Geometry::Transformation(volume_matrix_to_object).is_left_handed();
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
break;
}
}
// splits volume out of imported geometry
TriangleMesh triangle_mesh;
stl_file &stl = triangle_mesh.stl;
unsigned int triangles_count = volume_data.last_triangle_id - volume_data.first_triangle_id + 1;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)triangles_count;
stl.stats.original_num_facets = (int)stl.stats.number_of_facets;
stl_allocate(&stl);
unsigned int src_start_id = volume_data.first_triangle_id * 3;
for (unsigned int i = 0; i < triangles_count; ++i) {
unsigned int ii = i * 3;
stl_facet& facet = stl.facet_start[i];
for (unsigned int v = 0; v < 3; ++v) {
unsigned int tri_id = geometry.triangles[src_start_id + ii + v] * 3;
if (tri_id + 2 >= geometry.vertices.size()) {
add_error("Malformed triangle mesh");
std::vector<Vec3i> faces(geometry.triangles.begin() + volume_data.first_triangle_id, geometry.triangles.begin() + volume_data.last_triangle_id + 1);
const size_t triangles_count = faces.size();
for (Vec3i face : faces)
for (unsigned int tri_id : face)
if (tri_id < 0 || tri_id >= geometry.vertices.size()) {
add_error("Found invalid vertex id");
return false;
}
facet.vertex[v] = Vec3f(geometry.vertices[tri_id + 0], geometry.vertices[tri_id + 1], geometry.vertices[tri_id + 2]);
}
}
stl_get_size(&stl);
triangle_mesh.repair();
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
// PrusaSlicer older than 2.4.0 saved mirrored volumes with reversed winding of the triangles
// This caused the call to TriangleMesh::repair() to reverse all the facets because the calculated volume was negative
if (is_left_handed && stl.stats.facets_reversed > 0 && stl.stats.facets_reversed == stl.stats.original_num_facets)
stl.stats.facets_reversed = 0;
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
TriangleMesh triangle_mesh(std::move(geometry.vertices), std::move(faces));
if (m_version == 0) {
// if the 3mf was not produced by PrusaSlicer and there is only one instance,
// bake the transformation into the geometry to allow the reload from disk command
// to work properly
if (object.instances.size() == 1) {
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix());
triangle_mesh.transform(object.instances.front()->get_transformation().get_matrix(), false);
object.instances.front()->set_transformation(Slic3r::Geometry::Transformation());
//FIXME do the mesh fixing?
}
}
if (triangle_mesh.volume() < 0)
triangle_mesh.flip_triangles();
ModelVolume* volume = object.add_volume(std::move(triangle_mesh));
// stores the volume matrix taken from the metadata, if present
if (has_transform)
volume->source.transform = Slic3r::Geometry::Transformation(volume_matrix_to_object);
volume->calculate_convex_hull();
// recreate custom supports, seam and mmu segmentation from previously loaded attribute
volume->supported_facets.reserve(triangles_count);
volume->seam_facets.reserve(triangles_count);
volume->mmu_segmentation_facets.reserve(triangles_count);
for (unsigned i=0; i<triangles_count; ++i) {
size_t index = src_start_id/3 + i;
for (size_t i=0; i<triangles_count; ++i) {
size_t index = volume_data.first_triangle_id + i;
assert(index < geometry.custom_supports.size());
assert(index < geometry.custom_seam.size());
assert(index < geometry.mmu_segmentation.size());
@ -2543,11 +2518,6 @@ namespace Slic3r {
if (volume == nullptr)
continue;
if (!volume->mesh().repaired)
throw Slic3r::FileIOError("store_3mf() requires repair()");
if (!volume->mesh().has_shared_vertices())
throw Slic3r::FileIOError("store_3mf() requires shared vertices");
volumes_offsets.insert({ volume, Offsets(vertices_count) });
const indexed_triangle_set &its = volume->mesh().its;
@ -2588,10 +2558,7 @@ namespace Slic3r {
if (volume == nullptr)
continue;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
bool is_left_handed = volume->is_left_handed();
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
VolumeToOffsetsMap::iterator volume_it = volumes_offsets.find(volume);
assert(volume_it != volumes_offsets.end());
@ -2606,7 +2573,6 @@ namespace Slic3r {
{
const Vec3i &idx = its.indices[i];
char *ptr = buf;
#if ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ <<
@ -2614,15 +2580,6 @@ namespace Slic3r {
idx[is_left_handed ? 2 : 0] + volume_it->second.first_vertex_id,
idx[1] + volume_it->second.first_vertex_id,
idx[is_left_handed ? 0 : 2] + volume_it->second.first_vertex_id);
#else
boost::spirit::karma::generate(ptr, boost::spirit::lit(" <") << TRIANGLE_TAG <<
" v1=\"" << boost::spirit::int_ <<
"\" v2=\"" << boost::spirit::int_ <<
"\" v3=\"" << boost::spirit::int_ << "\"",
idx[0] + volume_it->second.first_vertex_id,
idx[1] + volume_it->second.first_vertex_id,
idx[2] + volume_it->second.first_vertex_id);
#endif // ENABLE_FIX_MIRRORED_VOLUMES_3MF_IMPORT_EXPORT
*ptr = '\0';
output_buffer += buf;
}

View file

@ -244,11 +244,11 @@ struct AMFParserContext
// Map from obect name to object idx & instances.
std::map<std::string, Object> m_object_instances_map;
// Vertices parsed for the current m_object.
std::vector<float> m_object_vertices;
std::vector<Vec3f> m_object_vertices;
// Current volume allocated for an amf/object/mesh/volume subtree.
ModelVolume *m_volume { nullptr };
// Faces collected for the current m_volume.
std::vector<int> m_volume_facets;
std::vector<Vec3i> m_volume_facets;
// Transformation matrix of a volume mesh from its coordinate system to Object's coordinate system.
Transform3d m_volume_transform;
// Current material allocated for an amf/metadata subtree.
@ -598,9 +598,7 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VERTEX:
assert(m_object);
// Parse the vertex data
m_object_vertices.emplace_back((float)atof(m_value[0].c_str()));
m_object_vertices.emplace_back((float)atof(m_value[1].c_str()));
m_object_vertices.emplace_back((float)atof(m_value[2].c_str()));
m_object_vertices.emplace_back(float(atof(m_value[0].c_str())), float(atof(m_value[1].c_str())), float(atof(m_value[1].c_str())));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@ -609,9 +607,7 @@ void AMFParserContext::endElement(const char * /* name */)
// Faces of the current volume:
case NODE_TYPE_TRIANGLE:
assert(m_object && m_volume);
m_volume_facets.emplace_back(atoi(m_value[0].c_str()));
m_volume_facets.emplace_back(atoi(m_value[1].c_str()));
m_volume_facets.emplace_back(atoi(m_value[2].c_str()));
m_volume_facets.emplace_back(atoi(m_value[0].c_str()), atoi(m_value[1].c_str()), atoi(m_value[2].c_str()));
m_value[0].clear();
m_value[1].clear();
m_value[2].clear();
@ -621,44 +617,36 @@ void AMFParserContext::endElement(const char * /* name */)
case NODE_TYPE_VOLUME:
{
assert(m_object && m_volume);
TriangleMesh mesh;
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = int(m_volume_facets.size() / 3);
stl.stats.original_num_facets = stl.stats.number_of_facets;
stl_allocate(&stl);
bool has_transform = ! m_volume_transform.isApprox(Transform3d::Identity(), 1e-10);
for (size_t i = 0; i < m_volume_facets.size();) {
stl_facet &facet = stl.facet_start[i/3];
for (unsigned int v = 0; v < 3; ++v)
{
unsigned int tri_id = m_volume_facets[i++] * 3;
if (tri_id < 0 || tri_id + 2 >= m_object_vertices.size()) {
// Verify validity of face indices.
for (Vec3i face : m_volume_facets)
for (unsigned int tri_id : face)
if (tri_id < 0 || tri_id >= m_object_vertices.size()) {
this->stop("Malformed triangle mesh");
return;
}
facet.vertex[v] = Vec3f(m_object_vertices[tri_id + 0], m_object_vertices[tri_id + 1], m_object_vertices[tri_id + 2]);
}
}
stl_get_size(&stl);
mesh.repair();
m_volume->set_mesh(std::move(mesh));
// stores the volume matrix taken from the metadata, if present
if (has_transform)
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART))
{
TriangleMesh triangle_mesh { std::move(m_object_vertices), std::move(m_volume_facets) };
if (triangle_mesh.volume() < 0)
triangle_mesh.flip_triangles();
m_volume->set_mesh(std::move(triangle_mesh));
}
// stores the volume matrix taken from the metadata, if present
if (bool has_transform = !m_volume_transform.isApprox(Transform3d::Identity(), 1e-10); has_transform)
m_volume->source.transform = Slic3r::Geometry::Transformation(m_volume_transform);
if (m_volume->source.input_file.empty() && (m_volume->type() == ModelVolumeType::MODEL_PART)) {
m_volume->source.object_idx = (int)m_model.objects.size() - 1;
m_volume->source.volume_idx = (int)m_model.objects.back()->volumes.size() - 1;
m_volume->center_geometry_after_creation();
}
else
} else
// pass false if the mesh offset has been already taken from the data
m_volume->center_geometry_after_creation(m_volume->source.input_file.empty());
m_volume->calculate_convex_hull();
m_volume_facets.clear();
m_object_vertices.clear();
m_volume = nullptr;
break;
}
@ -1187,10 +1175,6 @@ bool store_amf(const char* path, Model* model, const DynamicPrintConfig* config,
int num_vertices = 0;
for (ModelVolume *volume : object->volumes) {
vertices_offsets.push_back(num_vertices);
if (! volume->mesh().repaired)
throw Slic3r::FileIOError("store_amf() requires repair()");
if (! volume->mesh().has_shared_vertices())
throw Slic3r::FileIOError("store_amf() requires shared vertices");
const indexed_triangle_set &its = volume->mesh().its;
const Transform3d& matrix = volume->get_matrix();
for (size_t i = 0; i < its.vertices.size(); ++i) {

View file

@ -19,7 +19,8 @@ namespace Slic3r {
bool load_obj(const char *path, TriangleMesh *meshptr)
{
if(meshptr == nullptr) return false;
if (meshptr == nullptr)
return false;
// Parse the OBJ file.
ObjParser::ObjData data;
@ -31,84 +32,69 @@ bool load_obj(const char *path, TriangleMesh *meshptr)
// 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(); ) {
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 (i == j)
continue;
size_t face_vertices = j - i;
if (face_vertices != 3 && face_vertices != 4) {
// Non-triangular and non-quad faces are not supported as of now.
return false;
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;
}
if (face_vertices == 4)
++ num_quads;
++ num_faces;
i = j + 1;
}
// Convert ObjData into STL.
TriangleMesh &mesh = *meshptr;
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = uint32_t(num_faces + num_quads);
stl.stats.original_num_facets = int(num_faces + num_quads);
// stl_allocate clears all the allocated data to zero, all normals are set to zeros as well.
stl_allocate(&stl);
size_t i_face = 0;
for (size_t i = 0; i < data.vertices.size(); ++ i) {
if (data.vertices[i].coordIdx == -1)
continue;
stl_facet &facet = stl.facet_start[i_face ++];
size_t num_normals = 0;
stl_normal normal(stl_normal::Zero());
for (unsigned int v = 0; v < 3; ++ v) {
const ObjParser::ObjVertex &vertex = data.vertices[i++];
memcpy(facet.vertex[v].data(), &data.coordinates[vertex.coordIdx*4], 3 * sizeof(float));
if (vertex.normalIdx != -1) {
normal(0) += data.normals[vertex.normalIdx*3];
normal(1) += data.normals[vertex.normalIdx*3+1];
normal(2) += data.normals[vertex.normalIdx*3+2];
++ num_normals;
}
}
// Result of obj_parseline() call is not checked, thus not all vertices are necessarily finalized with coord_Idx == -1.
if (i < data.vertices.size() && data.vertices[i].coordIdx != -1) {
// This is a quad. Produce the other triangle.
stl_facet &facet2 = stl.facet_start[i_face++];
facet2.vertex[0] = facet.vertex[0];
facet2.vertex[1] = facet.vertex[2];
const ObjParser::ObjVertex &vertex = data.vertices[i++];
memcpy(facet2.vertex[2].data(), &data.coordinates[vertex.coordIdx * 4], 3 * sizeof(float));
if (vertex.normalIdx != -1) {
normal(0) += data.normals[vertex.normalIdx*3];
normal(1) += data.normals[vertex.normalIdx*3+1];
normal(2) += data.normals[vertex.normalIdx*3+2];
++ num_normals;
}
if (num_normals == 4) {
// Normalize an average normal of a quad.
float len = facet.normal.norm();
if (len > EPSILON) {
normal /= len;
facet.normal = normal;
facet2.normal = normal;
}
}
} else if (num_normals == 3) {
// Normalize an average normal of a triangle.
float len = facet.normal.norm();
if (len > EPSILON)
facet.normal = normal / len;
}
// 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]);
}
stl_get_size(&stl);
mesh.repair();
if (mesh.facets_count() == 0) {
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 >= 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;
}

View file

@ -1,333 +0,0 @@
#include <string.h>
#include <exception>
#include <boost/algorithm/string.hpp>
#include "miniz_extension.hpp"
#include <Eigen/Geometry>
#include "../libslic3r.h"
#include "../Model.hpp"
#include "PRUS.hpp"
#if 0
// Enable debugging and assert in this file.
#define DEBUG
#define _DEBUG
#undef NDEBUG
#endif
#include <assert.h>
namespace Slic3r
{
struct StlHeader
{
char comment[80];
uint32_t nTriangles;
};
static_assert(sizeof(StlHeader) == 84, "StlHeader size not correct");
// Buffered line reader to a string buffer.
class LineReader
{
public:
LineReader(std::vector<char> &data) : m_buffer(data), m_pos(0), m_len((int)data.size()) {}
const char* next_line() {
// 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, ...)
{
const char *line = next_line();
if (line == nullptr)
return -1;
int result;
va_list arglist;
va_start(arglist, format);
result = vsscanf(line, format, arglist);
va_end(arglist);
return result;
}
private:
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);
Vec3d instance_rotation = Vec3d::Zero();
Vec3d instance_scaling_factor = Vec3d::Ones();
Vec3d instance_offset = Vec3d::Zero();
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) {
instance_scaling_factor = Vec3d((double)scale[0], (double)scale[1], (double)scale[2]);
instance_rotation = Vec3d(-(double)rotation[0], -(double)rotation[1], -(double)rotation[2]);
instance_offset = Vec3d((double)(position[0] - zero[0]), (double)(position[1] - zero[1]), (double)(position[2] - zero[2]));
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 Slic3r::FileIOError(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.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.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.data();
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();
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.
facet.normal = stl_normal::Zero();
}
facets.emplace_back(facet);
}
if (! facets.empty() && solid_name.empty()) {
stl_file &stl = mesh.stl;
stl.stats.type = inmemory;
stl.stats.number_of_facets = (uint32_t)facets.size();
stl.stats.original_num_facets = (int)facets.size();
stl_allocate(&stl);
memcpy((void*)stl.facet_start.data(), facets.data(), facets.size() * 50);
stl_get_size(&stl);
mesh.repair();
// Add a mesh to a model.
if (mesh.facets_count() > 0)
mesh_valid = true;
}
}
if (! mesh_valid)
throw Slic3r::FileIOError(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();
instance->set_rotation(instance_rotation);
instance->set_scaling_factor(instance_scaling_factor);
instance->set_offset(instance_offset);
if (group_id != (unsigned int)(-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)
volume->config.set("extruder", int(extruder_id));
}
// Load a PrusaControl project file into a provided model.
bool load_prus(const char *path, Model *model)
{
mz_zip_archive archive;
mz_zip_zero_struct(&archive);
size_t n_models_initial = model->objects.size();
mz_bool res = MZ_FALSE;
try {
if (!open_zip_reader(&archive, path))
throw Slic3r::FileIOError(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, 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)
throw Slic3r::FileIOError(std::string("Error while extracting a file from ") + path);
if (strcmp(stat.m_filename, "scene.xml") == 0) {
if (! scene_xml_data.empty())
throw Slic3r::FileIOError(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) {
close_zip_reader(&archive);
throw ex;
}
close_zip_reader(&archive);
return model->objects.size() > n_models_initial;
}
}; // namespace Slic3r

View file

@ -1,11 +0,0 @@
#define slic3r_Format_PRUS_hpp_
namespace Slic3r {
class TriangleMesh;
class Model;
// Load a PrusaControl project file into a provided model.
extern bool load_prus(const char *path, Model *model);
}; // namespace Slic3r

View file

@ -21,8 +21,7 @@ bool load_stl(const char *path, Model *model, const char *object_name_in)
// die "Failed to open $file\n" if !-e $path;
return false;
}
mesh.repair();
if (mesh.facets_count() == 0) {
if (mesh.empty()) {
// die "This STL file couldn't be read because it's empty.\n"
return false;
}