Allow manually generating a new UV map for image data
This commit is contained in:
@@ -431,6 +431,7 @@ static constexpr const char* SOURCE_OFFSET_Y_KEY = "source_offset_y";
|
||||
static constexpr const char* SOURCE_OFFSET_Z_KEY = "source_offset_z";
|
||||
static constexpr const char* SOURCE_IN_INCHES = "source_in_inches";
|
||||
static constexpr const char* SOURCE_IN_METERS = "source_in_meters";
|
||||
static constexpr const char* UV_MAP_GENERATOR_VERSION_KEY = "uv_map_generator_version";
|
||||
|
||||
static constexpr const char *MATERIALS_NAMESPACE = "http://schemas.microsoft.com/3dmanufacturing/material/2015/02";
|
||||
static constexpr const char *MODEL_TEXTURE_REL_TYPE = "http://schemas.microsoft.com/3dmanufacturing/2013/01/3dtexture";
|
||||
@@ -5910,6 +5911,8 @@ static void append_triangle_material_data(std::vector<uint8_t> &uv_valid,
|
||||
volume->source.is_converted_from_inches = metadata.value == "1";
|
||||
else if (metadata.key == SOURCE_IN_METERS)
|
||||
volume->source.is_converted_from_meters = metadata.value == "1";
|
||||
else if (metadata.key == UV_MAP_GENERATOR_VERSION_KEY)
|
||||
volume->uv_map_generator_version = std::max(0, ::atoi(metadata.value.c_str()));
|
||||
else if ((metadata.key == MATRIX_KEY) || (metadata.key == MESH_SHARED_KEY))
|
||||
continue;
|
||||
else
|
||||
@@ -6126,6 +6129,8 @@ static void append_triangle_material_data(std::vector<uint8_t> &uv_valid,
|
||||
volume->source.is_converted_from_inches = metadata.value == "1";
|
||||
else if (metadata.key == SOURCE_IN_METERS)
|
||||
volume->source.is_converted_from_meters = metadata.value == "1";
|
||||
else if (metadata.key == UV_MAP_GENERATOR_VERSION_KEY)
|
||||
volume->uv_map_generator_version = std::max(0, ::atoi(metadata.value.c_str()));
|
||||
else
|
||||
volume->config.set_deserialize(metadata.key, metadata.value, config_substitutions);
|
||||
}
|
||||
@@ -7984,7 +7989,8 @@ static void append_triangle_material_data(std::vector<uint8_t> &uv_valid,
|
||||
&& (shared_volume->imported_texture_width == volume->imported_texture_width)
|
||||
&& (shared_volume->imported_texture_height == volume->imported_texture_height)
|
||||
&& (shared_volume->imported_texture_raw_channels == volume->imported_texture_raw_channels)
|
||||
&& (shared_volume->imported_texture_raw_metadata_json == volume->imported_texture_raw_metadata_json))
|
||||
&& (shared_volume->imported_texture_raw_metadata_json == volume->imported_texture_raw_metadata_json)
|
||||
&& (shared_volume->uv_map_generator_version == volume->uv_map_generator_version))
|
||||
{
|
||||
auto data = iter->second.first;
|
||||
const_cast<_BBS_3MF_Exporter *>(this)->m_volume_paths.insert({volume, {data->sub_path, data->volumes_objectID.find(iter->second.second)->second}});
|
||||
@@ -9286,6 +9292,10 @@ static void append_triangle_material_data(std::vector<uint8_t> &uv_valid,
|
||||
}
|
||||
stream << "\"/>\n";
|
||||
|
||||
if (volume->uv_map_generator_version > 0)
|
||||
stream << " <" << METADATA_TAG << " " << KEY_ATTR << "=\"" << UV_MAP_GENERATOR_VERSION_KEY << "\" "
|
||||
<< VALUE_ATTR << "=\"" << volume->uv_map_generator_version << "\"/>\n";
|
||||
|
||||
// stores volume's source data
|
||||
{
|
||||
auto file_path =get_dealed_platform_path(volume->source.input_file);
|
||||
|
||||
@@ -845,6 +845,7 @@ Model Model::read_from_file(const std::string&
|
||||
volume->imported_texture_height = 0;
|
||||
volume->imported_texture_raw_channels = 0;
|
||||
volume->imported_texture_raw_metadata_json.clear();
|
||||
volume->uv_map_generator_version = 0;
|
||||
bool has_imported_usable_uv_texture_data = false;
|
||||
|
||||
const size_t triangle_count = volume->mesh().its.indices.size();
|
||||
@@ -882,6 +883,7 @@ Model Model::read_from_file(const std::string&
|
||||
volume->imported_texture_raw_filament_offsets.clear();
|
||||
volume->imported_texture_raw_channels = 0;
|
||||
volume->imported_texture_raw_metadata_json.clear();
|
||||
volume->uv_map_generator_version = 0;
|
||||
has_imported_usable_uv_texture_data = has_any_valid_uv_face;
|
||||
}
|
||||
}
|
||||
@@ -4918,7 +4920,8 @@ static bool model_volume_texture_mapping_data_matches(const ModelVolume &mv_old,
|
||||
mv_old.imported_texture_width == mv_new.imported_texture_width &&
|
||||
mv_old.imported_texture_height == mv_new.imported_texture_height &&
|
||||
mv_old.imported_texture_raw_channels == mv_new.imported_texture_raw_channels &&
|
||||
mv_old.imported_texture_raw_metadata_json == mv_new.imported_texture_raw_metadata_json;
|
||||
mv_old.imported_texture_raw_metadata_json == mv_new.imported_texture_raw_metadata_json &&
|
||||
mv_old.uv_map_generator_version == mv_new.uv_map_generator_version;
|
||||
}
|
||||
|
||||
bool model_texture_mapping_color_data_changed(const ModelObject& mo, const ModelObject& mo_new)
|
||||
|
||||
@@ -1065,6 +1065,7 @@ public:
|
||||
uint32_t imported_texture_height{0};
|
||||
uint32_t imported_texture_raw_channels{0};
|
||||
std::string imported_texture_raw_metadata_json;
|
||||
int uv_map_generator_version{0};
|
||||
|
||||
// List of mesh facets painted for fuzzy skin.
|
||||
FacetsAnnotation fuzzy_skin_facets;
|
||||
@@ -1317,6 +1318,7 @@ private:
|
||||
imported_texture_height(other.imported_texture_height),
|
||||
imported_texture_raw_channels(other.imported_texture_raw_channels),
|
||||
imported_texture_raw_metadata_json(other.imported_texture_raw_metadata_json),
|
||||
uv_map_generator_version(other.uv_map_generator_version),
|
||||
fuzzy_skin_facets(other.fuzzy_skin_facets), cut_info(other.cut_info), text_configuration(other.text_configuration), emboss_shape(other.emboss_shape)
|
||||
{
|
||||
assert(this->id().valid());
|
||||
@@ -1417,7 +1419,7 @@ private:
|
||||
cereal::load_by_value(ar, imported_texture_uv_valid);
|
||||
cereal::load_by_value(ar, imported_texture_rgba);
|
||||
cereal::load_by_value(ar, imported_texture_raw_filament_offsets);
|
||||
ar(imported_texture_width, imported_texture_height, imported_texture_raw_channels, imported_texture_raw_metadata_json);
|
||||
ar(imported_texture_width, imported_texture_height, imported_texture_raw_channels, imported_texture_raw_metadata_json, uv_map_generator_version);
|
||||
cereal::load_by_value(ar, fuzzy_skin_facets);
|
||||
mesh_changed |= t != fuzzy_skin_facets.timestamp();
|
||||
cereal::load_by_value(ar, config);
|
||||
@@ -1434,6 +1436,7 @@ private:
|
||||
imported_texture_height = 0;
|
||||
imported_texture_raw_channels = 0;
|
||||
imported_texture_raw_metadata_json.clear();
|
||||
uv_map_generator_version = 0;
|
||||
texture_mapping_color_facets.reset();
|
||||
}
|
||||
assert(m_mesh);
|
||||
@@ -1459,7 +1462,7 @@ private:
|
||||
cereal::save_by_value(ar, imported_texture_uv_valid);
|
||||
cereal::save_by_value(ar, imported_texture_rgba);
|
||||
cereal::save_by_value(ar, imported_texture_raw_filament_offsets);
|
||||
ar(imported_texture_width, imported_texture_height, imported_texture_raw_channels, imported_texture_raw_metadata_json);
|
||||
ar(imported_texture_width, imported_texture_height, imported_texture_raw_channels, imported_texture_raw_metadata_json, uv_map_generator_version);
|
||||
cereal::save_by_value(ar, fuzzy_skin_facets);
|
||||
cereal::save_by_value(ar, config);
|
||||
cereal::save(ar, text_configuration);
|
||||
|
||||
@@ -4354,20 +4354,26 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
||||
#else
|
||||
size_t num_filaments = this->filament_presets.size();
|
||||
#endif
|
||||
if (to_delete_filament_id == -1)
|
||||
if (to_delete_filament_id == size_t(-1))
|
||||
to_delete_filament_id = num_filaments;
|
||||
|
||||
// Now verify if flush_volumes_matrix has proper size (it is used to deduce number of extruders in wipe tower generator):
|
||||
std::vector<double> old_matrix = this->project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values;
|
||||
size_t old_nozzle_nums = this->project_config.option<ConfigOptionFloats>("flush_multiplier")->values.size();
|
||||
size_t old_number_of_filaments = size_t(sqrt(old_matrix.size() / old_nozzle_nums) + EPSILON);
|
||||
std::vector<double>& f_multiplier = this->project_config.option<ConfigOptionFloats>("flush_multiplier")->values;
|
||||
size_t old_nozzle_nums = std::max<size_t>(f_multiplier.size(), 1);
|
||||
size_t nozzle_nums = get_printer_extruder_count();
|
||||
if (old_nozzle_nums != nozzle_nums) {
|
||||
std::vector<double>& f_multiplier = this->project_config.option<ConfigOptionFloats>("flush_multiplier")->values;
|
||||
f_multiplier.resize(nozzle_nums, 1.f);
|
||||
if (f_multiplier.size() != nozzle_nums) {
|
||||
const double multiplier = f_multiplier.empty() ? 1. : f_multiplier.back();
|
||||
f_multiplier.resize(nozzle_nums, multiplier);
|
||||
}
|
||||
|
||||
if ( (num_filaments * num_filaments) != size_t(old_matrix.size() / old_nozzle_nums) ) {
|
||||
const size_t old_matrix_values_per_nozzle = old_matrix.size() / old_nozzle_nums;
|
||||
const size_t old_number_of_filaments = size_t(sqrt(old_matrix_values_per_nozzle) + EPSILON);
|
||||
const size_t old_matrix_size = old_number_of_filaments * old_number_of_filaments;
|
||||
const size_t new_matrix_size = num_filaments * num_filaments;
|
||||
const size_t expected_matrix_size = new_matrix_size * nozzle_nums;
|
||||
|
||||
if (old_matrix.size() != expected_matrix_size) {
|
||||
// First verify if purging volumes presets for each extruder matches number of extruders
|
||||
std::vector<double>& filaments = this->project_config.option<ConfigOptionFloats>("flush_volumes_vector")->values;
|
||||
while (filaments.size() < 2* num_filaments) {
|
||||
@@ -4379,21 +4385,22 @@ void PresetBundle::update_multi_material_filament_presets(size_t to_delete_filam
|
||||
filaments.pop_back();
|
||||
}
|
||||
|
||||
size_t old_matrix_size = old_number_of_filaments * old_number_of_filaments;
|
||||
size_t new_matrix_size = num_filaments * num_filaments;
|
||||
std::vector<double> new_matrix(new_matrix_size * nozzle_nums, 0);
|
||||
for (unsigned int i = 0; i < num_filaments; ++i)
|
||||
for (unsigned int j = 0; j < num_filaments; ++j) {
|
||||
if (i < old_number_of_filaments && j < old_number_of_filaments) {
|
||||
unsigned int old_i = i >= to_delete_filament_id ? i + 1 : i;
|
||||
unsigned int old_j = j >= to_delete_filament_id ? j + 1 : j;
|
||||
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
|
||||
new_matrix[i * num_filaments + j + new_matrix_size * nozzle_id] = old_matrix[old_i * old_number_of_filaments + old_j + old_matrix_size * nozzle_id];
|
||||
}
|
||||
} else {
|
||||
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
|
||||
new_matrix[i * num_filaments + j + new_matrix_size * nozzle_id] = (i == j ? 0. : filaments[2 * i] + filaments[2 * j + 1]);
|
||||
unsigned int old_i = i >= to_delete_filament_id ? i + 1 : i;
|
||||
unsigned int old_j = j >= to_delete_filament_id ? j + 1 : j;
|
||||
for (size_t nozzle_id = 0; nozzle_id < nozzle_nums; ++nozzle_id) {
|
||||
double value = i == j ? 0. : filaments[2 * i] + filaments[2 * j + 1];
|
||||
if (old_matrix_size == old_matrix_values_per_nozzle &&
|
||||
old_i < old_number_of_filaments &&
|
||||
old_j < old_number_of_filaments) {
|
||||
const size_t source_nozzle_id = std::min(nozzle_id, old_nozzle_nums - 1);
|
||||
const size_t old_idx = old_i * old_number_of_filaments + old_j + old_matrix_size * source_nozzle_id;
|
||||
if (old_idx < old_matrix.size())
|
||||
value = old_matrix[old_idx];
|
||||
}
|
||||
new_matrix[i * num_filaments + j + new_matrix_size * nozzle_id] = value;
|
||||
}
|
||||
}
|
||||
this->project_config.option<ConfigOptionFloats>("flush_volumes_matrix")->values = new_matrix;
|
||||
|
||||
@@ -283,7 +283,8 @@ static bool model_volume_texture_mapping_data_equal_for_print_sharing(const Mode
|
||||
lhs.imported_texture_width == rhs.imported_texture_width &&
|
||||
lhs.imported_texture_height == rhs.imported_texture_height &&
|
||||
lhs.imported_texture_raw_channels == rhs.imported_texture_raw_channels &&
|
||||
lhs.imported_texture_raw_metadata_json == rhs.imported_texture_raw_metadata_json;
|
||||
lhs.imported_texture_raw_metadata_json == rhs.imported_texture_raw_metadata_json &&
|
||||
lhs.uv_map_generator_version == rhs.uv_map_generator_version;
|
||||
}
|
||||
|
||||
template class PrintState<PrintStep, psCount>;
|
||||
|
||||
@@ -92,6 +92,7 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst,
|
||||
mv_dst.imported_texture_height = mv_src.imported_texture_height;
|
||||
mv_dst.imported_texture_raw_channels = mv_src.imported_texture_raw_channels;
|
||||
mv_dst.imported_texture_raw_metadata_json = mv_src.imported_texture_raw_metadata_json;
|
||||
mv_dst.uv_map_generator_version = mv_src.uv_map_generator_version;
|
||||
assert(mv_dst.fuzzy_skin_facets.id() == mv_src.fuzzy_skin_facets.id());
|
||||
mv_dst.fuzzy_skin_facets.assign(mv_src.fuzzy_skin_facets);
|
||||
//FIXME what to do with the materials?
|
||||
|
||||
@@ -110,6 +110,17 @@ static int menu_item_position(wxMenu *menu, const wxString &name)
|
||||
return wxNOT_FOUND;
|
||||
}
|
||||
|
||||
static int first_separator_position(wxMenu *menu)
|
||||
{
|
||||
int position = 0;
|
||||
for (auto node = menu->GetMenuItems().GetFirst(); node; node = node->GetNext(), ++position) {
|
||||
wxMenuItem *item = node->GetData();
|
||||
if (item != nullptr && item->IsSeparator())
|
||||
return position;
|
||||
}
|
||||
return wxNOT_FOUND;
|
||||
}
|
||||
|
||||
static ModelObject *selected_managed_color_data_object()
|
||||
{
|
||||
Plater *app_plater = wxGetApp().plater();
|
||||
@@ -1210,13 +1221,18 @@ void MenuFactory::append_menu_item_manage_color_data(wxMenu *menu)
|
||||
if (selected_managed_color_data_object() == nullptr)
|
||||
return;
|
||||
|
||||
int insert_pos = menu_item_position(menu, _L("Flush Options"));
|
||||
int insert_pos = first_separator_position(menu);
|
||||
if (insert_pos != wxNOT_FOUND)
|
||||
++insert_pos;
|
||||
else {
|
||||
insert_pos = menu_item_position(menu, _L("Edit in Parameter Table"));
|
||||
insert_pos = menu_item_position(menu, _L("Flush Options"));
|
||||
if (insert_pos != wxNOT_FOUND)
|
||||
++insert_pos;
|
||||
else {
|
||||
insert_pos = menu_item_position(menu, _L("Edit in Parameter Table"));
|
||||
if (insert_pos != wxNOT_FOUND)
|
||||
++insert_pos;
|
||||
}
|
||||
}
|
||||
|
||||
auto can_manage_color_data = []() {
|
||||
|
||||
@@ -61,6 +61,25 @@ static std::vector<ColorRGBA> get_extruders_colors()
|
||||
return wxGetApp().plater() != nullptr ? wxGetApp().plater()->get_extruders_colors() : std::vector<ColorRGBA>{};
|
||||
}
|
||||
|
||||
static size_t extruder_color_index_for_filament_id(unsigned int filament_id, size_t color_count)
|
||||
{
|
||||
if (color_count == 0)
|
||||
return 0;
|
||||
|
||||
if (filament_id >= 1 && filament_id <= color_count)
|
||||
return size_t(filament_id - 1);
|
||||
|
||||
if (wxGetApp().preset_bundle != nullptr) {
|
||||
const size_t physical_count = size_t(std::max(wxGetApp().filaments_cnt(), 0));
|
||||
const unsigned int resolved_id =
|
||||
wxGetApp().preset_bundle->texture_mapping_zones.resolve_zone_component(filament_id, physical_count, 0);
|
||||
if (resolved_id >= 1 && resolved_id <= color_count)
|
||||
return size_t(resolved_id - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void GLGizmoMmuSegmentation::on_opening()
|
||||
{
|
||||
if (get_extruders_colors().size() > GLGizmoMmuSegmentation::EXTRUDERS_LIMIT)
|
||||
@@ -2226,6 +2245,11 @@ static ColorRGBA sample_rgba_bilinear_clamped(const std::vector<uint8_t> &rgba,
|
||||
a);
|
||||
}
|
||||
|
||||
static ColorRGBA sample_rgba_bilinear_wrapped(const std::vector<uint8_t> &rgba, uint32_t width, uint32_t height, float u, float v)
|
||||
{
|
||||
return sample_rgba_bilinear_clamped(rgba, width, height, wrap_texture_uv_for_vertex_bake(u), wrap_texture_uv_for_vertex_bake(v));
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> sample_raw_offsets_bilinear_clamped(const ImageMapRawFilamentOffsetAtlas &atlas, float u, float v)
|
||||
{
|
||||
std::vector<uint8_t> values;
|
||||
@@ -2258,6 +2282,43 @@ static std::vector<uint8_t> sample_raw_offsets_bilinear_clamped(const ImageMapRa
|
||||
return values;
|
||||
}
|
||||
|
||||
static std::vector<uint8_t> sample_raw_offsets_bilinear_wrapped(const std::vector<uint8_t> &offsets,
|
||||
uint32_t width,
|
||||
uint32_t height,
|
||||
uint32_t channels,
|
||||
float u,
|
||||
float v)
|
||||
{
|
||||
std::vector<uint8_t> values(size_t(channels), 0);
|
||||
if (width == 0 || height == 0 || channels == 0 ||
|
||||
offsets.size() < size_t(width) * size_t(height) * size_t(channels))
|
||||
return values;
|
||||
|
||||
u = wrap_texture_uv_for_vertex_bake(u);
|
||||
v = wrap_texture_uv_for_vertex_bake(v);
|
||||
const float x = u * float(width > 1 ? width - 1 : 0);
|
||||
const float y = v * float(height > 1 ? height - 1 : 0);
|
||||
const size_t x0 = std::min<size_t>(size_t(std::floor(x)), size_t(width - 1));
|
||||
const size_t y0 = std::min<size_t>(size_t(std::floor(y)), size_t(height - 1));
|
||||
const size_t x1 = std::min<size_t>(x0 + 1, size_t(width - 1));
|
||||
const size_t y1 = std::min<size_t>(y0 + 1, size_t(height - 1));
|
||||
const float tx = x - float(x0);
|
||||
const float ty = y - float(y0);
|
||||
|
||||
auto channel = [&offsets, width, channels](size_t sx, size_t sy, size_t ch) {
|
||||
return float(offsets[(sy * size_t(width) + sx) * size_t(channels) + ch]);
|
||||
};
|
||||
for (size_t ch = 0; ch < values.size(); ++ch) {
|
||||
const float c00 = channel(x0, y0, ch);
|
||||
const float c10 = channel(x1, y0, ch);
|
||||
const float c01 = channel(x0, y1, ch);
|
||||
const float c11 = channel(x1, y1, ch);
|
||||
const float value = (c00 + (c10 - c00) * tx) + ((c01 + (c11 - c01) * tx) - (c00 + (c10 - c00) * tx)) * ty;
|
||||
values[ch] = uint8_t(std::clamp(int(std::lround(value)), 0, 255));
|
||||
}
|
||||
return values;
|
||||
}
|
||||
|
||||
static ColorRGBA blend_projection_color(const ColorRGBA &base, const ColorRGBA &overlay, float opacity)
|
||||
{
|
||||
const float alpha = std::clamp(overlay.a(), 0.f, 1.f) * std::clamp(opacity, 0.f, 1.f);
|
||||
@@ -2875,6 +2936,7 @@ static ColorRGBA projection_base_color_for_volume(const ModelVolume &volume)
|
||||
}
|
||||
|
||||
static constexpr uint32_t GENERATED_IMAGE_TEXTURE_SIZE = 4096;
|
||||
static constexpr int GENERATED_IMAGE_TEXTURE_UV_MAP_VERSION = 1;
|
||||
|
||||
struct GeneratedImageTextureIsland
|
||||
{
|
||||
@@ -3113,6 +3175,7 @@ static bool initialize_generated_image_texture(ModelVolume &volum
|
||||
volume.imported_texture_width = GENERATED_IMAGE_TEXTURE_SIZE;
|
||||
volume.imported_texture_height = GENERATED_IMAGE_TEXTURE_SIZE;
|
||||
volume.imported_texture_rgba.assign(size_t(GENERATED_IMAGE_TEXTURE_SIZE) * size_t(GENERATED_IMAGE_TEXTURE_SIZE) * 4, 0);
|
||||
volume.uv_map_generator_version = GENERATED_IMAGE_TEXTURE_UV_MAP_VERSION;
|
||||
clear_imported_texture_raw_atlas(volume);
|
||||
for (size_t idx = 0; idx < size_t(GENERATED_IMAGE_TEXTURE_SIZE) * size_t(GENERATED_IMAGE_TEXTURE_SIZE); ++idx) {
|
||||
volume.imported_texture_rgba[idx * 4 + 0] = r;
|
||||
@@ -4593,6 +4656,7 @@ static bool clear_object_managed_color_data(ModelObject &object, ManagedColorDat
|
||||
clear_imported_texture_raw_atlas(*volume);
|
||||
volume->imported_texture_width = 0;
|
||||
volume->imported_texture_height = 0;
|
||||
volume->uv_map_generator_version = 0;
|
||||
changed = true;
|
||||
}
|
||||
break;
|
||||
@@ -5370,6 +5434,190 @@ static bool convert_object_to_image_texture(ModelObject &object, const ManagedCo
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool object_has_regenerable_image_texture_uvs(const ModelObject &object)
|
||||
{
|
||||
for (const ModelVolume *volume : object.volumes)
|
||||
if (volume != nullptr && volume->is_model_part() && model_volume_has_bakeable_image_texture_data(volume))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static wxString managed_image_texture_uv_map_info_text(const ModelObject *object)
|
||||
{
|
||||
if (object == nullptr)
|
||||
return _L("UV map: none");
|
||||
|
||||
bool has_texture = false;
|
||||
bool has_imported = false;
|
||||
bool has_generated = false;
|
||||
int generated_version = -1;
|
||||
bool mixed_generated_versions = false;
|
||||
for (const ModelVolume *volume : object->volumes) {
|
||||
if (volume == nullptr || !volume->is_model_part() || !model_volume_has_imported_image_texture_data(volume))
|
||||
continue;
|
||||
|
||||
has_texture = true;
|
||||
if (volume->uv_map_generator_version > 0) {
|
||||
has_generated = true;
|
||||
if (generated_version < 0)
|
||||
generated_version = volume->uv_map_generator_version;
|
||||
else if (generated_version != volume->uv_map_generator_version)
|
||||
mixed_generated_versions = true;
|
||||
} else {
|
||||
has_imported = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!has_texture)
|
||||
return _L("UV map: none");
|
||||
if (has_imported && has_generated)
|
||||
return _L("UV map: mixed");
|
||||
if (has_generated) {
|
||||
if (mixed_generated_versions)
|
||||
return _L("UV map: generated (mixed)");
|
||||
return wxString::Format(_L("UV map: generated (v%d)"), generated_version);
|
||||
}
|
||||
return _L("UV map: imported");
|
||||
}
|
||||
|
||||
static bool regenerate_volume_image_texture_uv_map(ModelObject &object, ModelVolume &volume)
|
||||
{
|
||||
if (!model_volume_has_bakeable_image_texture_data(&volume))
|
||||
return false;
|
||||
|
||||
const indexed_triangle_set &its = volume.mesh().its;
|
||||
const uint32_t source_width = volume.imported_texture_width;
|
||||
const uint32_t source_height = volume.imported_texture_height;
|
||||
const std::vector<uint8_t> source_rgba(volume.imported_texture_rgba.begin(), volume.imported_texture_rgba.end());
|
||||
const std::vector<float> source_uvs(volume.imported_texture_uvs_per_face.begin(), volume.imported_texture_uvs_per_face.end());
|
||||
const std::vector<uint8_t> source_uv_valid(volume.imported_texture_uv_valid.begin(), volume.imported_texture_uv_valid.end());
|
||||
const bool source_has_raw_atlas = model_volume_has_raw_atlas_texture_data(&volume);
|
||||
const uint32_t source_raw_channels = volume.imported_texture_raw_channels;
|
||||
const std::vector<uint8_t> source_raw_offsets(volume.imported_texture_raw_filament_offsets.begin(),
|
||||
volume.imported_texture_raw_filament_offsets.end());
|
||||
const std::string source_raw_metadata = volume.imported_texture_raw_metadata_json;
|
||||
|
||||
GeneratedImageTextureAtlas generated_atlas;
|
||||
Transform3d metric_matrix = volume.get_matrix();
|
||||
if (!object.instances.empty() && object.instances.front() != nullptr)
|
||||
metric_matrix = object.instances.front()->get_transformation().get_matrix() * metric_matrix;
|
||||
if (!initialize_generated_image_texture(volume,
|
||||
blank_color_for_managed_target(ManagedColorDataType::ImageTexture),
|
||||
&generated_atlas,
|
||||
&metric_matrix))
|
||||
return false;
|
||||
|
||||
if (source_has_raw_atlas) {
|
||||
volume.imported_texture_raw_channels = source_raw_channels;
|
||||
volume.imported_texture_raw_metadata_json = source_raw_metadata;
|
||||
volume.imported_texture_raw_filament_offsets.assign(size_t(volume.imported_texture_width) *
|
||||
size_t(volume.imported_texture_height) *
|
||||
size_t(source_raw_channels),
|
||||
0);
|
||||
}
|
||||
|
||||
bool changed = true;
|
||||
const float target_width = float(volume.imported_texture_width);
|
||||
const float target_height = float(volume.imported_texture_height);
|
||||
for (const GeneratedImageTextureIsland &island : generated_atlas.islands) {
|
||||
const size_t tri_idx = island.tri_idx;
|
||||
if (tri_idx >= its.indices.size() ||
|
||||
tri_idx >= source_uv_valid.size() ||
|
||||
source_uv_valid[tri_idx] == 0)
|
||||
continue;
|
||||
|
||||
const size_t source_uv_offset = tri_idx * 6;
|
||||
const size_t target_uv_offset = tri_idx * 6;
|
||||
if (source_uv_offset + 5 >= source_uvs.size() ||
|
||||
target_uv_offset + 5 >= volume.imported_texture_uvs_per_face.size())
|
||||
continue;
|
||||
|
||||
const std::array<Vec2f, 3> source_face_uvs = unwrap_projection_uvs(std::array<Vec2f, 3>{
|
||||
Vec2f(source_uvs[source_uv_offset + 0], source_uvs[source_uv_offset + 1]),
|
||||
Vec2f(source_uvs[source_uv_offset + 2], source_uvs[source_uv_offset + 3]),
|
||||
Vec2f(source_uvs[source_uv_offset + 4], source_uvs[source_uv_offset + 5])
|
||||
});
|
||||
const std::array<Vec2f, 3> target_pixel_uvs = {
|
||||
Vec2f(volume.imported_texture_uvs_per_face[target_uv_offset + 0] * target_width,
|
||||
volume.imported_texture_uvs_per_face[target_uv_offset + 1] * target_height),
|
||||
Vec2f(volume.imported_texture_uvs_per_face[target_uv_offset + 2] * target_width,
|
||||
volume.imported_texture_uvs_per_face[target_uv_offset + 3] * target_height),
|
||||
Vec2f(volume.imported_texture_uvs_per_face[target_uv_offset + 4] * target_width,
|
||||
volume.imported_texture_uvs_per_face[target_uv_offset + 5] * target_height)
|
||||
};
|
||||
|
||||
const int min_x = std::clamp(island.x, 0, int(volume.imported_texture_width) - 1);
|
||||
const int max_x = std::clamp(island.x + island.rect_width - 1, 0, int(volume.imported_texture_width) - 1);
|
||||
const int min_y = std::clamp(island.y, 0, int(volume.imported_texture_height) - 1);
|
||||
const int max_y = std::clamp(island.y + island.rect_height - 1, 0, int(volume.imported_texture_height) - 1);
|
||||
for (int y_px = min_y; y_px <= max_y; ++y_px) {
|
||||
for (int x_px = min_x; x_px <= max_x; ++x_px) {
|
||||
Vec3f barycentric = Vec3f::Zero();
|
||||
const Vec2f pixel(float(x_px) + 0.5f, float(y_px) + 0.5f);
|
||||
if (!barycentric_weights_2d(pixel, target_pixel_uvs[0], target_pixel_uvs[1], target_pixel_uvs[2], barycentric))
|
||||
continue;
|
||||
if (barycentric.x() < -1e-4f || barycentric.y() < -1e-4f || barycentric.z() < -1e-4f)
|
||||
barycentric = normalized_nonnegative_barycentric(barycentric);
|
||||
|
||||
const Vec3f safe_barycentric =
|
||||
texture_barycentric_for_bleed_safe_sampling(barycentric,
|
||||
source_face_uvs[0],
|
||||
source_face_uvs[1],
|
||||
source_face_uvs[2],
|
||||
source_width,
|
||||
source_height);
|
||||
const Vec2f source_uv = source_face_uvs[0] * safe_barycentric.x() +
|
||||
source_face_uvs[1] * safe_barycentric.y() +
|
||||
source_face_uvs[2] * safe_barycentric.z();
|
||||
if (source_has_raw_atlas) {
|
||||
const std::vector<uint8_t> values =
|
||||
sample_raw_offsets_bilinear_wrapped(source_raw_offsets,
|
||||
source_width,
|
||||
source_height,
|
||||
source_raw_channels,
|
||||
source_uv.x(),
|
||||
source_uv.y());
|
||||
write_raw_offset_pixel(volume.imported_texture_raw_filament_offsets,
|
||||
volume.imported_texture_width,
|
||||
source_raw_channels,
|
||||
uint32_t(x_px),
|
||||
uint32_t(y_px),
|
||||
values);
|
||||
} else {
|
||||
const ColorRGBA color = sample_rgba_bilinear_wrapped(source_rgba,
|
||||
source_width,
|
||||
source_height,
|
||||
source_uv.x(),
|
||||
source_uv.y());
|
||||
write_rgba_pixel(volume.imported_texture_rgba,
|
||||
volume.imported_texture_width,
|
||||
uint32_t(x_px),
|
||||
uint32_t(y_px),
|
||||
color);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (source_has_raw_atlas) {
|
||||
refresh_imported_texture_preview_from_raw_offsets(volume);
|
||||
refresh_imported_texture_raw_storage(volume);
|
||||
}
|
||||
refresh_imported_texture_storage(volume);
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool regenerate_object_image_texture_uv_maps(ModelObject &object)
|
||||
{
|
||||
bool changed = false;
|
||||
for (ModelVolume *volume : object.volumes) {
|
||||
if (volume == nullptr || !volume->is_model_part())
|
||||
continue;
|
||||
changed |= regenerate_volume_image_texture_uv_map(object, *volume);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
static bool convert_object_to_rgba_data(ModelObject &object, const ManagedColorDataCreateSource &source)
|
||||
{
|
||||
if (object_has_rgba_data(object))
|
||||
@@ -5823,6 +6071,7 @@ private:
|
||||
wxStaticText *size = nullptr;
|
||||
wxButton *clear = nullptr;
|
||||
wxButton *create = nullptr;
|
||||
wxButton *actions = nullptr;
|
||||
};
|
||||
|
||||
void add_raw_image_texture_info_row(wxFlexGridSizer *grid)
|
||||
@@ -5852,17 +6101,27 @@ private:
|
||||
wxStaticText *size = new wxStaticText(this, wxID_ANY, wxString());
|
||||
wxButton *clear = new wxButton(this, wxID_ANY, _L("Clear"));
|
||||
wxButton *create = new wxButton(this, wxID_ANY, _L("Create From..."));
|
||||
wxButton *actions = nullptr;
|
||||
wxBoxSizer *size_sizer = new wxBoxSizer(wxHORIZONTAL);
|
||||
size_sizer->Add(size, 1, wxALIGN_CENTER_VERTICAL);
|
||||
if (type == ManagedColorDataType::ImageTexture) {
|
||||
actions = new wxButton(this, wxID_ANY, wxString::FromUTF8("\xE2\x96\xBE"), wxDefaultPosition, wxDefaultSize, wxBU_EXACTFIT);
|
||||
actions->SetToolTip(_L("Image texture actions"));
|
||||
size_sizer->Add(actions, 0, wxALIGN_CENTER_VERTICAL | wxLEFT, FromDIP(6));
|
||||
}
|
||||
|
||||
grid->Add(new wxStaticText(this, wxID_ANY, label), 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(status, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(preview, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(size, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(size_sizer, 1, wxEXPAND | wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(clear, 0, wxALIGN_CENTER_VERTICAL);
|
||||
grid->Add(create, 0, wxALIGN_CENTER_VERTICAL);
|
||||
|
||||
clear->Bind(wxEVT_BUTTON, [this, type](wxCommandEvent &) { clear_data(type); });
|
||||
create->Bind(wxEVT_BUTTON, [this, type, create](wxCommandEvent &) { show_create_menu(type, create); });
|
||||
m_rows.push_back({ type, status, preview, size, clear, create });
|
||||
if (actions != nullptr)
|
||||
actions->Bind(wxEVT_BUTTON, [this, actions](wxCommandEvent &) { show_image_texture_menu(actions); });
|
||||
m_rows.push_back({ type, status, preview, size, clear, create, actions });
|
||||
}
|
||||
|
||||
void refresh_rows()
|
||||
@@ -5877,6 +6136,8 @@ private:
|
||||
row.size->SetLabel(managed_color_data_size_text(summary, row.type));
|
||||
row.clear->Enable(has_data);
|
||||
row.create->Enable(m_object != nullptr && !has_data);
|
||||
if (row.actions != nullptr)
|
||||
row.actions->Enable(m_object != nullptr);
|
||||
}
|
||||
const bool show_raw_info = summary.has_raw_offset_image_texture;
|
||||
if (m_raw_image_texture_status != nullptr)
|
||||
@@ -5959,6 +6220,39 @@ private:
|
||||
button->PopupMenu(&menu, wxPoint(0, button->GetSize().GetHeight()));
|
||||
}
|
||||
|
||||
void show_image_texture_menu(wxButton *button)
|
||||
{
|
||||
if (button == nullptr)
|
||||
return;
|
||||
|
||||
wxMenu menu;
|
||||
wxMenuItem *info = menu.Append(wxWindow::NewControlId(), managed_image_texture_uv_map_info_text(m_object));
|
||||
info->Enable(false);
|
||||
menu.AppendSeparator();
|
||||
|
||||
const int generate_id = wxWindow::NewControlId();
|
||||
wxMenuItem *generate = menu.Append(generate_id, _L("Generate new UV Map"));
|
||||
generate->Enable(m_object != nullptr && object_has_regenerable_image_texture_uvs(*m_object));
|
||||
menu.Bind(wxEVT_COMMAND_MENU_SELECTED, [this, generate_id](wxCommandEvent &event) {
|
||||
if (event.GetId() == generate_id)
|
||||
generate_new_uv_map();
|
||||
});
|
||||
button->PopupMenu(&menu, wxPoint(0, button->GetSize().GetHeight()));
|
||||
}
|
||||
|
||||
void generate_new_uv_map()
|
||||
{
|
||||
if (m_object == nullptr || !object_has_regenerable_image_texture_uvs(*m_object))
|
||||
return;
|
||||
|
||||
Plater::TakeSnapshot snapshot(wxGetApp().plater(), "Generate new image texture UV map", UndoRedo::SnapshotType::GizmoAction);
|
||||
if (!regenerate_object_image_texture_uv_maps(*m_object))
|
||||
return;
|
||||
|
||||
refresh_object_after_change();
|
||||
refresh_rows();
|
||||
}
|
||||
|
||||
void create_data(ManagedColorDataType type, const ManagedColorDataCreateSource &source)
|
||||
{
|
||||
if (m_object == nullptr || object_has_managed_color_data(*m_object, type) || (source.type && *source.type == type))
|
||||
@@ -6891,9 +7185,10 @@ void GLGizmoMmuSegmentation::init_model_triangle_selectors()
|
||||
if (!mv->is_model_part())
|
||||
continue;
|
||||
|
||||
int extruder_idx = (mv->extruder_id() > 0) ? mv->extruder_id() - 1 : 0;
|
||||
const unsigned int extruder_id = mv->extruder_id() > 0 ? unsigned(mv->extruder_id()) : 1u;
|
||||
const size_t extruder_color_id = extruder_color_index_for_filament_id(extruder_id, m_extruders_colors.size());
|
||||
std::vector<ColorRGBA> ebt_colors;
|
||||
ebt_colors.push_back(m_extruders_colors[size_t(extruder_idx)]);
|
||||
ebt_colors.push_back(m_extruders_colors[extruder_color_id]);
|
||||
ebt_colors.insert(ebt_colors.end(), m_extruders_colors.begin(), m_extruders_colors.end());
|
||||
|
||||
// This mesh does not account for the possible Z up SLA offset.
|
||||
@@ -6910,10 +7205,14 @@ void GLGizmoMmuSegmentation::init_model_triangle_selectors()
|
||||
|
||||
void GLGizmoMmuSegmentation::update_triangle_selectors_colors()
|
||||
{
|
||||
if (m_extruders_colors.empty())
|
||||
return;
|
||||
|
||||
for (int i = 0; i < m_triangle_selectors.size(); i++) {
|
||||
TriangleSelectorPatch* selector = dynamic_cast<TriangleSelectorPatch*>(m_triangle_selectors[i].get());
|
||||
int extruder_idx = m_volumes_extruder_idxs[i];
|
||||
int extruder_color_idx = std::max(0, extruder_idx - 1);
|
||||
int extruder_idx = i < int(m_volumes_extruder_idxs.size()) ? m_volumes_extruder_idxs[i] : 1;
|
||||
const unsigned int extruder_id = extruder_idx > 0 ? unsigned(extruder_idx) : 1u;
|
||||
const size_t extruder_color_idx = extruder_color_index_for_filament_id(extruder_id, m_extruders_colors.size());
|
||||
std::vector<ColorRGBA> ebt_colors;
|
||||
ebt_colors.push_back(m_extruders_colors[extruder_color_idx]);
|
||||
ebt_colors.insert(ebt_colors.end(), m_extruders_colors.begin(), m_extruders_colors.end());
|
||||
@@ -7201,6 +7500,7 @@ void GLGizmoMmuSegmentation::bake_selected_object_image_texture_to_vertex_colors
|
||||
clear_imported_texture_raw_atlas(*volume);
|
||||
volume->imported_texture_width = 0;
|
||||
volume->imported_texture_height = 0;
|
||||
volume->uv_map_generator_version = 0;
|
||||
baked = true;
|
||||
}
|
||||
|
||||
@@ -7564,6 +7864,7 @@ void GLGizmoMmuSegmentation::clear_selected_object_image_texture_data()
|
||||
clear_imported_texture_raw_atlas(*volume);
|
||||
volume->imported_texture_width = 0;
|
||||
volume->imported_texture_height = 0;
|
||||
volume->uv_map_generator_version = 0;
|
||||
cleared = true;
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ set(SLIC3R_APP_KEY "OrcaSlicer")
|
||||
if(NOT DEFINED BBL_INTERNAL_TESTING)
|
||||
set(BBL_INTERNAL_TESTING "0")
|
||||
endif()
|
||||
set(SoftFever_VERSION "1.0.0")
|
||||
set(SoftFever_VERSION "1.0.5")
|
||||
string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)"
|
||||
SoftFever_VERSION_MATCH ${SoftFever_VERSION})
|
||||
set(ORCA_VERSION_MAJOR ${CMAKE_MATCH_1})
|
||||
|
||||
Reference in New Issue
Block a user