When importing 3mf which had texture mapping zones, make sure to assign or create a zone so textures are still visible

This commit is contained in:
sentientstardust
2026-05-21 21:34:59 +01:00
parent 06e7a6d611
commit 31969e8ae0
2 changed files with 172 additions and 7 deletions

View File

@@ -1821,6 +1821,7 @@ static void append_triangle_material_data(std::vector<uint8_t> &uv_valid,
void _extract_print_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, const std::string& archive_filename);
//BBS: add project config file logic
void _extract_project_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config, ConfigSubstitutionContext& subs_context, Model& model);
void _extract_texture_mapping_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config);
//BBS: extract project embedded presets
void _extract_project_embedded_presets_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, std::vector<Preset*>&project_presets, Model& model, Preset::Type type, bool use_json = true);
@@ -2591,9 +2592,12 @@ static void append_triangle_material_data(std::vector<uint8_t> &uv_valid,
// extract slic3r print config file
// _extract_print_config_from_archive(archive, stat, config, config_substitutions, filename);
//} else
if (!dont_load_config && boost::algorithm::iequals(name, BBS_PROJECT_CONFIG_FILE)) {
if (boost::algorithm::iequals(name, BBS_PROJECT_CONFIG_FILE)) {
// extract slic3r print config file
_extract_project_config_from_archive(archive, stat, config, config_substitutions, model);
if (!dont_load_config)
_extract_project_config_from_archive(archive, stat, config, config_substitutions, model);
else
_extract_texture_mapping_config_from_archive(archive, stat, config);
}
else if (boost::algorithm::iequals(name, CUT_INFORMATION_FILE)) {
// extract object cut info
@@ -3331,6 +3335,37 @@ static void append_triangle_material_data(std::vector<uint8_t> &uv_valid,
}
}
void _BBS_3MF_Importer::_extract_texture_mapping_config_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, DynamicPrintConfig& config)
{
if (stat.m_uncomp_size == 0)
return;
std::string buffer(size_t(stat.m_uncomp_size), 0);
mz_bool res = mz_zip_reader_extract_to_mem(&archive, stat.m_file_index, (void *)buffer.data(), size_t(stat.m_uncomp_size), 0);
if (res == 0)
return;
try {
const nlohmann::json root = nlohmann::json::parse(buffer);
const auto texture_defs_it = root.find("texture_mapping_definitions");
if (texture_defs_it != root.end() && texture_defs_it->is_string())
config.set_key_value("texture_mapping_definitions", new ConfigOptionString(texture_defs_it->get<std::string>()));
const auto filament_colour_it = root.find("filament_colour");
if (filament_colour_it != root.end() && filament_colour_it->is_array()) {
std::vector<std::string> colors;
colors.reserve(filament_colour_it->size());
for (const nlohmann::json &entry : *filament_colour_it)
if (entry.is_string())
colors.emplace_back(entry.get<std::string>());
if (!colors.empty())
config.set_key_value("filament_colour", new ConfigOptionStrings(std::move(colors)));
}
} catch (const std::exception &e) {
BOOST_LOG_TRIVIAL(warning) << "_extract_texture_mapping_config_from_archive JSON parse failed: " << e.what();
}
}
//BBS: extract project embedded presets
void _BBS_3MF_Importer::_extract_project_embedded_presets_from_archive(mz_zip_archive& archive, const mz_zip_archive_file_stat& stat, std::vector<Preset*>&project_presets, Model& model, Preset::Type type, bool use_json)
{

View File

@@ -641,9 +641,9 @@ static bool assign_imported_texture_mapping_zone(Model &model)
return true;
}
static bool texture_mapping_config_uses_zone_id(const ConfigOptionResolver &config, unsigned int zone_id)
static const std::array<const char *, 6> &texture_mapping_filament_config_keys()
{
static constexpr std::array<const char *, 6> filament_keys = {
static const std::array<const char *, 6> filament_keys = {
"extruder",
"wall_filament",
"sparse_infill_filament",
@@ -651,8 +651,13 @@ static bool texture_mapping_config_uses_zone_id(const ConfigOptionResolver &conf
"support_filament",
"support_interface_filament"
};
return filament_keys;
}
static bool texture_mapping_config_uses_zone_id(const ConfigOptionResolver &config, unsigned int zone_id)
{
const int target = int(zone_id);
for (const char *key : filament_keys) {
for (const char *key : texture_mapping_filament_config_keys()) {
const ConfigOptionInt *opt = dynamic_cast<const ConfigOptionInt *>(config.option(key));
if (opt != nullptr && opt->getInt() == target)
return true;
@@ -687,6 +692,121 @@ static bool model_uses_texture_mapping_zone_id(const Model &model, const ConfigO
return false;
}
static std::set<unsigned int> texture_mapping_zone_ids_from_import_config(const DynamicPrintConfig &config)
{
const ConfigOptionString *defs = config.option<ConfigOptionString>("texture_mapping_definitions");
if (defs == nullptr || defs->value.empty())
return {};
std::set<unsigned int> ids;
try {
const json root = json::parse(defs->value);
if (!root.is_array())
return {};
for (const json &entry : root) {
if (!entry.is_object() || !entry.value("enabled", true))
continue;
const unsigned int zone_id = entry.value("zone_id", entry.value("filament_id", 0u));
if (zone_id != 0)
ids.insert(zone_id);
}
} catch (...) {
ids.clear();
}
return ids;
}
static bool store_texture_mapping_definitions(PresetBundle &bundle)
{
const std::string serialized = bundle.texture_mapping_zones.serialize_entries();
DynamicPrintConfig &project_config = bundle.project_config;
if (ConfigOptionString *opt = project_config.option<ConfigOptionString>("texture_mapping_definitions"))
opt->value = serialized;
else
project_config.set_key_value("texture_mapping_definitions", new ConfigOptionString(serialized));
DynamicPrintConfig &print_config = bundle.prints.get_edited_preset().config;
if (ConfigOptionString *opt = print_config.option<ConfigOptionString>("texture_mapping_definitions"))
opt->value = serialized;
else
print_config.set_key_value("texture_mapping_definitions", new ConfigOptionString(serialized));
return true;
}
static unsigned int ensure_texture_mapping_import_target_zone(PresetBundle &bundle, bool &changed)
{
DynamicPrintConfig &project_config = bundle.project_config;
const ConfigOptionStrings *color_opt = project_config.option<ConfigOptionStrings>("filament_colour", false);
if (color_opt == nullptr || color_opt->values.size() < 2)
return 0;
project_config.option<ConfigOptionString>("texture_mapping_definitions", true);
bundle.texture_mapping_zones.load_entries(project_config.opt_string("texture_mapping_definitions"), color_opt->values);
for (const unsigned int zone_id : bundle.texture_mapping_zones.zone_ids_by_index())
if (zone_id != 0)
return zone_id;
const unsigned int zone_id = bundle.texture_mapping_zones.ensure_image_texture_zone(color_opt->values.size(), color_opt->values);
if (zone_id != 0)
changed |= store_texture_mapping_definitions(bundle);
return zone_id;
}
static bool remap_texture_mapping_model_config(ModelConfig &config, const std::set<unsigned int> &source_zone_ids, unsigned int target_zone_id)
{
bool changed = false;
for (const char *key : texture_mapping_filament_config_keys()) {
const ConfigOptionInt *opt = dynamic_cast<const ConfigOptionInt *>(config.option(key));
if (opt == nullptr || source_zone_ids.find(unsigned(opt->getInt())) == source_zone_ids.end())
continue;
if (opt->getInt() != int(target_zone_id)) {
config.set(key, int(target_zone_id));
changed = true;
}
}
return changed;
}
static bool assign_imported_3mf_texture_mapping_zones(Model &model, const std::set<unsigned int> &source_zone_ids)
{
if (source_zone_ids.empty())
return false;
std::set<unsigned int> used_zone_ids;
for (const unsigned int zone_id : source_zone_ids)
if (model_uses_texture_mapping_zone_id(model, nullptr, zone_id))
used_zone_ids.insert(zone_id);
if (used_zone_ids.empty())
return false;
PresetBundle *bundle = wxGetApp().preset_bundle;
if (bundle == nullptr)
return false;
bool changed = false;
const unsigned int target_zone_id = ensure_texture_mapping_import_target_zone(*bundle, changed);
if (target_zone_id == 0)
return changed;
for (ModelObject *object : model.objects) {
if (object == nullptr)
continue;
changed |= remap_texture_mapping_model_config(object->config, used_zone_ids, target_zone_id);
for (auto &layer_range : object->layer_config_ranges)
changed |= remap_texture_mapping_model_config(layer_range.second, used_zone_ids, target_zone_id);
for (ModelVolume *volume : object->volumes)
if (volume != nullptr)
changed |= remap_texture_mapping_model_config(volume->config, used_zone_ids, target_zone_id);
}
std::map<unsigned int, unsigned int> state_map;
for (const unsigned int zone_id : used_zone_ids)
if (zone_id != target_zone_id)
state_map[zone_id] = target_zone_id;
changed |= remap_mmu_segmentation_filaments(model, state_map);
return changed;
}
static wxColour parse_texture_mapping_color(const std::string &hex)
{
unsigned char rgba[4] = {38, 166, 154, 255};
@@ -8781,6 +8901,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// const bool type_prusa = std::regex_match(path.string(), pattern_prusa);
Slic3r::Model model;
std::set<unsigned int> imported_texture_mapping_zone_ids;
// BBS: add auxiliary files related logic
bool load_aux = strategy & LoadStrategy::LoadAuxiliary, load_old_project = false;
if (load_model && load_config && type_3mf) {
@@ -8818,9 +8939,14 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
if (cancel)
is_user_cancel = cancel;
});
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__
BOOST_LOG_TRIVIAL(info) << __FUNCTION__ << ":" << __LINE__
<< boost::format(", plate_data.size %1%, project_preset.size %2%, is_bbs_or_orca_3mf %3%, file_version %4% \n") % plate_data.size() %
project_presets.size() % (en_3mf_file_type == En3mfType::From_BBS || en_3mf_file_type == En3mfType::From_Orca) % file_version.to_string();
imported_texture_mapping_zone_ids = texture_mapping_zone_ids_from_import_config(config_loaded);
if (load_model && !load_config && assign_imported_3mf_texture_mapping_zones(model, imported_texture_mapping_zone_ids)) {
sidebar->update_texture_mapping_panel(false);
sidebar->update_dynamic_filament_list();
}
// 1. add extruder for prusa model if the number of existing extruders is not enough
// 2. add extruder for BBS or Other model if only import geometry
@@ -9551,7 +9677,7 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
// convert_model_if(model, answer_convert_from_imperial_units == wxID_YES);
}
if (!is_project_file && model.looks_like_multipart_object()) {
if (!is_project_file && model.looks_like_multipart_object()) {
MessageDialog msg_dlg(q, _L(
"This file contains several objects positioned at multiple heights.\n"
"Instead of considering them as multiple objects, should \n"
@@ -9561,6 +9687,10 @@ std::vector<size_t> Plater::priv::load_files(const std::vector<fs::path>& input_
model.convert_multipart_object(filaments_cnt);
}
}
if (type_3mf && load_model && !load_config && assign_imported_3mf_texture_mapping_zones(model, imported_texture_mapping_zone_ids)) {
sidebar->update_texture_mapping_panel(false);
sidebar->update_dynamic_filament_list();
}
}
// else if ((wxGetApp().get_mode() == comSimple) && (type_3mf || type_any_amf) && model_has_advanced_features(model)) {
// MessageDialog msg_dlg(q, _L("This file cannot be loaded in a simple mode. Do you want to switch to an advanced mode?")+"\n",