diff --git a/src/BambuStudio.cpp b/src/BambuStudio.cpp index 4295026efb..58fa0b74b2 100644 --- a/src/BambuStudio.cpp +++ b/src/BambuStudio.cpp @@ -371,17 +371,15 @@ int CLI::run(int argc, char **argv) /*BOOST_LOG_TRIVIAL(info) << "begin to setup params, argc=" << argc << std::endl; for (int index=0; index < argc; index++) BOOST_LOG_TRIVIAL(info) << "index="<< index <<", arg is "<< argv[index] <setup(debug_argc, debug_argv))*/ if (!this->setup(argc, argv)) @@ -656,6 +654,7 @@ int CLI::run(int argc, char **argv) //BBS: add plate data related logic PlateDataPtrs plate_data_src; int arrange_option; + int plate_to_slice = 0; bool first_file = true, is_bbl_3mf = false, need_arrange = true, has_thumbnails = false; Semver file_version; std::map orients_requirement; @@ -663,6 +662,9 @@ int CLI::run(int argc, char **argv) // Read input file(s) if any. BOOST_LOG_TRIVIAL(info) << "Will start to read model file now, file count :" << m_input_files.size() << "\n"; + ConfigOptionInt* slice_option = m_config.option("slice"); + if (slice_option) + plate_to_slice = slice_option->value; /*for (const std::string& file : m_input_files) if (is_gcode_file(file) && boost::filesystem::exists(file)) { start_as_gcodeviewer = true; @@ -696,7 +698,7 @@ int CLI::run(int argc, char **argv) // BBS: adjust whebackup //LoadStrategy strategy = LoadStrategy::LoadModel | LoadStrategy::LoadConfig|LoadStrategy::AddDefaultInstances; //if (load_aux) strategy = strategy | LoadStrategy::LoadAuxiliary; - model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version); + model = Model::read_from_file(file, &config, &config_substitutions, strategy, &plate_data_src, &project_presets, &is_bbl_3mf, &file_version, nullptr, nullptr, nullptr, nullptr, nullptr, plate_to_slice); if (is_bbl_3mf) { if (!first_file) @@ -1240,7 +1242,6 @@ int CLI::run(int argc, char **argv) // loop through action options bool export_to_3mf = false; - int plate_to_slice = 0; std::string export_3mf_file; std::string outfile_dir = m_config.opt_string("outputdir"); std::vector calibration_thumbnails; diff --git a/src/libslic3r/Format/bbs_3mf.cpp b/src/libslic3r/Format/bbs_3mf.cpp index 23a7974393..f7dcea38aa 100644 --- a/src/libslic3r/Format/bbs_3mf.cpp +++ b/src/libslic3r/Format/bbs_3mf.cpp @@ -678,6 +678,147 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) typedef std::map> IdToSlaSupportPointsMap; typedef std::map> IdToSlaDrainHolesMap;*/ + struct ObjectImporter + { + IdToCurrentObjectMap object_list; + CurrentObject *current_object{nullptr}; + std::string object_path; + std::string zip_path; + _BBS_3MF_Importer *top_importer{nullptr}; + XML_Parser object_xml_parser; + bool obj_parse_error { false }; + std::string obj_parse_error_message; + + //local parsed datas + std::string obj_curr_metadata_name; + std::string obj_curr_characters; + float object_unit_factor; + int object_current_color_group{-1}; + std::map object_group_id_to_color; + bool is_bbl_3mf { false }; + + ObjectImporter(_BBS_3MF_Importer *importer, std::string file_path, std::string obj_path) + { + top_importer = importer; + object_path = obj_path; + zip_path = file_path; + } + + ~ObjectImporter() + { + _destroy_object_xml_parser(); + } + + void _destroy_object_xml_parser() + { + if (object_xml_parser != nullptr) { + XML_ParserFree(object_xml_parser); + object_xml_parser = nullptr; + } + } + + void _stop_object_xml_parser(const std::string& msg = std::string()) + { + assert(! obj_parse_error); + assert(obj_parse_error_message.empty()); + assert(object_xml_parser != nullptr); + obj_parse_error = true; + obj_parse_error_message = msg; + XML_StopParser(object_xml_parser, false); + } + + bool object_parse_error() const { return obj_parse_error; } + const char* object_parse_error_message() const { + return obj_parse_error ? + // The error was signalled by the user code, not the expat parser. + (obj_parse_error_message.empty() ? "Invalid 3MF format" : obj_parse_error_message.c_str()) : + // The error was signalled by the expat parser. + XML_ErrorString(XML_GetErrorCode(object_xml_parser)); + } + + bool _extract_object_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat); + + bool extract_object_model() + { + mz_zip_archive archive; + mz_zip_archive_file_stat stat; + mz_zip_zero_struct(&archive); + + if (!open_zip_reader(&archive, zip_path)) { + top_importer->add_error("Unable to open the zipfile "+ zip_path); + return false; + } + + if (!top_importer->_extract_from_archive(archive, object_path, [this] (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) { + return _extract_object_from_archive(archive, stat); + }, top_importer->m_load_restore)) { + std::string error_msg = std::string("Archive does not contain a valid model for ") + object_path; + top_importer->add_error(error_msg); + + close_zip_reader(&archive); + return false; + } + + close_zip_reader(&archive); + + if (obj_parse_error) { + //already add_error inside + //top_importer->add_error(object_parse_error_message()); + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", Found error while extrace object %1%\n")%object_path; + return false; + } + return true; + } + + bool _handle_object_start_model(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_model(); + + bool _handle_object_start_resources(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_resources(); + + bool _handle_object_start_object(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_object(); + + bool _handle_object_start_color_group(const char **attributes, unsigned int num_attributes); + bool _handle_object_end_color_group(); + + bool _handle_object_start_color(const char **attributes, unsigned int num_attributes); + bool _handle_object_end_color(); + + bool _handle_object_start_mesh(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_mesh(); + + bool _handle_object_start_vertices(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_vertices(); + + bool _handle_object_start_vertex(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_vertex(); + + bool _handle_object_start_triangles(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_triangles(); + + bool _handle_object_start_triangle(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_triangle(); + + bool _handle_object_start_components(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_components(); + + bool _handle_object_start_component(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_component(); + + bool _handle_object_start_metadata(const char** attributes, unsigned int num_attributes); + bool _handle_object_end_metadata(); + + void _handle_object_start_model_xml_element(const char* name, const char** attributes); + void _handle_object_end_model_xml_element(const char* name); + void _handle_object_xml_characters(const XML_Char* s, int len); + + // callbacks to parse the .model file of an object + static void XMLCALL _handle_object_start_model_xml_element(void* userData, const char* name, const char** attributes); + static void XMLCALL _handle_object_end_model_xml_element(void* userData, const char* name); + static void XMLCALL _handle_object_xml_characters(void* userData, const XML_Char* s, int len); + }; + // Version of the 3mf file unsigned int m_version; bool m_check_version; @@ -729,6 +870,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) std::string m_start_part_path; std::string m_thumbnail_path; std::vector m_sub_model_paths; + std::vector m_object_importers; std::map m_shared_meshes; @@ -748,7 +890,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //BBS: add plate data related logic // add backup & restore logic - bool load_model_from_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector& project_presets, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool& is_bbl_3mf, Semver& file_version, Import3mfProgressFn proFn = nullptr, BBLProject *project = nullptr); + bool load_model_from_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector& project_presets, DynamicPrintConfig& config, + ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool& is_bbl_3mf, Semver& file_version, Import3mfProgressFn proFn = nullptr, BBLProject *project = nullptr, int plate_id = 0); bool get_thumbnail(const std::string &filename, std::string &data); unsigned int version() const { return m_version; } @@ -768,7 +911,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //BBS: add plate data related logic // add backup & restore logic bool _load_model_from_file(std::string filename, Model& model, PlateDataPtrs& plate_data_list, std::vector& project_presets, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Import3mfProgressFn proFn = nullptr, - BBLProject* project = nullptr); + BBLProject* project = nullptr, int plate_id = 0); bool _extract_from_archive(mz_zip_archive& archive, std::string const & path, std::function, bool restore = false); bool _extract_xml_from_archive(mz_zip_archive& archive, std::string const & path, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler); bool _extract_xml_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, XML_StartElementHandler start_handler, XML_EndElementHandler end_handler); @@ -889,7 +1032,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) bool _handle_start_relationship(const char** attributes, unsigned int num_attributes); - void _generate_current_object_list(std::vector &sub_objects, Id object_id, IdToCurrentObjectMap current_objects); + void _generate_current_object_list(std::vector &sub_objects, Id object_id, IdToCurrentObjectMap& current_objects); bool _generate_volumes_new(ModelObject& object, const std::vector &sub_objects, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions); bool _generate_volumes(ModelObject& object, const Geometry& geometry, const ObjectMetadata::VolumeMetadataList& volumes, ConfigSubstitutionContext& config_substitutions); @@ -944,7 +1087,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) //BBS: add plate data related logic // add backup & restore logic - bool _BBS_3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector& project_presets, DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool& is_bbl_3mf, Semver& file_version, Import3mfProgressFn proFn, BBLProject *project) + bool _BBS_3MF_Importer::load_model_from_file(const std::string& filename, Model& model, PlateDataPtrs& plate_data_list, std::vector& project_presets, DynamicPrintConfig& config, + ConfigSubstitutionContext& config_substitutions, LoadStrategy strategy, bool& is_bbl_3mf, Semver& file_version, Import3mfProgressFn proFn, BBLProject *project, int plate_id) { m_version = 0; m_fdm_supports_painting_version = 0; @@ -993,7 +1137,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) else { m_backup_path = model.get_backup_path(); } - bool result = _load_model_from_file(filename, model, plate_data_list, project_presets, config, config_substitutions, proFn, project); + bool result = _load_model_from_file(filename, model, plate_data_list, project_presets, config, config_substitutions, proFn, project, plate_id); is_bbl_3mf = m_is_bbl_3mf; if (m_bambuslicer_generator_version) file_version = *m_bambuslicer_generator_version; @@ -1071,7 +1215,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) DynamicPrintConfig& config, ConfigSubstitutionContext& config_substitutions, Import3mfProgressFn proFn, - BBLProject *project) + BBLProject *project, + int plate_id) { bool cb_cancel = false; //BBS progress point @@ -1148,13 +1293,16 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) _extract_xml_from_archive(archive, sub_rels, _handle_start_relationships_element, _handle_end_relationships_element); int index = 0; +#if 0 for (auto path : m_sub_model_paths) { - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_READ_FILES\n"); if (proFn) { proFn(IMPORT_STAGE_READ_FILES, ++index, 3 + m_sub_model_paths.size(), cb_cancel); if (cb_cancel) return false; } + else + ++index; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", read %1%th sub model file %2%\n")%index %path; m_sub_model_path = path; if (!_extract_from_archive(archive, path, [this] (mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) { return _extract_model_from_archive(archive, stat); @@ -1164,6 +1312,43 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } m_sub_model_path.clear(); } +#else + for (auto path : m_sub_model_paths) { + ObjectImporter *object_importer = new ObjectImporter(this, filename, path); + m_object_importers.push_back(object_importer); + } + + bool object_load_result = true; + boost::mutex mutex; + tbb::parallel_for( + tbb::blocked_range(0, m_object_importers.size()), + [this, &mutex, &object_load_result](const tbb::blocked_range& importer_range) { + for (size_t object_index = importer_range.begin(); object_index < importer_range.end(); ++ object_index) { + bool result = m_object_importers[object_index]->extract_object_model(); + { + boost::unique_lock l(mutex); + object_load_result &= result; + } + } + } + ); + + if (!object_load_result) { + BOOST_LOG_TRIVIAL(error) << __FUNCTION__ << ":" << __LINE__ << boost::format(", loading sub-objects error\n"); + return false; + } + + //merge these objects into one + for (auto obj_importer : m_object_importers) { + for (const IdToCurrentObjectMap::value_type& obj : obj_importer->object_list) + m_current_objects.insert({ std::move(obj.first), std::move(obj.second)}); + for (auto group_color : obj_importer->object_group_id_to_color) + m_group_id_to_color.insert(std::move(group_color)); + + delete obj_importer; + } + m_object_importers.clear(); +#endif // BBS: load root model if (proFn) { proFn(IMPORT_STAGE_READ_FILES, 2, 3, cb_cancel); @@ -1222,7 +1407,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) std::string name(stat.m_filename); std::replace(name.begin(), name.end(), '\\', '/'); - BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("extract file %1%\n")%name; + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("extract %1%th file %2%, total=%3%\n")%(i+1)%name%num_entries; if (boost::algorithm::iequals(name, BBS_LAYER_HEIGHTS_PROFILE_FILE)) { // extract slic3r layer heights profile file @@ -1310,6 +1495,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) if (m_version == 0) { // if the 3mf was not produced by BambuStudio and there is more than one instance, // split the object in as many objects as instances + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", found 3mf from other vendor, split as instance"); for (const IdToModelObjectMap::value_type& object : m_objects) { if (object.second >= int(m_model->objects.size())) { add_error("3rd 3mf, invalid object, id: "+std::to_string(object.first.second)); @@ -1351,6 +1537,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", process group colors, size %1%\n")%m_group_id_to_color.size(); std::map color_group_id_to_extruder_id_map; std::map color_to_extruder_id_map; int extruder_id = 0; @@ -1365,11 +1552,28 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } } + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", begin to assemble objects, size %1%\n")%m_objects.size(); + //only load objects in plate_id + PlateData* current_plate_data = nullptr; + if ((plate_id > 0) && (plate_id <= m_plater_data.size())) { + std::map::iterator it =m_plater_data.find(plate_id); + if (it != m_plater_data.end()) { + current_plate_data = it->second; + } + } for (const IdToModelObjectMap::value_type& object : m_objects) { if (object.second >= int(m_model->objects.size())) { add_error("invalid object, id: "+std::to_string(object.first.second)); return false; } + if (current_plate_data) { + std::map::iterator it = current_plate_data->obj_inst_map.find(object.first.second); + if (it == current_plate_data->obj_inst_map.end()) { + //not in current plate, skip + BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format(", could not find object %1% in plate %2%, skip it\n")%object.first.second %plate_id; + continue; + } + } ModelObject* model_object = m_model->objects[object.second]; /*IdToGeometryMap::const_iterator obj_geometry = m_geometries.find(object.first); if (obj_geometry == m_geometries.end()) { @@ -1527,7 +1731,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } plate_data_list[it->first-1]->locked = it->second->locked; plate_data_list[it->first-1]->plate_index = it->second->plate_index-1; - plate_data_list[it->first-1]->objects_and_instances = it->second->objects_and_instances; + plate_data_list[it->first-1]->obj_inst_map = it->second->obj_inst_map; plate_data_list[it->first-1]->gcode_file = (m_load_restore || it->second->gcode_file.empty()) ? it->second->gcode_file : m_backup_path + "/" + it->second->gcode_file; plate_data_list[it->first-1]->gcode_prediction = it->second->gcode_prediction; plate_data_list[it->first-1]->gcode_weight = it->second->gcode_weight; @@ -1543,6 +1747,21 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) it++; } + if ((plate_id > 0) && (plate_id <= m_plater_data.size())) { + //remove the no need objects + std::vector delete_ids; + for (int index = 0; index < m_model->objects.size(); index++) { + ModelObject* obj = m_model->objects[index]; + if (obj->volumes.size() == 0) { + //remove this model objects + delete_ids.push_back(index); + } + } + + for (int index = delete_ids.size() - 1; index >= 0; index--) + m_model->delete_object(delete_ids[index]); + } + //BBS progress point BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__ << boost::format("import 3mf IMPORT_STAGE_FINISH\n"); if (proFn) { @@ -3171,7 +3390,8 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } else if (key == OBJECT_ID_ATTR) { - int obj_id = atoi(value.c_str()); + m_curr_instance.object_id = atoi(value.c_str()); + /*int obj_id = atoi(value.c_str()); m_curr_instance.object_id = -1; IndexToPathMap::iterator index_iter = m_index_paths.find(obj_id); if (index_iter == m_index_paths.end()) { @@ -3186,7 +3406,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) << boost::format(", can not find object for plate's item, ID <%1%, %2%>, skip this object")%index_iter->second %index_iter->first; return true; } - m_curr_instance.object_id = object_item->second; + m_curr_instance.object_id = object_item->second;*/ } else if (key == PLATE_IDX_ATTR) { @@ -3324,7 +3544,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } - m_curr_plater->objects_and_instances.emplace_back(m_curr_instance.object_id, m_curr_instance.instance_id); + m_curr_plater->obj_inst_map.emplace(m_curr_instance.object_id, m_curr_instance.instance_id); m_curr_instance.object_id = m_curr_instance.instance_id = -1; return true; } @@ -3428,7 +3648,7 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) return true; } - void _BBS_3MF_Importer::_generate_current_object_list(std::vector &sub_objects, Id object_id, IdToCurrentObjectMap current_objects) + void _BBS_3MF_Importer::_generate_current_object_list(std::vector &sub_objects, Id object_id, IdToCurrentObjectMap& current_objects) { std::list id_list; id_list.push_back({ object_id, Transform3d::Identity() }); @@ -3844,6 +4064,468 @@ void PlateData::parse_filament_info(GCodeProcessorResult *result) } + /* functions of ObjectImporter */ + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_model(const char** attributes, unsigned int num_attributes) + { + object_unit_factor = bbs_get_unit_factor(bbs_get_attribute_value_string(attributes, num_attributes, UNIT_ATTR)); + + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_model() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_resources(const char** attributes, unsigned int num_attributes) + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_resources() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_object(const char** attributes, unsigned int num_attributes) + { + // reset current object data + if (current_object) { + delete current_object; + current_object = nullptr; + } + + std::string object_type = bbs_get_attribute_value_string(attributes, num_attributes, TYPE_ATTR); + + if (bbs_is_valid_object_type(object_type)) { + if (!current_object) { + current_object = new CurrentObject(); + } + + current_object->id = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR); + current_object->name = bbs_get_attribute_value_string(attributes, num_attributes, NAME_ATTR); + + current_object->uuid = bbs_get_attribute_value_string(attributes, num_attributes, PUUID_ATTR); + current_object->pid = bbs_get_attribute_value_int(attributes, num_attributes, PID_ATTR); + } + + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_object() + { + if (!current_object || (current_object->id == -1)) { + top_importer->add_error("Found invalid object for "+ object_path); + return false; + } + else { + if (is_bbl_3mf && boost::ends_with(current_object->uuid, OBJECT_UUID_SUFFIX) && top_importer->m_load_restore) { + std::istringstream iss(current_object->uuid); + int backup_id; + bool need_replace = false; + if (iss >> std::hex >> backup_id) { + need_replace = (current_object->id != backup_id); + current_object->id = backup_id; + } + //if (need_replace) + { + for (int index = 0; index < current_object->components.size(); index++) + { + int temp_id = (index + 1) << 16 | backup_id; + Component& component = current_object->components[index]; + std::string new_path = component.object_id.first; + Id new_id = std::make_pair(new_path, temp_id); + IdToCurrentObjectMap::iterator object_it = object_list.find(component.object_id); + if (object_it != object_list.end()) { + CurrentObject new_object; + new_object.geometry = std::move(object_it->second.geometry); + new_object.id = temp_id; + new_object.model_object_idx = object_it->second.model_object_idx; + new_object.name = object_it->second.name; + new_object.uuid = object_it->second.uuid; + + object_list.erase(object_it); + object_list.insert({ new_id, std::move(new_object) }); + } + else { + top_importer->add_error("can not find object for component, id=" + std::to_string(component.object_id.second)); + delete current_object; + current_object = nullptr; + return false; + } + + component.object_id.second = temp_id; + } + } + } + Id id = std::make_pair(object_path, current_object->id); + if (object_list.find(id) == object_list.end()) { + object_list.insert({ id, std::move(*current_object) }); + delete current_object; + current_object = nullptr; + } + else { + top_importer->add_error("Found object with duplicate id for "+object_path); + delete current_object; + current_object = nullptr; + return false; + } + } + + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_color_group(const char **attributes, unsigned int num_attributes) + { + object_current_color_group = bbs_get_attribute_value_int(attributes, num_attributes, ID_ATTR); + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_color_group() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_color(const char **attributes, unsigned int num_attributes) + { + std::string color = bbs_get_attribute_value_string(attributes, num_attributes, COLOR_ATTR); + object_group_id_to_color[object_current_color_group] = color; + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_color() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_mesh(const char** attributes, unsigned int num_attributes) + { + // reset current geometry + if (current_object) + current_object->geometry.reset(); + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_mesh() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_vertices(const char** attributes, unsigned int num_attributes) + { + // reset current vertices + if (current_object) + current_object->geometry.vertices.clear(); + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_vertices() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_vertex(const char** attributes, unsigned int num_attributes) + { + // appends the vertex coordinates + // missing values are set equal to ZERO + if (current_object) + current_object->geometry.vertices.emplace_back( + object_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, X_ATTR), + object_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, Y_ATTR), + object_unit_factor * bbs_get_attribute_value_float(attributes, num_attributes, Z_ATTR)); + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_vertex() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_triangles(const char** attributes, unsigned int num_attributes) + { + // reset current triangles + if (current_object) + current_object->geometry.triangles.clear(); + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_triangles() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_triangle(const char** attributes, unsigned int num_attributes) + { + // we are ignoring the following attributes: + // p1 + // p2 + // p3 + // pid + // see specifications + + // appends the triangle's vertices indices + // missing values are set equal to ZERO + if (current_object) { + current_object->geometry.triangles.emplace_back( + bbs_get_attribute_value_int(attributes, num_attributes, V1_ATTR), + bbs_get_attribute_value_int(attributes, num_attributes, V2_ATTR), + bbs_get_attribute_value_int(attributes, num_attributes, V3_ATTR)); + + current_object->geometry.custom_supports.push_back(bbs_get_attribute_value_string(attributes, num_attributes, CUSTOM_SUPPORTS_ATTR)); + current_object->geometry.custom_seam.push_back(bbs_get_attribute_value_string(attributes, num_attributes, CUSTOM_SEAM_ATTR)); + current_object->geometry.mmu_segmentation.push_back(bbs_get_attribute_value_string(attributes, num_attributes, MMU_SEGMENTATION_ATTR)); + // BBS + current_object->geometry.face_properties.push_back(bbs_get_attribute_value_string(attributes, num_attributes, FACE_PROPERTY_ATTR)); + } + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_triangle() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_components(const char** attributes, unsigned int num_attributes) + { + // reset current components + if (current_object) + current_object->components.clear(); + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_components() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_component(const char** attributes, unsigned int num_attributes) + { + int object_id = bbs_get_attribute_value_int(attributes, num_attributes, OBJECTID_ATTR); + Transform3d transform = bbs_get_transform_from_3mf_specs_string(bbs_get_attribute_value_string(attributes, num_attributes, TRANSFORM_ATTR)); + + /*Id id = std::make_pair(m_sub_model_path, object_id); + IdToModelObjectMap::iterator object_item = m_objects.find(id); + if (object_item == m_objects.end()) { + IdToAliasesMap::iterator alias_item = m_objects_aliases.find(id); + if (alias_item == m_objects_aliases.end()) { + add_error("Found component with invalid object id"); + return false; + } + }*/ + + if (current_object) { + Id id = std::make_pair(object_path, object_id); + current_object->components.emplace_back(id, transform); + } + + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_component() + { + // do nothing + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_start_metadata(const char** attributes, unsigned int num_attributes) + { + obj_curr_metadata_name.clear(); + + std::string name = bbs_get_attribute_value_string(attributes, num_attributes, NAME_ATTR); + if (!name.empty()) { + obj_curr_metadata_name = name; + } + + return true; + } + + bool _BBS_3MF_Importer::ObjectImporter::_handle_object_end_metadata() + { + if ((obj_curr_metadata_name == BBS_3MF_VERSION)||(obj_curr_metadata_name == BBS_3MF_VERSION1)) { + is_bbl_3mf = true; + } + return true; + } + void _BBS_3MF_Importer::ObjectImporter::_handle_object_start_model_xml_element(const char* name, const char** attributes) + { + if (object_xml_parser == nullptr) + return; + + bool res = true; + unsigned int num_attributes = (unsigned int)XML_GetSpecifiedAttributeCount(object_xml_parser); + + if (::strcmp(MODEL_TAG, name) == 0) + res = _handle_object_start_model(attributes, num_attributes); + else if (::strcmp(RESOURCES_TAG, name) == 0) + res = _handle_object_start_resources(attributes, num_attributes); + else if (::strcmp(OBJECT_TAG, name) == 0) + res = _handle_object_start_object(attributes, num_attributes); + else if (::strcmp(COLOR_GROUP_TAG, name) == 0) + res = _handle_object_start_color_group(attributes, num_attributes); + else if (::strcmp(COLOR_TAG, name) == 0) + res = _handle_object_start_color(attributes, num_attributes); + else if (::strcmp(MESH_TAG, name) == 0) + res = _handle_object_start_mesh(attributes, num_attributes); + else if (::strcmp(VERTICES_TAG, name) == 0) + res = _handle_object_start_vertices(attributes, num_attributes); + else if (::strcmp(VERTEX_TAG, name) == 0) + res = _handle_object_start_vertex(attributes, num_attributes); + else if (::strcmp(TRIANGLES_TAG, name) == 0) + res = _handle_object_start_triangles(attributes, num_attributes); + else if (::strcmp(TRIANGLE_TAG, name) == 0) + res = _handle_object_start_triangle(attributes, num_attributes); + else if (::strcmp(COMPONENTS_TAG, name) == 0) + res = _handle_object_start_components(attributes, num_attributes); + else if (::strcmp(COMPONENT_TAG, name) == 0) + res = _handle_object_start_component(attributes, num_attributes); + else if (::strcmp(METADATA_TAG, name) == 0) + res = _handle_object_start_metadata(attributes, num_attributes); + + if (!res) + _stop_object_xml_parser(); + } + + void _BBS_3MF_Importer::ObjectImporter::_handle_object_end_model_xml_element(const char* name) + { + if (object_xml_parser == nullptr) + return; + + bool res = true; + + if (::strcmp(MODEL_TAG, name) == 0) + res = _handle_object_end_model(); + else if (::strcmp(RESOURCES_TAG, name) == 0) + res = _handle_object_end_resources(); + else if (::strcmp(OBJECT_TAG, name) == 0) + res = _handle_object_end_object(); + else if (::strcmp(COLOR_GROUP_TAG, name) == 0) + res = _handle_object_end_color_group(); + else if (::strcmp(COLOR_TAG, name) == 0) + res = _handle_object_end_color(); + else if (::strcmp(MESH_TAG, name) == 0) + res = _handle_object_end_mesh(); + else if (::strcmp(VERTICES_TAG, name) == 0) + res = _handle_object_end_vertices(); + else if (::strcmp(VERTEX_TAG, name) == 0) + res = _handle_object_end_vertex(); + else if (::strcmp(TRIANGLES_TAG, name) == 0) + res = _handle_object_end_triangles(); + else if (::strcmp(TRIANGLE_TAG, name) == 0) + res = _handle_object_end_triangle(); + else if (::strcmp(COMPONENTS_TAG, name) == 0) + res = _handle_object_end_components(); + else if (::strcmp(COMPONENT_TAG, name) == 0) + res = _handle_object_end_component(); + else if (::strcmp(METADATA_TAG, name) == 0) + res = _handle_object_end_metadata(); + + if (!res) + _stop_object_xml_parser(); + } + + void _BBS_3MF_Importer::ObjectImporter::_handle_object_xml_characters(const XML_Char* s, int len) + { + obj_curr_characters.append(s, len); + } + + void XMLCALL _BBS_3MF_Importer::ObjectImporter::_handle_object_start_model_xml_element(void* userData, const char* name, const char** attributes) + { + ObjectImporter* importer = (ObjectImporter*)userData; + if (importer != nullptr) + importer->_handle_object_start_model_xml_element(name, attributes); + } + + void XMLCALL _BBS_3MF_Importer::ObjectImporter::_handle_object_end_model_xml_element(void* userData, const char* name) + { + ObjectImporter* importer = (ObjectImporter*)userData; + if (importer != nullptr) + importer->_handle_object_end_model_xml_element(name); + } + + void XMLCALL _BBS_3MF_Importer::ObjectImporter::_handle_object_xml_characters(void* userData, const XML_Char* s, int len) + { + ObjectImporter* importer = (ObjectImporter*)userData; + if (importer != nullptr) + importer->_handle_object_xml_characters(s, len); + } + + bool _BBS_3MF_Importer::ObjectImporter::_extract_object_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat) + { + if (stat.m_uncomp_size == 0) { + top_importer->add_error("Found invalid size for "+object_path); + return false; + } + + object_xml_parser = XML_ParserCreate(nullptr); + if (object_xml_parser == nullptr) { + top_importer->add_error("Unable to create parser for "+object_path); + return false; + } + + XML_SetUserData(object_xml_parser, (void*)this); + XML_SetElementHandler(object_xml_parser, _BBS_3MF_Importer::ObjectImporter::_handle_object_start_model_xml_element, _BBS_3MF_Importer::ObjectImporter::_handle_object_end_model_xml_element); + XML_SetCharacterDataHandler(object_xml_parser, _BBS_3MF_Importer::ObjectImporter::_handle_object_xml_characters); + + struct CallbackData + { + XML_Parser& parser; + _BBS_3MF_Importer::ObjectImporter& importer; + const mz_zip_archive_file_stat& stat; + + CallbackData(XML_Parser& parser, _BBS_3MF_Importer::ObjectImporter& importer, const mz_zip_archive_file_stat& stat) : parser(parser), importer(importer), stat(stat) {} + }; + + CallbackData data(object_xml_parser, *this, stat); + + mz_bool res = 0; + + try + { + mz_file_write_func callback = [](void* pOpaque, mz_uint64 file_ofs, const void* pBuf, size_t n)->size_t { + CallbackData* data = (CallbackData*)pOpaque; + if (!XML_Parse(data->parser, (const char*)pBuf, (int)n, (file_ofs + n == data->stat.m_uncomp_size) ? 1 : 0) || data->importer.object_parse_error()) { + char error_buf[1024]; + ::sprintf(error_buf, "Error (%s) while parsing '%s' at line %d", data->importer.object_parse_error_message(), data->stat.m_filename, (int)XML_GetCurrentLineNumber(data->parser)); + throw Slic3r::FileIOError(error_buf); + } + return n; + }; + void* opaque = &data; + res = mz_zip_reader_extract_to_callback(&archive, stat.m_file_index, callback, opaque, 0); + } + catch (const version_error& e) + { + // rethrow the exception + std::string error_message = std::string(e.what()) + " for " + object_path; + throw Slic3r::FileIOError(error_message); + } + catch (std::exception& e) + { + std::string error_message = std::string(e.what()) + " for " + object_path; + top_importer->add_error(error_message); + return false; + } + + if (res == 0) { + top_importer->add_error("Error while extracting model data from zip archive for "+object_path); + return false; + } + + return true; + } + + class _BBS_3MF_Exporter : public _BBS_3MF_Base { struct BuildItem @@ -6328,7 +7010,8 @@ private: //BBS: add plate data list related logic -bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector* project_presets, bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn, LoadStrategy strategy, BBLProject *project) +bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector* project_presets, + bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn, LoadStrategy strategy, BBLProject *project, int plate_id) { if (path == nullptr || config == nullptr || model == nullptr) return false; @@ -6336,7 +7019,7 @@ bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstituti // All import should use "C" locales for number formatting. CNumericLocalesSetter locales_setter; _BBS_3MF_Importer importer; - bool res = importer.load_model_from_file(path, *model, *plate_data_list, *project_presets, *config, *config_substitutions, strategy, *is_bbl_3mf, *file_version, proFn, project); + bool res = importer.load_model_from_file(path, *model, *plate_data_list, *project_presets, *config, *config_substitutions, strategy, *is_bbl_3mf, *file_version, proFn, project, plate_id); importer.log_errors(); //BBS: remove legacy project logic currently //handle_legacy_project_loaded(importer.version(), *config); diff --git a/src/libslic3r/Format/bbs_3mf.hpp b/src/libslic3r/Format/bbs_3mf.hpp index 4ba109bc46..620468eccf 100644 --- a/src/libslic3r/Format/bbs_3mf.hpp +++ b/src/libslic3r/Format/bbs_3mf.hpp @@ -57,6 +57,7 @@ struct PlateData int plate_index; std::vector> objects_and_instances; + std::map obj_inst_map; std::string gcode_file; std::string gcode_file_md5; std::string thumbnail_file; @@ -204,7 +205,8 @@ struct StoreParams //BBS: add plate data list related logic // add restore logic // Load the content of a 3mf file into the given model and preset bundle. -extern bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector* project_presets, bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn = nullptr, LoadStrategy strategy = LoadStrategy::Default, BBLProject *project = nullptr); +extern bool load_bbs_3mf(const char* path, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, Model* model, PlateDataPtrs* plate_data_list, std::vector* project_presets, + bool* is_bbl_3mf, Semver* file_version, Import3mfProgressFn proFn = nullptr, LoadStrategy strategy = LoadStrategy::Default, BBLProject *project = nullptr, int plate_id = 0); extern std::string bbs_3mf_get_thumbnail(const char * path); diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index a86fcf25ff..8d726089f3 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -138,7 +138,7 @@ Model::~Model() // Loading model from a file, it may be a simple geometry file as STL or OBJ, however it may be a project file as well. Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, LoadStrategy options, PlateDataPtrs* plate_data, std::vector* project_presets, bool *is_xxx, Semver* file_version, Import3mfProgressFn proFn, - ImportstlProgressFn stlFn, ImportStepProgressFn stepFn, StepIsUtf8Fn stepIsUtf8Fn, BBLProject* project) + ImportstlProgressFn stlFn, ImportStepProgressFn stepFn, StepIsUtf8Fn stepIsUtf8Fn, BBLProject* project, int plate_id) { Model model; @@ -180,7 +180,7 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c // BBS: backup & restore //FIXME options & LoadStrategy::CheckVersion ? //BBS: is_xxx is used for is_bbs_3mf when load 3mf - result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, is_xxx, file_version, proFn, options, project); + result = load_bbs_3mf(input_file.c_str(), config, config_substitutions, &model, plate_data, project_presets, is_xxx, file_version, proFn, options, project, plate_id); else throw Slic3r::RuntimeError("Unknown file format. Input file must have .stl, .obj, .amf(.xml) extension."); @@ -211,7 +211,8 @@ Model Model::read_from_file(const std::string& input_file, DynamicPrintConfig* c //BBS: add part plate related logic // BBS: backup & restore // Loading model from a file (3MF or AMF), not from a simple geometry file (STL or OBJ). -Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, En3mfType& out_file_type, LoadStrategy options, PlateDataPtrs* plate_data, std::vector* project_presets, Semver* file_version, Import3mfProgressFn proFn, BBLProject *project) +Model Model::read_from_archive(const std::string& input_file, DynamicPrintConfig* config, ConfigSubstitutionContext* config_substitutions, En3mfType& out_file_type, LoadStrategy options, + PlateDataPtrs* plate_data, std::vector* project_presets, Semver* file_version, Import3mfProgressFn proFn, BBLProject *project) { assert(config != nullptr); assert(config_substitutions != nullptr); diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 1364c80b98..5c8ab6c40a 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -1307,7 +1307,7 @@ public: DynamicPrintConfig* config = nullptr, ConfigSubstitutionContext* config_substitutions = nullptr, LoadStrategy options = LoadStrategy::AddDefaultInstances, PlateDataPtrs* plate_data = nullptr, std::vector* project_presets = nullptr, bool* is_xxx = nullptr, Semver* file_version = nullptr, Import3mfProgressFn proFn = nullptr, - ImportstlProgressFn stlFn = nullptr, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr); + ImportstlProgressFn stlFn = nullptr, ImportStepProgressFn stepFn = nullptr, StepIsUtf8Fn stepIsUtf8Fn = nullptr, BBLProject* project = nullptr, int plate_id = 0); // BBS static double findMaxSpeed(const ModelObject* object); static double getThermalLength(const ModelVolume* modelVolumePtr);