22fae3ddb Add 'Manage Color Data' dialog for converting between color data types

This commit is contained in:
sentientstardust
2026-05-02 13:56:53 +01:00
parent 0fbc85c0a0
commit 195b5868ac
12 changed files with 1920 additions and 146 deletions

View File

@@ -25,8 +25,8 @@
inkscape:cy="23.971385"
inkscape:window-width="1135"
inkscape:window-height="616"
inkscape:window-x="558"
inkscape:window-y="275"
inkscape:window-x="555"
inkscape:window-y="322"
inkscape:window-maximized="0"
inkscape:current-layer="svg17" />
<defs
@@ -39,7 +39,7 @@
x="3.9266238"
y="5.0815129" />
<path
style="fill:#2b3436;fill-opacity:1;stroke:#2b3436;stroke-opacity:1"
style="fill:none;fill-opacity:1;stroke:#2b3436;stroke-opacity:1"
d="m 26.446966,11.89536 v 17.136303 l 8.376353,4.936095 0.05434,-17.914796 z"
id="path20"
sodipodi:nodetypes="ccccc" />

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -25,8 +25,8 @@
inkscape:cy="23.971385"
inkscape:window-width="1135"
inkscape:window-height="616"
inkscape:window-x="558"
inkscape:window-y="275"
inkscape:window-x="555"
inkscape:window-y="322"
inkscape:window-maximized="0"
inkscape:current-layer="svg17" />
<defs
@@ -39,7 +39,7 @@
x="3.9266238"
y="5.0815129" />
<path
style="fill:#2b3436;fill-opacity:1;stroke:#b6b6b6;stroke-opacity:1"
style="fill:none;fill-opacity:1;stroke:#b6b6b6;stroke-opacity:1"
d="m 26.446966,11.89536 v 17.136303 l 8.376353,4.936095 0.05434,-17.914796 z"
id="path20"
sodipodi:nodetypes="ccccc" />

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -3347,6 +3347,10 @@ void ModelVolume::assign_new_unique_ids_recursive()
seam_facets.set_new_unique_id();
mmu_segmentation_facets.set_new_unique_id();
texture_mapping_color_facets.set_new_unique_id();
imported_vertex_colors_rgba.set_new_unique_id();
imported_texture_uvs_per_face.set_new_unique_id();
imported_texture_uv_valid.set_new_unique_id();
imported_texture_rgba.set_new_unique_id();
fuzzy_skin_facets.set_new_unique_id();
}

View File

@@ -37,6 +37,7 @@
#include <array>
#include <algorithm>
#include <functional>
#include <initializer_list>
#include <optional>
namespace cereal {
@@ -867,6 +868,10 @@ public:
void set_metadata_json(std::string metadata_json);
bool empty() const { return m_data.triangles_to_split.empty(); }
void reset();
static std::unique_ptr<ColorFacetsAnnotation> make_temporary()
{
return std::unique_ptr<ColorFacetsAnnotation>(new ColorFacetsAnnotation());
}
void reserve(int n_triangles) { m_data.triangles_to_split.reserve(n_triangles); }
void shrink_to_fit()
{
@@ -905,6 +910,55 @@ private:
TriangleColorSplittingData m_data;
};
template<class T>
class ModelVolumeImportedVector final : public ObjectBase, public std::vector<T>
{
public:
using std::vector<T>::vector;
ModelVolumeImportedVector() = default;
ModelVolumeImportedVector(const ModelVolumeImportedVector &rhs) = default;
ModelVolumeImportedVector(ModelVolumeImportedVector &&rhs) = default;
ModelVolumeImportedVector& operator=(const ModelVolumeImportedVector &rhs) = default;
ModelVolumeImportedVector& operator=(ModelVolumeImportedVector &&rhs) = default;
ModelVolumeImportedVector& operator=(const std::vector<T> &rhs)
{
this->as_vector() = rhs;
return *this;
}
ModelVolumeImportedVector& operator=(std::vector<T> &&rhs)
{
this->as_vector() = std::move(rhs);
return *this;
}
ModelVolumeImportedVector& operator=(std::initializer_list<T> rhs)
{
this->as_vector() = rhs;
return *this;
}
private:
explicit ModelVolumeImportedVector(int) : ObjectBase(-1) {}
std::vector<T>& as_vector() { return static_cast<std::vector<T>&>(*this); }
const std::vector<T>& as_vector() const { return static_cast<const std::vector<T>&>(*this); }
friend class cereal::access;
friend class UndoRedo::StackImpl;
friend class ModelVolume;
template<class Archive> void save(Archive &ar) const { ar(as_vector()); }
template<class Archive> void load(Archive &ar)
{
std::vector<T> loaded;
ar(loaded);
this->as_vector().swap(loaded);
}
};
// An object STL, or a modifier volume, over which a different set of parameters shall be applied.
// ModelVolume instances are owned by a ModelObject.
class ModelVolume final : public ObjectBase
@@ -993,11 +1047,11 @@ public:
ColorFacetsAnnotation texture_mapping_color_facets;
std::vector<uint32_t> imported_vertex_colors_rgba;
ModelVolumeImportedVector<uint32_t> imported_vertex_colors_rgba;
std::vector<float> imported_texture_uvs_per_face;
std::vector<uint8_t> imported_texture_uv_valid;
std::vector<uint8_t> imported_texture_rgba;
ModelVolumeImportedVector<float> imported_texture_uvs_per_face;
ModelVolumeImportedVector<uint8_t> imported_texture_uv_valid;
ModelVolumeImportedVector<uint8_t> imported_texture_rgba;
uint32_t imported_texture_width{0};
uint32_t imported_texture_height{0};
@@ -1128,6 +1182,10 @@ public:
this->seam_facets.set_new_unique_id();
this->mmu_segmentation_facets.set_new_unique_id();
this->texture_mapping_color_facets.set_new_unique_id();
this->imported_vertex_colors_rgba.set_new_unique_id();
this->imported_texture_uvs_per_face.set_new_unique_id();
this->imported_texture_uv_valid.set_new_unique_id();
this->imported_texture_rgba.set_new_unique_id();
this->fuzzy_skin_facets.set_new_unique_id();
}
@@ -1311,7 +1369,7 @@ private:
friend class cereal::access;
friend class UndoRedo::StackImpl;
// Used for deserialization, therefore no IDs are allocated.
ModelVolume() : ObjectBase(-1), config(-1), supported_facets(-1), seam_facets(-1), mmu_segmentation_facets(-1), texture_mapping_color_facets(-1), fuzzy_skin_facets(-1), object(nullptr) {
ModelVolume() : ObjectBase(-1), config(-1), supported_facets(-1), seam_facets(-1), mmu_segmentation_facets(-1), texture_mapping_color_facets(-1), imported_vertex_colors_rgba(-1), imported_texture_uvs_per_face(-1), imported_texture_uv_valid(-1), imported_texture_rgba(-1), fuzzy_skin_facets(-1), object(nullptr) {
assert(this->id().invalid());
assert(this->config.id().invalid());
assert(this->supported_facets.id().invalid());
@@ -1339,8 +1397,11 @@ private:
t = texture_mapping_color_facets.timestamp();
cereal::load_by_value(ar, texture_mapping_color_facets);
mesh_changed |= t != texture_mapping_color_facets.timestamp();
ar(imported_vertex_colors_rgba);
ar(imported_texture_uvs_per_face, imported_texture_uv_valid, imported_texture_rgba, imported_texture_width, imported_texture_height);
cereal::load_by_value(ar, imported_vertex_colors_rgba);
cereal::load_by_value(ar, imported_texture_uvs_per_face);
cereal::load_by_value(ar, imported_texture_uv_valid);
cereal::load_by_value(ar, imported_texture_rgba);
ar(imported_texture_width, imported_texture_height);
cereal::load_by_value(ar, fuzzy_skin_facets);
mesh_changed |= t != fuzzy_skin_facets.timestamp();
cereal::load_by_value(ar, config);
@@ -1374,8 +1435,11 @@ private:
cereal::save_by_value(ar, seam_facets);
cereal::save_by_value(ar, mmu_segmentation_facets);
cereal::save_by_value(ar, texture_mapping_color_facets);
ar(imported_vertex_colors_rgba);
ar(imported_texture_uvs_per_face, imported_texture_uv_valid, imported_texture_rgba, imported_texture_width, imported_texture_height);
cereal::save_by_value(ar, imported_vertex_colors_rgba);
cereal::save_by_value(ar, imported_texture_uvs_per_face);
cereal::save_by_value(ar, imported_texture_uv_valid);
cereal::save_by_value(ar, imported_texture_rgba);
ar(imported_texture_width, imported_texture_height);
cereal::save_by_value(ar, fuzzy_skin_facets);
cereal::save_by_value(ar, config);
cereal::save(ar, text_configuration);
@@ -1952,6 +2016,8 @@ static const double SINKING_MIN_Z_THRESHOLD = 0.05;
namespace cereal
{
template <class Archive, class T>
struct specialize<Archive, Slic3r::ModelVolumeImportedVector<T>, cereal::specialization::member_load_save> {};
template <class Archive> struct specialize<Archive, Slic3r::ModelVolume, cereal::specialization::member_load_save> {};
// BBS: backup
template <class Archive> struct specialize<Archive, Slic3r::Model, cereal::specialization::member_load_save> {};

View File

@@ -881,7 +881,7 @@ void TextureMappingManager::load_entries(const std::string &serialized,
zone.reduce_outer_surface_texture = texture.value("reduce_outer_surface_texture", false);
zone.seam_hiding = texture.value("hide_seams", false);
zone.nonlinear_offset_adjustment = texture.value("nonlinear_offset_adjustment", false);
zone.compact_offset_mode = texture.value("compact_offset_mode", false);
zone.compact_offset_mode = texture.value("compact_offset_mode", TextureMappingZone::DefaultCompactOffsetMode);
zone.contrast_pct = std::clamp(texture.value("contrast_pct", 100.f), 25.f, 300.f);
zone.high_resolution_sampling = texture.value("high_resolution_sampling", true);
zone.tone_gamma = normalize_tone_gamma(texture.value("tone_gamma", 1.f));

View File

@@ -70,7 +70,7 @@ struct TextureMappingZone
static constexpr bool DefaultReduceOuterSurfaceTexture = false;
static constexpr bool DefaultSeamHiding = false;
static constexpr bool DefaultNonlinearOffsetAdjustment = false;
static constexpr bool DefaultCompactOffsetMode = false;
static constexpr bool DefaultCompactOffsetMode = true;
static constexpr float DefaultContrastPct = 100.f;
static constexpr bool DefaultHighResolutionSampling = true;
static constexpr float DefaultToneGamma = 1.f;

File diff suppressed because it is too large Load Diff

View File

@@ -5,6 +5,7 @@
#include <array>
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include <vector>
@@ -63,7 +64,6 @@ public:
// IDs of the Vertex Array Objects, into which the geometry has been loaded.
// Zero if the VBOs are not sent to GPU yet.
unsigned int vertices_VAO_id{ 0 };
unsigned int vertices_VBO_id{0};
std::vector<unsigned int> triangle_indices_VBO_ids;
};
@@ -78,11 +78,8 @@ public:
void data_changed(bool is_serializing) override;
// TriangleSelector::serialization/deserialization has a limit to store 19 different states.
// EXTRUDER_LIMIT + 1 states are used to storing the painting because also uncolored triangles are stored.
// When increasing EXTRUDER_LIMIT, it needs to ensure that TriangleSelector::serialization/deserialization
// will be also extended to support additional states, requiring at least one state to remain free out of 19 states.
static const constexpr size_t EXTRUDERS_LIMIT = 16;
// Keep this in sync with the shared triangle-selector state range.
static const constexpr size_t EXTRUDERS_LIMIT = static_cast<size_t>(EnforcerBlockerType::ExtruderMax);
const float get_cursor_radius_min() const override { return CursorRadiusMin; }
@@ -95,12 +92,17 @@ protected:
ColorRGBA get_cursor_hover_color() const override;
void on_set_state() override;
EnforcerBlockerType get_left_button_state_type() const override { return EnforcerBlockerType(m_selected_extruder_idx + 1); }
EnforcerBlockerType get_left_button_state_type() const override
{
if (m_selected_extruder_idx < m_display_filament_ids.size())
return EnforcerBlockerType(m_display_filament_ids[m_selected_extruder_idx]);
return EnforcerBlockerType::Extruder1;
}
EnforcerBlockerType get_right_button_state_type() const override { return EnforcerBlockerType(-1); }
void on_render_input_window(float x, float y, float bottom_limit) override;
std::string on_get_name() const override;
void render_tooltip_button(float x, float y);
void show_tooltip_information(float caption_max, float x, float y);
bool on_is_selectable() const override;
bool on_is_activable() const override;
@@ -165,13 +167,6 @@ private:
// This map holds all translated description texts, so they can be easily referenced during layout calculations
// etc. When language changes, GUI is recreated and this class constructed again, so the change takes effect.
std::map<std::string, wxString> m_desc;
// Contains all shortcuts in the format of {shortcut, description}, e.g. {alt + _L("Left mouse button"), _L("Part_selection")}
std::vector<std::pair<wxString, wxString>> m_shortcuts_brush;
// Contains all shortcuts in the format of {shortcut, description}, e.g. {alt + _L("Left mouse button"), _L("Part_selection")}
std::vector<std::pair<wxString, wxString>> m_shortcuts_bucket_fill;
// Contains all shortcuts in the format of {shortcut, description}, e.g. {alt + _L("Left mouse button"), _L("Part_selection")}
std::vector<std::pair<wxString, wxString>> m_shortcuts_gap_fill;
};
class GLGizmoTrueColorPainting : public GLGizmoPainterBase
@@ -225,6 +220,7 @@ private:
void init_model_triangle_selectors();
ModelObject *selected_model_object() const;
void update_selected_object_color_state();
void open_color_data_management_dialog();
bool selected_object_has_rgb_data() const;
bool selected_object_has_imported_color_data() const;
void initialize_selected_object_rgb_data();
@@ -232,6 +228,8 @@ private:
void convert_selected_object_image_texture_to_rgb_data();
void refresh_selected_object_after_rgb_change(ModelObject *object);
void update_triangle_selectors_color();
bool record_brush_stroke_point(const Vec2d &mouse_position);
void clear_brush_stroke_points();
bool pick_color_from_model(const Vec2d &mouse_position);
bool sample_color_from_model(const Vec2d &mouse_position, ColorRGBA &color) const;
void set_active_color_from_sample(const ColorRGBA &color);
@@ -277,7 +275,10 @@ private:
bool m_selected_can_convert_vertex = false;
bool m_selected_can_convert_image = false;
bool m_color_picker_active = false;
bool m_brush_stroke_active = false;
ObjectID m_selected_color_state_object_id;
std::vector<std::vector<Vec3f>> m_brush_stroke_points_by_volume;
std::vector<std::unique_ptr<ColorFacetsAnnotation>> m_preview_rgb_data_by_volume;
struct ColorPickerVolumeSourceCache
{
ObjectID volume_id;
@@ -328,6 +329,7 @@ private:
bool ensure_overlay_texture();
OverlayRect overlay_rect() const;
ModelObject *selected_model_object() const;
void open_color_data_management_dialog();
void update_default_projection_mode();
ProjectionMode default_projection_mode() const;
bool projection_mode_allowed(ProjectionMode mode) const;

View File

@@ -1685,7 +1685,18 @@ void TriangleSelectorPatch::update_render_data()
const TextureMappingManager *texture_mgr = wxGetApp().preset_bundle != nullptr ?
&wxGetApp().preset_bundle->texture_mapping_zones : nullptr;
m_texture_preview_visual_signature = texture_preview_settings_signature(num_physical, texture_mgr);
if (model_volume_has_texture_preview_data_for_painting(*m_model_volume)) {
if (m_texture_mapping_color_preview != nullptr && !m_texture_mapping_color_preview->empty()) {
build_mmu_vertex_color_preview_models(*m_model_volume,
triangles_per_type,
m_ebt_colors,
m_model_volume->extruder_id() > 0 ? unsigned(m_model_volume->extruder_id()) : 0u,
num_physical,
texture_mgr,
m_vertex_color_preview_models,
m_vertex_color_preview_colors,
m_vertex_color_preview_filament_ids,
m_texture_mapping_color_preview);
} else if (model_volume_has_texture_preview_data_for_painting(*m_model_volume)) {
build_mmu_texture_preview_models(*m_model_volume,
triangles_per_type,
m_ebt_colors,

View File

@@ -132,6 +132,7 @@ public:
void set_none_state_rendered(bool rendered) { m_render_none_state = rendered; }
void set_texture_preview_needed(bool needed) { m_texture_preview_needed = needed; }
void set_texture_preview_opaque(bool opaque) { m_texture_preview_opaque = opaque; }
void set_texture_mapping_color_preview(const ColorFacetsAnnotation *preview) { m_texture_mapping_color_preview = preview; }
constexpr static float GapAreaMin = 0.f;
constexpr static float GapAreaMax = 5.f;
@@ -196,6 +197,7 @@ protected:
mutable std::vector<GLModel> m_vertex_color_preview_models;
std::vector<ColorRGBA> m_vertex_color_preview_colors;
std::vector<unsigned int> m_vertex_color_preview_filament_ids;
const ColorFacetsAnnotation *m_texture_mapping_color_preview { nullptr };
bool m_filter_state = false;
bool m_render_none_state = true;

View File

@@ -1387,13 +1387,17 @@ bool build_texture_mapping_color_preview_model_for_state(
const ModelVolume &model_volume,
const std::vector<TriangleSelector::FacetStateTriangle> &state_triangles,
const TexturePreviewSimulationSettings *simulation_settings,
GUI::GLModel &out_model)
GUI::GLModel &out_model,
const ColorFacetsAnnotation *texture_mapping_color_facets_override = nullptr)
{
if (!model_volume_has_texture_mapping_color_preview_data(model_volume) || state_triangles.empty())
const ColorFacetsAnnotation *color_source = texture_mapping_color_facets_override;
if (color_source == nullptr || color_source->empty())
color_source = &model_volume.texture_mapping_color_facets;
if (color_source == nullptr || color_source->empty() || state_triangles.empty())
return false;
std::vector<ColorFacetTriangle> color_facets;
model_volume.texture_mapping_color_facets.get_facet_triangles(model_volume, color_facets);
color_source->get_facet_triangles(model_volume, color_facets);
if (color_facets.empty())
return false;
@@ -2053,18 +2057,44 @@ bool build_mmu_vertex_color_preview_models(
const Transform3d &world_matrix,
std::vector<GUI::GLModel> &out_models,
std::vector<ColorRGBA> &out_colors,
std::vector<unsigned int> &out_filament_ids)
std::vector<unsigned int> &out_filament_ids,
const ColorFacetsAnnotation *texture_mapping_color_facets_override)
{
out_models.clear();
out_colors.clear();
out_filament_ids.clear();
const bool has_texture_mapping_color_override =
texture_mapping_color_facets_override != nullptr && !texture_mapping_color_facets_override->empty();
if (triangles_per_type.empty() || (texture_mgr == nullptr && !has_texture_mapping_color_override))
return false;
const std::vector<std::string> physical_colors = physical_filament_colors_for_texture_preview(num_physical);
bool built_any = false;
for (size_t state_id = 0; state_id < triangles_per_type.size(); ++state_id) {
const unsigned int filament_id = filament_id_for_state(state_id, base_filament_id);
const TextureMappingZone *zone = zone_for_filament(filament_id, num_physical, texture_mgr);
if (zone == nullptr || (!is_image_zone(*zone) && !is_gradient_zone(*zone)))
if (zone == nullptr) {
if (!has_texture_mapping_color_override || state_id != 0)
continue;
GUI::GLModel model;
if (!build_texture_mapping_color_preview_model_for_state(model_volume,
triangles_per_type[state_id],
nullptr,
model,
texture_mapping_color_facets_override) ||
!model.is_initialized())
continue;
out_models.emplace_back(std::move(model));
out_colors.emplace_back(state_id < state_colors.size() ? state_colors[state_id] :
(state_colors.empty() ? ColorRGBA(0.15f, 0.65f, 0.6f, 1.f) : state_colors.back()));
out_filament_ids.emplace_back(0u);
built_any = true;
continue;
}
if (!is_image_zone(*zone) && !is_gradient_zone(*zone))
continue;
GUI::GLModel model;
@@ -2079,11 +2109,16 @@ bool build_mmu_vertex_color_preview_models(
texture_preview_simulation_settings_for_filament(filament_id, num_physical, texture_mgr, physical_colors);
if (simulation_settings)
prepare_texture_preview_simulation_settings(*simulation_settings);
if (model_volume_has_texture_mapping_color_preview_data(model_volume)) {
const bool has_texture_mapping_color_preview =
has_texture_mapping_color_override || model_volume_has_texture_mapping_color_preview_data(model_volume);
if (has_texture_mapping_color_preview) {
const ColorFacetsAnnotation *preview_override =
has_texture_mapping_color_override ? texture_mapping_color_facets_override : nullptr;
if (!build_texture_mapping_color_preview_model_for_state(model_volume,
triangles_per_type[state_id],
simulation_settings ? &*simulation_settings : nullptr,
model))
model,
preview_override))
continue;
} else {
if (!build_vertex_color_preview_model_for_state(model_volume,
@@ -2112,7 +2147,8 @@ bool build_mmu_vertex_color_preview_models(
const TextureMappingManager *texture_mgr,
std::vector<GUI::GLModel> &out_models,
std::vector<ColorRGBA> &out_colors,
std::vector<unsigned int> &out_filament_ids)
std::vector<unsigned int> &out_filament_ids,
const ColorFacetsAnnotation *texture_mapping_color_facets_override)
{
return build_mmu_vertex_color_preview_models(model_volume,
triangles_per_type,
@@ -2123,7 +2159,8 @@ bool build_mmu_vertex_color_preview_models(
Transform3d::Identity(),
out_models,
out_colors,
out_filament_ids);
out_filament_ids,
texture_mapping_color_facets_override);
}
size_t model_volume_texture_preview_signature(const ModelVolume &model_volume)
@@ -2285,8 +2322,13 @@ void render_model_texture_preview_models(
const size_t texture_signature = model_volume_texture_preview_signature(model_volume);
GLuint bound_texture_id = 0;
for (size_t idx = 0; idx < models.size(); ++idx) {
const float mix = texture_preview_mix_for_filament(filament_ids[idx], num_physical, texture_mgr);
const bool invalid = texture_preview_settings_invalid_for_filament(filament_ids[idx], num_physical, texture_mgr);
const bool raw_vertex_color_preview = filament_ids[idx] == 0;
const float mix = raw_vertex_color_preview ?
1.f :
texture_preview_mix_for_filament(filament_ids[idx], num_physical, texture_mgr);
const bool invalid = raw_vertex_color_preview ?
false :
texture_preview_settings_invalid_for_filament(filament_ids[idx], num_physical, texture_mgr);
if (mix <= 0.f && !invalid)
continue;

View File

@@ -36,7 +36,8 @@ bool build_mmu_vertex_color_preview_models(
const Transform3d &world_matrix,
std::vector<GUI::GLModel> &out_models,
std::vector<ColorRGBA> &out_colors,
std::vector<unsigned int> &out_filament_ids);
std::vector<unsigned int> &out_filament_ids,
const ColorFacetsAnnotation *texture_mapping_color_facets_override = nullptr);
bool build_mmu_vertex_color_preview_models(
const ModelVolume &model_volume,
@@ -47,7 +48,8 @@ bool build_mmu_vertex_color_preview_models(
const TextureMappingManager *texture_mgr,
std::vector<GUI::GLModel> &out_models,
std::vector<ColorRGBA> &out_colors,
std::vector<unsigned int> &out_filament_ids);
std::vector<unsigned int> &out_filament_ids,
const ColorFacetsAnnotation *texture_mapping_color_facets_override = nullptr);
size_t model_volume_texture_preview_signature(const ModelVolume &model_volume);
size_t model_volume_texture_mapping_color_preview_signature(const ModelVolume &model_volume);