From f092f4ccd515e24f13818cc6fd06aa94488b786b Mon Sep 17 00:00:00 2001 From: cmguo <357215492@qq.com> Date: Wed, 14 Jun 2023 19:45:56 +0800 Subject: [PATCH 1/3] Support 3MF Production Extension (PR #10808 by @cmguo) --- src/libslic3r/Format/3mf.cpp | 194 ++++++++++++++++++++++++++++++----- 1 file changed, 170 insertions(+), 24 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index cd94eeaa07..f684b5e87f 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -91,6 +91,11 @@ const std::string SLA_DRAIN_HOLES_FILE = "Metadata/Slic3r_PE_sla_drain_holes.txt const std::string CUSTOM_GCODE_PER_PRINT_Z_FILE = "Metadata/Prusa_Slicer_custom_gcode_per_print_z.xml"; const std::string CUT_INFORMATION_FILE = "Metadata/Prusa_Slicer_cut_information.xml"; +static constexpr const char *RELATIONSHIP_TAG = "Relationship"; + +static constexpr const char* TARGET_ATTR = "Target"; +static constexpr const char* RELS_TYPE_ATTR = "Type"; + static constexpr const char* MODEL_TAG = "model"; static constexpr const char* RESOURCES_TAG = "resources"; static constexpr const char* OBJECT_TAG = "object"; @@ -118,6 +123,7 @@ static constexpr const char* Z_ATTR = "z"; static constexpr const char* V1_ATTR = "v1"; static constexpr const char* V2_ATTR = "v2"; static constexpr const char* V3_ATTR = "v3"; +static constexpr const char* PPATH_ATTR = "p:path"; static constexpr const char* OBJECTID_ATTR = "objectid"; static constexpr const char* TRANSFORM_ATTR = "transform"; static constexpr const char* PRINTABLE_ATTR = "printable"; @@ -336,18 +342,21 @@ namespace Slic3r { class _3MF_Importer : public _3MF_Base { + typedef std::pair PathId; + struct Component { - int object_id; + PathId object_id; + std::string path; Transform3d transform; - explicit Component(int object_id) + explicit Component(PathId object_id) : object_id(object_id) , transform(Transform3d::Identity()) { } - Component(int object_id, const Transform3d& transform) + Component(PathId object_id, const Transform3d &transform) : object_id(object_id) , transform(transform) { @@ -465,11 +474,11 @@ namespace Slic3r { }; // Map from a 1 based 3MF object ID to a 0 based ModelObject index inside m_model->objects. - typedef std::map IdToModelObjectMap; - typedef std::map IdToAliasesMap; + typedef std::map IdToModelObjectMap; + typedef std::map IdToAliasesMap; typedef std::vector InstancesList; typedef std::map IdToMetadataMap; - typedef std::map IdToGeometryMap; + typedef std::map IdToGeometryMap; typedef std::map> IdToLayerHeightsProfileMap; typedef std::map IdToLayerConfigRangesMap; typedef std::map IdToCutObjectInfoMap; @@ -509,6 +518,8 @@ namespace Slic3r { std::string m_curr_metadata_name; std::string m_curr_characters; std::string m_name; + std::string m_start_part_path; + std::string m_model_path; public: _3MF_Importer(); @@ -532,8 +543,9 @@ namespace Slic3r { } bool _load_model_from_file(const std::string& filename, Model& model, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions); + bool _extract_relationships_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat); + bool _extract_model_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat); bool _is_svg_shape_file(const std::string &filename) const; - bool _extract_model_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_cut_information_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); void _extract_layer_heights_profile_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); void _extract_layer_config_ranges_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, ConfigSubstitutionContext& config_substitutions); @@ -546,6 +558,11 @@ namespace Slic3r { bool _extract_model_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, Model& model); void _extract_embossed_svg_shape_file(const std::string &filename, mz_zip_archive &archive, const mz_zip_archive_file_stat &stat); + // handlers to parse the .rels file + void _handle_start_relationships_element(const char* name, const char** attributes); + void _handle_end_relationships_element(const char* name); + bool _handle_start_relationship(const char **attributes, unsigned int num_attributes); + // handlers to parse the .model file void _handle_start_model_xml_element(const char* name, const char** attributes); void _handle_end_model_xml_element(const char* name); @@ -597,7 +614,7 @@ namespace Slic3r { bool _handle_start_text_configuration(const char** attributes, unsigned int num_attributes); bool _handle_start_shape_configuration(const char **attributes, unsigned int num_attributes); - bool _create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter); + bool _create_object_instance(PathId object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter); void _apply_transform(ModelInstance& instance, const Transform3d& transform); @@ -617,6 +634,10 @@ namespace Slic3r { bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions); + // callbacks to parse the .rels file + static void XMLCALL _handle_start_relationships_element(void *userData, const char *name, const char **attributes); + static void XMLCALL _handle_end_relationships_element(void* userData, const char* name); + // callbacks to parse the .model file static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes); static void XMLCALL _handle_end_model_xml_element(void* userData, const char* name); @@ -705,7 +726,14 @@ namespace Slic3r { m_name = boost::filesystem::path(filename).stem().string(); + int index = mz_zip_reader_locate_file(&archive, RELATIONSHIPS_FILE.c_str(), nullptr, 0); + if (index < 0 || !mz_zip_reader_file_stat(&archive, index, &stat)) + return false; + if (!_extract_relationships_from_archive(archive, stat)) + return false; + // we first loop the entries to read from the archive the .model file only, in order to extract the version from it + mz_zip_archive_file_stat start_part_stat { -1 }; bool found_model = false; for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(&archive, i, &stat)) { @@ -723,6 +751,11 @@ namespace Slic3r { try { // valid model name -> extract model + m_model_path = "/" + name; + if (m_model_path == m_start_part_path) { + start_part_stat = stat; + continue; + } if (!_extract_model_from_archive(archive, stat)) { close_zip_reader(&archive); add_error("Archive does not contain a valid model"); @@ -738,6 +771,21 @@ namespace Slic3r { } } } + + if (start_part_stat.m_file_index >= 0) { + try { + m_model_path.clear(); + if (!_extract_model_from_archive(archive, start_part_stat)) { + close_zip_reader(&archive); + add_error("Archive does not contain a valid model"); + return false; + } + } catch (const std::exception &e) { + // ensure the zip archive is closed and rethrow the exception + close_zip_reader(&archive); + throw Slic3r::FileIOError(e.what()); + } + } if (!found_model) { close_zip_reader(&archive); add_error("Not valid 3mf. There is missing .model file."); @@ -876,7 +924,7 @@ namespace Slic3r { ObjectMetadata::VolumeMetadataList volumes; ObjectMetadata::VolumeMetadataList* volumes_ptr = nullptr; - IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first); + IdToMetadataMap::iterator obj_metadata = m_objects_metadata.find(object.first.second); if (obj_metadata != m_objects_metadata.end()) { // config data has been found, this model was saved using slic3r pe @@ -963,6 +1011,47 @@ namespace Slic3r { return true; } + bool _3MF_Importer::_extract_relationships_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat) + { + if (stat.m_uncomp_size == 0) { + add_error("Found invalid size"); + return false; + } + + _destroy_xml_parser(); + + m_xml_parser = XML_ParserCreate(nullptr); + if (m_xml_parser == nullptr) { + add_error("Unable to create parser"); + return false; + } + + XML_SetUserData(m_xml_parser, (void *) this); + XML_SetElementHandler(m_xml_parser, _handle_start_relationships_element, _handle_end_relationships_element); + + void *parser_buffer = XML_GetBuffer(m_xml_parser, (int) stat.m_uncomp_size); + if (parser_buffer == nullptr) { + add_error("Unable to create buffer"); + return false; + } + + mz_bool res = mz_zip_reader_extract_file_to_mem(&archive, stat.m_filename, parser_buffer, (size_t) stat.m_uncomp_size, 0); + if (res == 0) { + add_error("Error while reading config data to buffer"); + return false; + } + + if (!XML_ParseBuffer(m_xml_parser, (int) stat.m_uncomp_size, 1)) { + char error_buf[1024]; + ::sprintf(error_buf, "Error (%s) while parsing xml file at line %d", XML_ErrorString(XML_GetErrorCode(m_xml_parser)), + (int) XML_GetCurrentLineNumber(m_xml_parser)); + add_error(error_buf); + return false; + } + + return true; + } + bool _3MF_Importer::_is_svg_shape_file(const std::string &name) const { return boost::starts_with(name, MODEL_FOLDER) && boost::ends_with(name, ".svg"); } @@ -1524,7 +1613,58 @@ namespace Slic3r { } } - void _3MF_Importer::_handle_start_model_xml_element(const char* name, const char** attributes) + void XMLCALL _3MF_Importer::_handle_start_relationships_element(void *userData, const char *name, const char **attributes) + { + _3MF_Importer *importer = (_3MF_Importer *) userData; + if (importer != nullptr) + importer->_handle_start_relationships_element(name, attributes); + } + + void XMLCALL _3MF_Importer::_handle_end_relationships_element(void *userData, const char *name) + { + _3MF_Importer *importer = (_3MF_Importer *) userData; + if (importer != nullptr) + importer->_handle_end_relationships_element(name); + } + + void _3MF_Importer::_handle_start_relationships_element(const char *name, const char **attributes) + { + if (m_xml_parser == nullptr) + return; + + bool res = true; + unsigned int num_attributes = (unsigned int) XML_GetSpecifiedAttributeCount(m_xml_parser); + + if (::strcmp(RELATIONSHIP_TAG, name) == 0) + res = _handle_start_relationship(attributes, num_attributes); + + m_curr_characters.clear(); + if (!res) + _stop_xml_parser(); + } + + void _3MF_Importer::_handle_end_relationships_element(const char *name) + { + if (m_xml_parser == nullptr) + return; + + bool res = true; + + if (!res) + _stop_xml_parser(); + } + + bool _3MF_Importer::_handle_start_relationship(const char **attributes, unsigned int num_attributes) + { + std::string path = get_attribute_value_string(attributes, num_attributes, TARGET_ATTR); + std::string type = get_attribute_value_string(attributes, num_attributes, RELS_TYPE_ATTR); + if (boost::starts_with(type, "http://schemas.microsoft.com/3dmanufacturing/") && boost::ends_with(type, "3dmodel")) { + m_start_part_path = path; + } + return true; + } + + void _3MF_Importer::_handle_start_model_xml_element(const char *name, const char **attributes) { if (m_xml_parser == nullptr) return; @@ -1663,6 +1803,9 @@ namespace Slic3r { bool _3MF_Importer::_handle_end_model() { + if (!m_model_path.empty()) + return true; + // deletes all non-built or non-instanced objects for (const IdToModelObjectMap::value_type& object : m_objects) { if (object.second >= int(m_model->objects.size())) { @@ -1731,6 +1874,7 @@ namespace Slic3r { bool _3MF_Importer::_handle_end_object() { if (m_curr_object.object != nullptr) { + PathId object_id{m_model_path, m_curr_object.id}; if (m_curr_object.geometry.empty()) { // no geometry defined // remove the object from the model @@ -1738,26 +1882,26 @@ namespace Slic3r { if (m_curr_object.components.empty()) { // no components defined -> invalid object, delete it - IdToModelObjectMap::iterator object_item = m_objects.find(m_curr_object.id); + IdToModelObjectMap::iterator object_item = m_objects.find(object_id); if (object_item != m_objects.end()) m_objects.erase(object_item); - IdToAliasesMap::iterator alias_item = m_objects_aliases.find(m_curr_object.id); + IdToAliasesMap::iterator alias_item = m_objects_aliases.find(object_id); if (alias_item != m_objects_aliases.end()) m_objects_aliases.erase(alias_item); } else // adds components to aliases - m_objects_aliases.insert({ m_curr_object.id, m_curr_object.components }); + m_objects_aliases.insert({ object_id, m_curr_object.components }); } else { // geometry defined, store it for later use - m_geometries.insert({ m_curr_object.id, std::move(m_curr_object.geometry) }); + m_geometries.insert({ object_id, std::move(m_curr_object.geometry) }); // stores the object for later use - if (m_objects.find(m_curr_object.id) == m_objects.end()) { - m_objects.insert({ m_curr_object.id, m_curr_object.model_object_idx }); - m_objects_aliases.insert({ m_curr_object.id, { 1, Component(m_curr_object.id) } }); // aliases itself + if (m_objects.find(object_id) == m_objects.end()) { + m_objects.insert({ object_id, m_curr_object.model_object_idx }); + m_objects_aliases.insert({object_id, {1, Component(object_id)}}); // aliases itself } else { add_error("Found object with duplicate id"); @@ -1868,19 +2012,21 @@ namespace Slic3r { bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes) { - int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); + std::string path = get_attribute_value_string(attributes, num_attributes, PPATH_ATTR); + int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); - - IdToModelObjectMap::iterator object_item = m_objects.find(object_id); + + PathId path_id { path, object_id }; + IdToModelObjectMap::iterator object_item = m_objects.find(path_id); if (object_item == m_objects.end()) { - IdToAliasesMap::iterator alias_item = m_objects_aliases.find(object_id); + IdToAliasesMap::iterator alias_item = m_objects_aliases.find(path_id); if (alias_item == m_objects_aliases.end()) { add_error("Found component with invalid object id"); return false; } } - m_curr_object.components.emplace_back(object_id, transform); + m_curr_object.components.emplace_back(path_id, transform); return true; } @@ -1916,7 +2062,7 @@ namespace Slic3r { Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); int printable = get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR); - return _create_object_instance(object_id, transform, printable, 1); + return _create_object_instance({m_model_path, object_id}, transform, printable, 1); } bool _3MF_Importer::_handle_end_item() @@ -2072,7 +2218,7 @@ namespace Slic3r { return true; } - bool _3MF_Importer::_create_object_instance(int object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter) + bool _3MF_Importer::_create_object_instance(PathId object_id, const Transform3d& transform, const bool printable, unsigned int recur_counter) { static const unsigned int MAX_RECURSIONS = 10; From 1696e0447bdd59815a254b9bbfc01b76160d44a9 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Mon, 12 Feb 2024 14:39:29 +0100 Subject: [PATCH 2/3] Use strict schema name. Limit file size of .rels Remove unneccesary end tag processing Skip errors from loadind relations(to support previous behavior without .rels file) Fix not setted PPath in component. Add support for PPath in build item --- src/libslic3r/Format/3mf.cpp | 78 ++++++++++++++---------------------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index f684b5e87f..0f9c3858ad 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -560,7 +560,6 @@ namespace Slic3r { // handlers to parse the .rels file void _handle_start_relationships_element(const char* name, const char** attributes); - void _handle_end_relationships_element(const char* name); bool _handle_start_relationship(const char **attributes, unsigned int num_attributes); // handlers to parse the .model file @@ -636,7 +635,6 @@ namespace Slic3r { // callbacks to parse the .rels file static void XMLCALL _handle_start_relationships_element(void *userData, const char *name, const char **attributes); - static void XMLCALL _handle_end_relationships_element(void* userData, const char* name); // callbacks to parse the .model file static void XMLCALL _handle_start_model_xml_element(void* userData, const char* name, const char** attributes); @@ -687,6 +685,7 @@ namespace Slic3r { m_sla_support_points.clear(); m_curr_metadata_name.clear(); m_curr_characters.clear(); + m_start_part_path = MODEL_FILE; // set default value for invalid .rel file clear_errors(); return _load_model_from_file(filename, model, config, config_substitutions); @@ -729,33 +728,26 @@ namespace Slic3r { int index = mz_zip_reader_locate_file(&archive, RELATIONSHIPS_FILE.c_str(), nullptr, 0); if (index < 0 || !mz_zip_reader_file_stat(&archive, index, &stat)) return false; - if (!_extract_relationships_from_archive(archive, stat)) - return false; - - // we first loop the entries to read from the archive the .model file only, in order to extract the version from it - mz_zip_archive_file_stat start_part_stat { -1 }; + + mz_zip_archive_file_stat start_part_stat{std::numeric_limits::max()}; + m_model_path = MODEL_FILE; + _extract_relationships_from_archive(archive, stat); bool found_model = false; + + // we first loop the entries to read from the .model files which are not root for (mz_uint i = 0; i < num_entries; ++i) { if (mz_zip_reader_file_stat(&archive, i, &stat)) { std::string name(stat.m_filename); std::replace(name.begin(), name.end(), '\\', '/'); if (boost::algorithm::iends_with(name, MODEL_EXTENSION)) { - if(found_model){ - close_zip_reader(&archive); - add_error("3mf contain multiple .model files and it is not supported yet."); - return false; + // valid model name -> extract model + m_model_path = "/" + name; + if (m_model_path == m_start_part_path) { + start_part_stat = stat; + continue; } - found_model = true; - - try - { - // valid model name -> extract model - m_model_path = "/" + name; - if (m_model_path == m_start_part_path) { - start_part_stat = stat; - continue; - } + try { if (!_extract_model_from_archive(archive, stat)) { close_zip_reader(&archive); add_error("Archive does not contain a valid model"); @@ -768,11 +760,13 @@ namespace Slic3r { close_zip_reader(&archive); throw Slic3r::FileIOError(e.what()); } + found_model = true; } } } - if (start_part_stat.m_file_index >= 0) { + // Read root model file + if (start_part_stat.m_file_index < num_entries) { try { m_model_path.clear(); if (!_extract_model_from_archive(archive, start_part_stat)) { @@ -785,6 +779,7 @@ namespace Slic3r { close_zip_reader(&archive); throw Slic3r::FileIOError(e.what()); } + found_model = true; } if (!found_model) { close_zip_reader(&archive); @@ -1013,7 +1008,9 @@ namespace Slic3r { bool _3MF_Importer::_extract_relationships_from_archive(mz_zip_archive &archive, const mz_zip_archive_file_stat &stat) { - if (stat.m_uncomp_size == 0) { + if (stat.m_uncomp_size == 0 || + stat.m_uncomp_size > 10000000 // Prevent overloading by big Relations file(>10MB). there is no reason to be soo big + ) { add_error("Found invalid size"); return false; } @@ -1027,7 +1024,7 @@ namespace Slic3r { } XML_SetUserData(m_xml_parser, (void *) this); - XML_SetElementHandler(m_xml_parser, _handle_start_relationships_element, _handle_end_relationships_element); + XML_SetStartElementHandler(m_xml_parser, _handle_start_relationships_element); void *parser_buffer = XML_GetBuffer(m_xml_parser, (int) stat.m_uncomp_size); if (parser_buffer == nullptr) { @@ -1613,20 +1610,13 @@ namespace Slic3r { } } - void XMLCALL _3MF_Importer::_handle_start_relationships_element(void *userData, const char *name, const char **attributes) + void XMLCALL _3MF_Importer::_handle_start_relationships_element(void *userData, const char *name, const char **attributes) { _3MF_Importer *importer = (_3MF_Importer *) userData; if (importer != nullptr) importer->_handle_start_relationships_element(name, attributes); } - void XMLCALL _3MF_Importer::_handle_end_relationships_element(void *userData, const char *name) - { - _3MF_Importer *importer = (_3MF_Importer *) userData; - if (importer != nullptr) - importer->_handle_end_relationships_element(name); - } - void _3MF_Importer::_handle_start_relationships_element(const char *name, const char **attributes) { if (m_xml_parser == nullptr) @@ -1643,22 +1633,12 @@ namespace Slic3r { _stop_xml_parser(); } - void _3MF_Importer::_handle_end_relationships_element(const char *name) - { - if (m_xml_parser == nullptr) - return; - - bool res = true; - - if (!res) - _stop_xml_parser(); - } - bool _3MF_Importer::_handle_start_relationship(const char **attributes, unsigned int num_attributes) { - std::string path = get_attribute_value_string(attributes, num_attributes, TARGET_ATTR); std::string type = get_attribute_value_string(attributes, num_attributes, RELS_TYPE_ATTR); - if (boost::starts_with(type, "http://schemas.microsoft.com/3dmanufacturing/") && boost::ends_with(type, "3dmodel")) { + // only exactly that string type mean root model file + if (type == "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dmodel") { + std::string path = get_attribute_value_string(attributes, num_attributes, TARGET_ATTR); m_start_part_path = path; } return true; @@ -2012,7 +1992,9 @@ namespace Slic3r { bool _3MF_Importer::_handle_start_component(const char** attributes, unsigned int num_attributes) { - std::string path = get_attribute_value_string(attributes, num_attributes, PPATH_ATTR); + std::string path = get_attribute_value_string(attributes, num_attributes, PPATH_ATTR); + if (path.empty()) path = m_model_path; + int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); @@ -2060,9 +2042,11 @@ namespace Slic3r { int object_id = get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); Transform3d transform = get_transform_from_3mf_specs_string(get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); + std::string path = get_attribute_value_string(attributes, num_attributes, PPATH_ATTR); + if (path.empty()) path = m_model_path; int printable = get_attribute_value_bool(attributes, num_attributes, PRINTABLE_ATTR); - return _create_object_instance({m_model_path, object_id}, transform, printable, 1); + return _create_object_instance({path, object_id}, transform, printable, 1); } bool _3MF_Importer::_handle_end_item() From 99a08efc9c983f1e9ac5efe2375c3a0045cad1a8 Mon Sep 17 00:00:00 2001 From: Filip Sykala - NTB T15p Date: Tue, 27 Feb 2024 13:51:16 +0100 Subject: [PATCH 3/3] Disallow import of 3mf without mesh, which are not produced by PrusaSlic3r --- src/libslic3r/Format/3mf.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/libslic3r/Format/3mf.cpp b/src/libslic3r/Format/3mf.cpp index 0f9c3858ad..e84ee5b4d5 100644 --- a/src/libslic3r/Format/3mf.cpp +++ b/src/libslic3r/Format/3mf.cpp @@ -1000,8 +1000,12 @@ namespace Slic3r { } } -// // fixes the min z of the model if negative -// model.adjust_min_z(); + // We support our 3mf contains only configuration without mesh, + // others MUST contain mesh (triangles and vertices). + if (!m_prusaslicer_generator_version.has_value() && model.objects.empty()) { + const std::string msg = (boost::format(_u8L("3mf File (\"%1%\") do not contain valid mesh.")) % filename).str(); + throw Slic3r::RuntimeError(msg); + } return true; }