Add simulated color preview support for top-surface coloring. Make preview settings apply to every texture mapping zone

This commit is contained in:
sentientstardust
2026-05-27 20:07:01 +01:00
parent 7b209131d4
commit 1f3fe6d6a1
8 changed files with 786 additions and 113 deletions

View File

@@ -3,6 +3,7 @@
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
const float UV_EDGE_EPSILON = 0.000001;
const float INVALID_TEXTURE_CHECKER_SCALE = 0.2;
const float CONTONING_FLAT_SURFACE_NORMAL_Z = 0.999;
struct PrintVolumeDetection
{
@@ -13,8 +14,11 @@ struct PrintVolumeDetection
uniform vec4 uniform_color;
uniform sampler2D uniform_texture;
uniform sampler2D contoning_flat_surface_texture;
uniform float texture_preview_mix;
uniform bool invalid_texture_mapping;
uniform bool contoning_flat_surface_texture_enabled;
uniform bool contoning_flat_surface_include_bottom;
uniform bool color_match_preview_active;
uniform vec3 color_match_target_oklab;
uniform float color_match_tolerance_sq;
@@ -78,6 +82,12 @@ void main()
vec4 color = uniform_color;
vec4 texture_color = texture2D(uniform_texture, texture_preview_coord(tex_coord));
if (contoning_flat_surface_texture_enabled) {
float normal_z = normalize(world_normal).z;
if (normal_z >= CONTONING_FLAT_SURFACE_NORMAL_Z ||
(contoning_flat_surface_include_bottom && normal_z <= -CONTONING_FLAT_SURFACE_NORMAL_Z))
texture_color = texture2D(contoning_flat_surface_texture, texture_preview_coord(tex_coord));
}
float mix_factor = clamp(texture_preview_mix, 0.0, 1.0);
if (color_match_preview_active) {
float source_alpha = clamp(texture_color.a, 0.0, 1.0);

View File

@@ -3,6 +3,7 @@
const vec3 ZERO = vec3(0.0, 0.0, 0.0);
const float UV_EDGE_EPSILON = 0.000001;
const float INVALID_TEXTURE_CHECKER_SCALE = 0.2;
const float CONTONING_FLAT_SURFACE_NORMAL_Z = 0.999;
struct PrintVolumeDetection
{
@@ -13,8 +14,11 @@ struct PrintVolumeDetection
uniform vec4 uniform_color;
uniform sampler2D uniform_texture;
uniform sampler2D contoning_flat_surface_texture;
uniform float texture_preview_mix;
uniform bool invalid_texture_mapping;
uniform bool contoning_flat_surface_texture_enabled;
uniform bool contoning_flat_surface_include_bottom;
uniform bool color_match_preview_active;
uniform vec3 color_match_target_oklab;
uniform float color_match_tolerance_sq;
@@ -80,6 +84,12 @@ void main()
vec4 color = uniform_color;
vec4 texture_color = texture(uniform_texture, texture_preview_coord(tex_coord));
if (contoning_flat_surface_texture_enabled) {
float normal_z = normalize(world_normal).z;
if (normal_z >= CONTONING_FLAT_SURFACE_NORMAL_Z ||
(contoning_flat_surface_include_bottom && normal_z <= -CONTONING_FLAT_SURFACE_NORMAL_Z))
texture_color = texture(contoning_flat_surface_texture, texture_preview_coord(tex_coord));
}
float mix_factor = clamp(texture_preview_mix, 0.0, 1.0);
if (color_match_preview_active) {
float source_alpha = clamp(texture_color.a, 0.0, 1.0);

View File

@@ -3760,6 +3760,7 @@ void PresetBundle::load_config_file_config(const std::string &name_or_path, bool
this->project_config.apply_only(config, s_project_options);
if (const auto *color_opt = this->project_config.option<ConfigOptionStrings>("filament_colour", false); color_opt != nullptr)
this->texture_mapping_zones.load_entries(this->project_config.opt_string("texture_mapping_definitions"), color_opt->values);
this->texture_mapping_global_settings.load(this->project_config.opt_string("texture_mapping_global_settings"));
break;
}

View File

@@ -262,6 +262,7 @@ static void remove_texture_mapping_preview_options(nlohmann::json &root)
texture_it->erase("preview_opacity_pct");
texture_it->erase("simulate_preview_colors");
texture_it->erase("limit_preview_resolution");
texture_it->erase("simulate_top_surface_lod");
}
}
@@ -286,16 +287,67 @@ static bool texture_mapping_definitions_equal_for_gcode(const ConfigOption *lhs,
return lhs_json == rhs_json;
}
static bool texture_mapping_global_settings_json_for_gcode(const ConfigOption *opt, nlohmann::json &out)
{
out = nlohmann::json::object();
if (opt == nullptr)
return true;
if (opt->type() != coString)
return false;
const std::string &value = static_cast<const ConfigOptionString *>(opt)->value;
if (value.empty())
return true;
try {
out = nlohmann::json::parse(value);
} catch (const nlohmann::json::exception &) {
return false;
}
if (!out.is_object())
return false;
out.erase("schema");
auto preview_it = out.find("preview_options");
if (preview_it != out.end()) {
if (preview_it->is_object()) {
preview_it->erase("preview_opacity_pct");
preview_it->erase("simulate_preview_colors");
preview_it->erase("limit_preview_resolution");
preview_it->erase("simulate_top_surface_lod");
if (preview_it->empty())
out.erase(preview_it);
}
}
out.erase("preview_opacity_pct");
out.erase("simulate_preview_colors");
out.erase("limit_preview_resolution");
out.erase("simulate_top_surface_lod");
return true;
}
static bool texture_mapping_global_settings_equal_for_gcode(const ConfigOption *lhs, const ConfigOption *rhs)
{
if (config_options_equal(lhs, rhs))
return true;
nlohmann::json lhs_json;
nlohmann::json rhs_json;
return texture_mapping_global_settings_json_for_gcode(lhs, lhs_json) &&
texture_mapping_global_settings_json_for_gcode(rhs, rhs_json) &&
lhs_json == rhs_json;
}
static void remove_texture_mapping_preview_only_diff(t_config_option_keys &diff,
const ConfigBase &current_config,
const DynamicPrintConfig &new_full_config)
{
auto it = std::find(diff.begin(), diff.end(), "texture_mapping_definitions");
if (it == diff.end())
return;
if (texture_mapping_definitions_equal_for_gcode(current_config.option("texture_mapping_definitions"),
if (it != diff.end() &&
texture_mapping_definitions_equal_for_gcode(current_config.option("texture_mapping_definitions"),
new_full_config.option("texture_mapping_definitions")))
diff.erase(it);
it = std::find(diff.begin(), diff.end(), "texture_mapping_global_settings");
if (it != diff.end() &&
texture_mapping_global_settings_equal_for_gcode(current_config.option("texture_mapping_global_settings"),
new_full_config.option("texture_mapping_global_settings")))
diff.erase(it);
}
// Collect changes to print config, account for overrides of extruder retract values by filament presets.
@@ -325,6 +377,8 @@ static t_config_option_keys print_config_diffs(
} else if (*opt_new != *opt_old) {
if (opt_key == "texture_mapping_definitions" && texture_mapping_definitions_equal_for_gcode(opt_old, opt_new))
continue;
if (opt_key == "texture_mapping_global_settings" && texture_mapping_global_settings_equal_for_gcode(opt_old, opt_new))
continue;
//BBS: add plate_index logic for wipe_tower_x/wipe_tower_y
if (!opt_key.compare("wipe_tower_x") || !opt_key.compare("wipe_tower_y")) {
const ConfigOptionFloats* option_new = dynamic_cast<const ConfigOptionFloats*>(opt_new);
@@ -358,6 +412,8 @@ static t_config_option_keys full_print_config_diffs(const DynamicPrintConfig &cu
if (opt_old == nullptr || *opt_new != *opt_old) {
if (opt_key == "texture_mapping_definitions" && texture_mapping_definitions_equal_for_gcode(opt_old, opt_new))
continue;
if (opt_key == "texture_mapping_global_settings" && texture_mapping_global_settings_equal_for_gcode(opt_old, opt_new))
continue;
//BBS: add plate_index logic for wipe_tower_x/wipe_tower_y
if (opt_old && (!opt_key.compare("wipe_tower_x") || !opt_key.compare("wipe_tower_y"))) {
const ConfigOptionFloats* option_new = dynamic_cast<const ConfigOptionFloats*>(opt_new);

View File

@@ -916,7 +916,8 @@ static std::string normalized_prime_tower_color_mode_name(std::string name)
std::string TextureMappingGlobalSettings::serialize() const
{
const std::string normalized_mode = normalize_color_mode_name(prime_tower_color_mode);
if (!enabled &&
const bool prime_tower_defaults =
!enabled &&
std::abs((std::isfinite(angle_offset_deg) ? angle_offset_deg : 0.f)) <= 1e-6f &&
!preserve_aspect_ratio &&
normalized_mode == "auto" &&
@@ -928,26 +929,44 @@ std::string TextureMappingGlobalSettings::serialize() const
image_file_back.empty() &&
image_name_back.empty() &&
image_width_back == 0 &&
image_height_back == 0)
image_height_back == 0;
const bool preview_defaults =
std::abs((std::isfinite(preview_opacity_pct) ? preview_opacity_pct : TextureMappingZone::DefaultPreviewOpacityPct) -
TextureMappingZone::DefaultPreviewOpacityPct) <= 1e-6f &&
preview_simulate_colors == TextureMappingZone::DefaultPreviewSimulateColors &&
preview_limit_resolution == TextureMappingZone::DefaultPreviewLimitResolution &&
preview_simulate_top_surface_lod == TextureMappingZone::DefaultPreviewSimulateTopSurfaceLod;
if (prime_tower_defaults && preview_defaults)
return {};
nlohmann::json root;
root["schema"] = 1;
nlohmann::json prime_tower_texture_mapping;
prime_tower_texture_mapping["enabled"] = enabled;
prime_tower_texture_mapping["angle_offset_deg"] = std::clamp(std::isfinite(angle_offset_deg) ? angle_offset_deg : 0.f, 0.f, 360.f);
prime_tower_texture_mapping["preserve_aspect_ratio"] = preserve_aspect_ratio;
prime_tower_texture_mapping["prime_tower_color_mode"] = normalized_mode;
prime_tower_texture_mapping["settings_zone_uid"] = prime_tower_settings_zone_uid;
prime_tower_texture_mapping["image_file"] = image_file;
prime_tower_texture_mapping["image_name"] = image_name;
prime_tower_texture_mapping["image_width"] = image_width;
prime_tower_texture_mapping["image_height"] = image_height;
prime_tower_texture_mapping["image_file_back"] = image_file_back;
prime_tower_texture_mapping["image_name_back"] = image_name_back;
prime_tower_texture_mapping["image_width_back"] = image_width_back;
prime_tower_texture_mapping["image_height_back"] = image_height_back;
root["prime_tower_texture_mapping"] = std::move(prime_tower_texture_mapping);
if (!preview_defaults || !prime_tower_defaults) {
nlohmann::json preview_options;
preview_options["preview_opacity_pct"] =
std::clamp(std::isfinite(preview_opacity_pct) ? preview_opacity_pct : TextureMappingZone::DefaultPreviewOpacityPct, 0.f, 100.f);
preview_options["simulate_preview_colors"] = preview_simulate_colors;
preview_options["limit_preview_resolution"] = preview_limit_resolution;
preview_options["simulate_top_surface_lod"] = preview_simulate_top_surface_lod;
root["preview_options"] = std::move(preview_options);
}
if (!prime_tower_defaults) {
nlohmann::json prime_tower_texture_mapping;
prime_tower_texture_mapping["enabled"] = enabled;
prime_tower_texture_mapping["angle_offset_deg"] = std::clamp(std::isfinite(angle_offset_deg) ? angle_offset_deg : 0.f, 0.f, 360.f);
prime_tower_texture_mapping["preserve_aspect_ratio"] = preserve_aspect_ratio;
prime_tower_texture_mapping["prime_tower_color_mode"] = normalized_mode;
prime_tower_texture_mapping["settings_zone_uid"] = prime_tower_settings_zone_uid;
prime_tower_texture_mapping["image_file"] = image_file;
prime_tower_texture_mapping["image_name"] = image_name;
prime_tower_texture_mapping["image_width"] = image_width;
prime_tower_texture_mapping["image_height"] = image_height;
prime_tower_texture_mapping["image_file_back"] = image_file_back;
prime_tower_texture_mapping["image_name_back"] = image_name_back;
prime_tower_texture_mapping["image_width_back"] = image_width_back;
prime_tower_texture_mapping["image_height_back"] = image_height_back;
root["prime_tower_texture_mapping"] = std::move(prime_tower_texture_mapping);
}
return root.dump();
}
@@ -967,6 +986,25 @@ void TextureMappingGlobalSettings::load(const std::string &serialized)
if (!root.is_object())
return;
const auto preview_options_it = root.find("preview_options");
if (preview_options_it != root.end() && preview_options_it->is_object()) {
const auto opacity_it = preview_options_it->find("preview_opacity_pct");
if (opacity_it != preview_options_it->end() && opacity_it->is_number())
preview_opacity_pct = std::clamp(opacity_it->get<float>(), 0.f, 100.f);
const auto simulate_it = preview_options_it->find("simulate_preview_colors");
if (simulate_it != preview_options_it->end() && simulate_it->is_boolean())
preview_simulate_colors = simulate_it->get<bool>();
const auto limit_it = preview_options_it->find("limit_preview_resolution");
if (limit_it != preview_options_it->end() && limit_it->is_boolean())
preview_limit_resolution = limit_it->get<bool>();
const auto top_surface_lod_it = preview_options_it->find("simulate_top_surface_lod");
if (top_surface_lod_it != preview_options_it->end() && top_surface_lod_it->is_boolean())
preview_simulate_top_surface_lod = top_surface_lod_it->get<bool>();
}
const auto root_simulate_it = root.find("simulate_preview_colors");
if (root_simulate_it != root.end() && root_simulate_it->is_boolean())
preview_simulate_colors = root_simulate_it->get<bool>();
const auto prime_tower_texture_mapping_it = root.find("prime_tower_texture_mapping");
if (prime_tower_texture_mapping_it == root.end() || !prime_tower_texture_mapping_it->is_object())
return;
@@ -1122,9 +1160,6 @@ bool TextureMappingZone::operator==(const TextureMappingZone &rhs) const
high_resolution_sampling == rhs.high_resolution_sampling &&
std::abs(tone_gamma - rhs.tone_gamma) <= eps &&
transmission_distance_calibration_mode == rhs.transmission_distance_calibration_mode &&
std::abs(preview_opacity_pct - rhs.preview_opacity_pct) <= eps &&
preview_simulate_colors == rhs.preview_simulate_colors &&
preview_limit_resolution == rhs.preview_limit_resolution &&
auto_adjust_filament_selection == rhs.auto_adjust_filament_selection &&
floats_equal(filament_strengths_pct, rhs.filament_strengths_pct) &&
floats_equal(filament_minimum_offsets_pct, rhs.filament_minimum_offsets_pct) &&
@@ -1527,10 +1562,6 @@ std::string TextureMappingManager::serialize_entries()
texture["tone_gamma"] = normalize_tone_gamma(zone.tone_gamma);
texture["transmission_distance_calibration"] =
transmission_distance_calibration_mode_name(zone.transmission_distance_calibration_mode);
texture["preview_opacity_pct"] =
std::clamp(finite_or(zone.preview_opacity_pct, TextureMappingZone::DefaultPreviewOpacityPct), 0.f, 100.f);
texture["simulate_preview_colors"] = zone.preview_simulate_colors;
texture["limit_preview_resolution"] = zone.preview_limit_resolution;
texture["auto_adjust_filaments"] = zone.auto_adjust_filament_selection;
texture["strength_pct"] = floats_to_json(normalized_strengths);
texture["minimum_offset_pct"] = floats_to_json(normalized_min_offsets);
@@ -1832,10 +1863,6 @@ void TextureMappingManager::load_entries(const std::string &serialized,
zone.high_resolution_sampling = true;
zone.tone_gamma = normalize_tone_gamma(texture.value("tone_gamma", 1.f));
zone.transmission_distance_calibration_mode = transmission_distance_calibration_mode_from_json(texture);
zone.preview_opacity_pct =
std::clamp(texture.value("preview_opacity_pct", TextureMappingZone::DefaultPreviewOpacityPct), 0.f, 100.f);
zone.preview_simulate_colors = texture.value("simulate_preview_colors", TextureMappingZone::DefaultPreviewSimulateColors);
zone.preview_limit_resolution = texture.value("limit_preview_resolution", true);
zone.auto_adjust_filament_selection = texture.value("auto_adjust_filaments", true);
zone.filament_strengths_pct = normalize_strengths(floats_from_json(texture.value("strength_pct", nlohmann::json::array())));
zone.filament_minimum_offsets_pct =

View File

@@ -219,6 +219,7 @@ struct TextureMappingZone
static constexpr int DefaultTransmissionDistanceCalibrationMode = int(TDCalibrationAbsolute);
static constexpr bool DefaultPreviewSimulateColors = false;
static constexpr bool DefaultPreviewLimitResolution = true;
static constexpr bool DefaultPreviewSimulateTopSurfaceLod = true;
static constexpr bool DefaultAutoAdjustFilamentSelection = true;
static constexpr int default_modulation_mode_for_surface_pattern(int surface_pattern)
@@ -329,9 +330,6 @@ struct TextureMappingZone
bool high_resolution_sampling = DefaultHighResolutionSampling;
float tone_gamma = DefaultToneGamma;
int transmission_distance_calibration_mode = DefaultTransmissionDistanceCalibrationMode;
float preview_opacity_pct = DefaultPreviewOpacityPct;
bool preview_simulate_colors = DefaultPreviewSimulateColors;
bool preview_limit_resolution = DefaultPreviewLimitResolution;
bool auto_adjust_filament_selection = DefaultAutoAdjustFilamentSelection;
std::vector<float> filament_strengths_pct;
std::vector<float> filament_minimum_offsets_pct;
@@ -454,9 +452,6 @@ struct TextureMappingZone
high_resolution_sampling = DefaultHighResolutionSampling;
tone_gamma = DefaultToneGamma;
transmission_distance_calibration_mode = DefaultTransmissionDistanceCalibrationMode;
preview_opacity_pct = DefaultPreviewOpacityPct;
preview_simulate_colors = DefaultPreviewSimulateColors;
preview_limit_resolution = DefaultPreviewLimitResolution;
auto_adjust_filament_selection = DefaultAutoAdjustFilamentSelection;
linear_gradient_mode = DefaultLinearGradientMode;
linear_gradient_radius_mm = DefaultLinearGradientRadiusMm;
@@ -519,6 +514,10 @@ struct TextureMappingGlobalSettings
bool enabled = false;
float angle_offset_deg = 0.f;
bool preserve_aspect_ratio = false;
float preview_opacity_pct = TextureMappingZone::DefaultPreviewOpacityPct;
bool preview_simulate_colors = TextureMappingZone::DefaultPreviewSimulateColors;
bool preview_limit_resolution = TextureMappingZone::DefaultPreviewLimitResolution;
bool preview_simulate_top_surface_lod = TextureMappingZone::DefaultPreviewSimulateTopSurfaceLod;
std::string prime_tower_color_mode = "auto";
uint64_t prime_tower_settings_zone_uid = 0;
std::string image_file;

View File

@@ -44,6 +44,8 @@ constexpr size_t k_temporary_simulated_texture_preview_max_pixels = 1024ull * 10
constexpr size_t k_temporary_simulated_texture_preview_min_source_pixels = k_temporary_simulated_texture_preview_max_pixels * 3 / 2;
constexpr size_t k_surface_gradient_preview_max_components = 10;
constexpr size_t k_surface_gradient_preview_lut_size = 33;
constexpr size_t k_contoning_flat_surface_preview_max_candidates = 250000;
constexpr double k_contoning_top_surface_preview_lod_max_samples = 350000.0;
constexpr const char *TEXTURE_MAPPING_BACKGROUND_COLOR_CONFIG_KEY = "texture_mapping_background_color";
struct TexturePreviewMixCandidate
@@ -86,6 +88,10 @@ struct TexturePreviewSimulationSettings
int generic_solver_lookup_mode = int(TextureMappingZone::GenericSolverClosestMix);
int generic_solver_mode = int(TextureMappingZone::GenericSolverV2);
int generic_solver_mix_model = TextureMappingZone::DefaultGenericSolverMixModel;
bool contoning_flat_surface_quantization = false;
int contoning_flat_surface_pattern_filaments = TextureMappingZone::DefaultTopSurfaceContoningPatternFilaments;
bool simulate_top_surface_lod = false;
float top_surface_lod_pitch_mm = 0.f;
float minimum_visibility_offset_factor = 0.f;
std::vector<unsigned int> component_ids;
std::vector<std::array<float, 3>> component_colors;
@@ -257,7 +263,7 @@ std::array<Vec2f, 3> unwrap_triangle_uvs(const Vec2f &uv0, const Vec2f &uv1, con
return out;
}
float estimated_texture_preview_mm_per_pixel(const ModelVolume &model_volume)
float estimated_texture_preview_mm_per_pixel(const ModelVolume &model_volume, const Transform3d *world_matrix = nullptr)
{
const unsigned int width = model_volume.imported_texture_width;
const unsigned int height = model_volume.imported_texture_height;
@@ -286,9 +292,15 @@ float estimated_texture_preview_mm_per_pixel(const ModelVolume &model_volume)
size_t(indices[2]) >= its.vertices.size())
continue;
const Vec3d p0 = its.vertices[size_t(indices[0])].cast<double>();
const Vec3d p1 = its.vertices[size_t(indices[1])].cast<double>();
const Vec3d p2 = its.vertices[size_t(indices[2])].cast<double>();
const Vec3d p0 = world_matrix != nullptr ?
((*world_matrix) * its.vertices[size_t(indices[0])].cast<double>()) :
its.vertices[size_t(indices[0])].cast<double>();
const Vec3d p1 = world_matrix != nullptr ?
((*world_matrix) * its.vertices[size_t(indices[1])].cast<double>()) :
its.vertices[size_t(indices[1])].cast<double>();
const Vec3d p2 = world_matrix != nullptr ?
((*world_matrix) * its.vertices[size_t(indices[2])].cast<double>()) :
its.vertices[size_t(indices[2])].cast<double>();
const double tri_area_mm2 = 0.5 * (p1 - p0).cross(p2 - p0).norm();
if (!std::isfinite(tri_area_mm2) || tri_area_mm2 <= double(k_epsilon))
continue;
@@ -457,6 +469,57 @@ float texture_preview_config_float(const char *key, float fallback)
return fallback;
}
float texture_preview_config_nozzle_mm(const DynamicPrintConfig &config, const std::vector<unsigned int> &component_ids)
{
float nozzle = 0.4f;
if (const ConfigOptionFloats *opt = config.option<ConfigOptionFloats>("nozzle_diameter")) {
for (const unsigned int component_id : component_ids) {
const size_t idx = component_id > 0 ? size_t(component_id - 1) : size_t(0);
if (idx < opt->values.size() && std::isfinite(opt->values[idx]) && opt->values[idx] > 0.0)
nozzle = std::max(nozzle, float(opt->values[idx]));
}
if (!opt->values.empty() && std::isfinite(opt->values.front()) && opt->values.front() > 0.0)
nozzle = std::max(nozzle, float(opt->values.front()));
}
return nozzle;
}
float texture_preview_config_line_width_mm(const DynamicPrintConfig &config,
const char *key,
float ratio_over,
float fallback)
{
const ConfigOptionFloatOrPercent *opt = config.option<ConfigOptionFloatOrPercent>(key);
if (opt == nullptr || !std::isfinite(opt->value) || std::abs(opt->value) <= k_epsilon)
return fallback;
const float value = opt->percent ? ratio_over * float(opt->value) / 100.f : float(opt->value);
return std::isfinite(value) && value > 0.f ? value : fallback;
}
float contoning_flat_surface_lod_pitch_for_texture_preview(const TextureMappingZone &zone,
const std::vector<unsigned int> &component_ids)
{
if (GUI::wxGetApp().preset_bundle == nullptr)
return 0.f;
const DynamicPrintConfig config = GUI::wxGetApp().preset_bundle->full_config();
const float nozzle = texture_preview_config_nozzle_mm(config, component_ids);
const float line_width = texture_preview_config_line_width_mm(config, "line_width", nozzle, nozzle);
const float external_width = texture_preview_config_line_width_mm(config, "outer_wall_line_width", nozzle, line_width);
const float configured =
std::clamp(zone.top_surface_contoning_min_feature_mm,
TextureMappingZone::MinTopSurfaceContoningMinFeatureMm,
TextureMappingZone::MaxTopSurfaceContoningMinFeatureMm);
const float min_feature = configured > 0.f ?
configured :
std::max({ 2.0f, 4.f * nozzle, 3.f * external_width });
float pitch = std::clamp(external_width,
0.25f,
std::max(0.25f, min_feature * 0.5f));
return std::clamp(pitch, 0.25f, std::max(0.25f, min_feature));
}
std::array<float, 2> raw_offset_visibility_factors_for_texture_preview(const TextureMappingZone &zone)
{
const float base_outer_width_mm =
@@ -693,11 +756,34 @@ std::array<unsigned int, 2> simulated_texture_preview_size(unsigned int width,
unsigned int max_edge = 0,
size_t max_pixels = 0)
{
if (max_edge > 0 && max_pixels > 0)
return limited_simulated_texture_preview_size(width, height, max_edge, max_pixels);
return settings.limit_texture_resolution ?
std::array<unsigned int, 2> size = max_edge > 0 && max_pixels > 0 ?
limited_simulated_texture_preview_size(width, height, max_edge, max_pixels) :
settings.limit_texture_resolution ?
limited_simulated_texture_preview_size(width, height) :
std::array<unsigned int, 2>{ width, height };
if (settings.contoning_flat_surface_quantization &&
settings.simulate_top_surface_lod &&
std::isfinite(settings.texture_preview_mm_per_pixel) &&
std::isfinite(settings.top_surface_lod_pitch_mm) &&
settings.texture_preview_mm_per_pixel > 0.f &&
settings.top_surface_lod_pitch_mm > k_epsilon) {
const double scale = double(settings.texture_preview_mm_per_pixel) / double(settings.top_surface_lod_pitch_mm);
if (std::isfinite(scale) && scale > 0.0 && scale < 1.0) {
unsigned int lod_width = std::max(1u, unsigned(std::ceil(double(width) * scale)));
unsigned int lod_height = std::max(1u, unsigned(std::ceil(double(height) * scale)));
const double lod_pixels = double(lod_width) * double(lod_height);
if (lod_pixels > k_contoning_top_surface_preview_lod_max_samples) {
const double sample_cap_scale = std::sqrt(k_contoning_top_surface_preview_lod_max_samples / lod_pixels);
lod_width = std::max(1u, unsigned(std::floor(double(lod_width) * sample_cap_scale)));
lod_height = std::max(1u, unsigned(std::floor(double(lod_height) * sample_cap_scale)));
}
size[0] = std::min(size[0], lod_width);
size[1] = std::min(size[1], lod_height);
}
}
return size;
}
bool simulated_texture_preview_needs_temporary_result(unsigned int width,
@@ -1030,12 +1116,50 @@ bool is_gradient_zone(const TextureMappingZone &zone)
return zone.enabled && !zone.deleted && zone.is_surface_gradient();
}
const TextureMappingGlobalSettings *texture_mapping_global_preview_settings()
{
return GUI::wxGetApp().preset_bundle != nullptr ?
&GUI::wxGetApp().preset_bundle->texture_mapping_global_settings :
nullptr;
}
float texture_preview_opacity_factor()
{
const TextureMappingGlobalSettings *settings = texture_mapping_global_preview_settings();
const float opacity_pct = settings != nullptr ?
settings->preview_opacity_pct :
TextureMappingZone::DefaultPreviewOpacityPct;
return std::clamp(opacity_pct, 0.f, 100.f) / 100.f;
}
bool texture_preview_simulate_colors()
{
const TextureMappingGlobalSettings *settings = texture_mapping_global_preview_settings();
return settings != nullptr ?
settings->preview_simulate_colors :
TextureMappingZone::DefaultPreviewSimulateColors;
}
bool texture_preview_limit_resolution()
{
const TextureMappingGlobalSettings *settings = texture_mapping_global_preview_settings();
return settings != nullptr ?
settings->preview_limit_resolution :
TextureMappingZone::DefaultPreviewLimitResolution;
}
bool texture_preview_simulate_top_surface_lod()
{
const TextureMappingGlobalSettings *settings = texture_mapping_global_preview_settings();
return settings != nullptr && settings->preview_simulate_top_surface_lod;
}
float texture_preview_mix_for_filament(unsigned int filament_id, size_t num_physical, const TextureMappingManager *texture_mgr)
{
const TextureMappingZone *zone = zone_for_filament(filament_id, num_physical, texture_mgr);
if (zone == nullptr || (!is_image_zone(*zone) && !is_gradient_zone(*zone)))
return 0.f;
return std::clamp(zone->preview_opacity_pct, 0.f, 100.f) / 100.f;
return texture_preview_opacity_factor();
}
bool texture_preview_settings_invalid_for_filament(unsigned int filament_id, size_t num_physical, const TextureMappingManager *texture_mgr)
@@ -1524,6 +1648,81 @@ std::vector<float> best_component_mix_weights_for_target(const std::vector<Textu
return weights;
}
size_t contoning_flat_surface_preview_candidate_count(size_t component_count, int total_units, size_t limit)
{
if (component_count == 0 || total_units <= 0)
return 0;
const size_t n = size_t(total_units) + component_count - 1;
size_t k = component_count - 1;
k = std::min(k, n - k);
long double count = 1.0L;
for (size_t idx = 1; idx <= k; ++idx) {
count = count * static_cast<long double>(n - k + idx) / static_cast<long double>(idx);
if (count > static_cast<long double>(limit))
return 0;
}
return size_t(std::max(1.0L, std::round(count)));
}
std::vector<TexturePreviewMixCandidate> build_contoning_flat_surface_mix_candidates(
const std::vector<std::array<float, 3>> &component_colors,
int total_units)
{
const size_t candidate_count =
contoning_flat_surface_preview_candidate_count(component_colors.size(),
total_units,
k_contoning_flat_surface_preview_max_candidates);
if (component_colors.empty() || total_units <= 0 || candidate_count == 0)
return {};
const size_t component_count = component_colors.size();
std::vector<int> units(component_count, 0);
std::vector<float> weights(component_count, 0.f);
std::vector<TexturePreviewMixCandidate> candidates;
candidates.reserve(candidate_count);
std::function<void(size_t, int)> recurse = [&](size_t idx, int remaining_units) {
if (idx + 1 == component_count) {
units[idx] = remaining_units;
for (size_t weight_idx = 0; weight_idx < component_count; ++weight_idx)
weights[weight_idx] = float(units[weight_idx]) / float(std::max(1, total_units));
TexturePreviewMixCandidate candidate;
candidate.rgb = mix_color_solver_components(component_colors, weights, ColorSolverMixModel::PigmentPainter);
candidate.perceptual = oklab_from_srgb(candidate.rgb);
candidates.emplace_back(std::move(candidate));
return;
}
for (int unit = 0; unit <= remaining_units; ++unit) {
units[idx] = unit;
recurse(idx + 1, remaining_units - unit);
}
};
recurse(0, total_units);
return candidates;
}
TexturePreviewMixNearestResult nearest_contoning_flat_surface_mix_candidate(
const std::vector<TexturePreviewMixCandidate> &candidates,
const std::vector<TexturePreviewMixCandidateKdNode> &nodes,
int root,
const std::array<float, 3> &target_rgb)
{
TexturePreviewMixNearestResult result;
if (candidates.empty())
return result;
const std::array<float, 3> target_oklab = oklab_from_srgb(target_rgb);
const std::array<float, 3> axis_weights = { 1.f, 4.f, 4.f };
if (root >= 0 && !nodes.empty())
query_texture_preview_mix_candidate_perceptual_kd_tree(candidates, nodes, target_oklab, axis_weights, root, result);
if (result.best_idx >= candidates.size())
result = nearest_texture_preview_mix_candidates_perceptual_linear(candidates, target_oklab, axis_weights);
return result;
}
float apply_texture_tone_gamma(float channel, float tone_gamma)
{
const float safe_channel = clamp01(channel);
@@ -1654,8 +1853,8 @@ std::vector<TexturePreviewBinaryDitherCandidate> binary_dither_candidates_for_te
return candidates;
}
std::array<float, 3> texture_preview_target_oklab(const TexturePreviewSimulationSettings &settings,
const std::array<float, 4> &sample_rgba)
std::array<float, 3> texture_preview_target_rgb(const TexturePreviewSimulationSettings &settings,
const std::array<float, 4> &sample_rgba)
{
std::array<float, 3> target = {
clamp01(sample_rgba[0]),
@@ -1667,8 +1866,13 @@ std::array<float, 3> texture_preview_target_oklab(const TexturePreviewSimulation
target[1] = apply_texture_tone_gamma(target[1], settings.tone_gamma);
target[2] = apply_texture_tone_gamma(target[2], settings.tone_gamma);
}
target = apply_texture_contrast_to_rgb(target, std::clamp(settings.contrast_pct, 25.f, 300.f) / 100.f);
return oklab_from_srgb(target);
return apply_texture_contrast_to_rgb(target, std::clamp(settings.contrast_pct, 25.f, 300.f) / 100.f);
}
std::array<float, 3> texture_preview_target_oklab(const TexturePreviewSimulationSettings &settings,
const std::array<float, 4> &sample_rgba)
{
return oklab_from_srgb(texture_preview_target_rgb(settings, sample_rgba));
}
struct TexturePreviewBinaryDitherNearestResult {
@@ -2118,6 +2322,96 @@ std::vector<float> component_weights_for_texture_preview(const TexturePreviewSim
return desired;
}
std::array<float, 3> fallback_contoning_flat_surface_rgb_for_texture_preview(
const TexturePreviewSimulationSettings &settings,
const std::array<float, 4> &sample_rgba,
int total_units)
{
const size_t component_count = settings.component_colors.size();
if (component_count == 0 || total_units <= 0)
return { sample_rgba[0], sample_rgba[1], sample_rgba[2] };
TexturePreviewSimulationSettings local_settings = settings;
local_settings.dithering_enabled = false;
local_settings.compact_offset_mode = false;
local_settings.contoning_flat_surface_quantization = false;
std::vector<float> continuous_weights = component_weights_for_texture_preview(local_settings, sample_rgba, false);
continuous_weights.resize(component_count, 0.f);
float total_weight = 0.f;
for (float &weight : continuous_weights) {
weight = clamp01(weight);
total_weight += weight;
}
if (total_weight <= k_epsilon) {
const std::array<float, 3> target_rgb = texture_preview_target_rgb(settings, sample_rgba);
const std::array<float, 3> target_oklab = oklab_from_srgb(target_rgb);
const std::array<float, 3> axis_weights = { 1.f, 4.f, 4.f };
size_t best_idx = 0;
float best_error = std::numeric_limits<float>::max();
for (size_t idx = 0; idx < settings.component_colors.size(); ++idx) {
const std::array<float, 3> component_oklab = oklab_from_srgb(settings.component_colors[idx]);
const float dl = component_oklab[0] - target_oklab[0];
const float da = component_oklab[1] - target_oklab[1];
const float db = component_oklab[2] - target_oklab[2];
const float error = axis_weights[0] * dl * dl + axis_weights[1] * da * da + axis_weights[2] * db * db;
if (error < best_error) {
best_error = error;
best_idx = idx;
}
}
std::vector<float> one_hot(component_count, 0.f);
one_hot[best_idx] = 1.f;
return mix_color_solver_components(settings.component_colors, one_hot, ColorSolverMixModel::PigmentPainter);
}
struct QuantizedWeight {
size_t idx { 0 };
float remainder { 0.f };
};
std::vector<int> counts(component_count, 0);
std::vector<QuantizedWeight> remainders;
remainders.reserve(component_count);
int assigned = 0;
for (size_t idx = 0; idx < component_count; ++idx) {
const float exact = continuous_weights[idx] * float(total_units) / total_weight;
const int count = std::clamp(int(std::floor(exact)), 0, total_units);
counts[idx] = count;
assigned += count;
remainders.push_back({ idx, exact - float(count) });
}
std::sort(remainders.begin(), remainders.end(), [](const QuantizedWeight &lhs, const QuantizedWeight &rhs) {
return lhs.remainder > rhs.remainder;
});
for (int remaining = total_units - assigned, idx = 0; remaining > 0 && !remainders.empty(); --remaining, ++idx)
++counts[remainders[size_t(idx) % remainders.size()].idx];
std::vector<float> quantized_weights(component_count, 0.f);
for (size_t idx = 0; idx < component_count; ++idx)
quantized_weights[idx] = float(counts[idx]) / float(total_units);
return mix_color_solver_components(settings.component_colors, quantized_weights, ColorSolverMixModel::PigmentPainter);
}
std::array<float, 3> contoning_flat_surface_rgb_for_texture_preview(
const TexturePreviewSimulationSettings &settings,
const std::array<float, 4> &sample_rgba,
const std::vector<TexturePreviewMixCandidate> &candidates,
const std::vector<TexturePreviewMixCandidateKdNode> &nodes,
int root,
int total_units)
{
const std::array<float, 3> target_rgb = texture_preview_target_rgb(settings, sample_rgba);
if (!candidates.empty()) {
const TexturePreviewMixNearestResult nearest =
nearest_contoning_flat_surface_mix_candidate(candidates, nodes, root, target_rgb);
if (nearest.best_idx < candidates.size())
return candidates[nearest.best_idx].rgb;
}
return fallback_contoning_flat_surface_rgb_for_texture_preview(settings, sample_rgba, total_units);
}
std::vector<float> raw_offset_print_width_weights_for_texture_preview(const TexturePreviewSimulationSettings &settings,
const std::vector<float> &raw_weights)
{
@@ -2205,7 +2499,7 @@ std::optional<TexturePreviewSimulationSettings> texture_preview_simulation_setti
const std::vector<std::string> &physical_colors)
{
const TextureMappingZone *zone = zone_for_filament(filament_id, num_physical, texture_mgr);
if (zone == nullptr || !is_image_zone(*zone) || !zone->preview_simulate_colors)
if (zone == nullptr || !is_image_zone(*zone) || !texture_preview_simulate_colors())
return std::nullopt;
TexturePreviewSimulationSettings settings;
@@ -2216,7 +2510,7 @@ std::optional<TexturePreviewSimulationSettings> texture_preview_simulation_setti
int(TextureMappingZone::FilamentColorAny),
int(TextureMappingZone::FilamentColorRGBKW));
settings.force_sequential_filaments = zone->force_sequential_filaments;
settings.limit_texture_resolution = zone->preview_limit_resolution;
settings.limit_texture_resolution = texture_preview_limit_resolution();
settings.dithering_enabled =
zone->dithering_enabled &&
settings.mapping_mode != int(TextureMappingZone::TextureMappingRawValues);
@@ -2246,6 +2540,17 @@ std::optional<TexturePreviewSimulationSettings> texture_preview_simulation_setti
int(TextureMappingZone::GenericSolverLegacy),
int(TextureMappingZone::GenericSolverV2));
settings.generic_solver_mix_model = TextureMappingZone::DefaultGenericSolverMixModel;
if (zone->top_surface_contoning_active()) {
const int stack_layers =
std::clamp(zone->top_surface_contoning_stack_layers,
TextureMappingZone::MinTopSurfaceContoningStackLayers,
TextureMappingZone::MaxTopSurfaceContoningStackLayers);
const int pattern_filaments =
std::clamp(zone->top_surface_contoning_pattern_filaments,
TextureMappingZone::MinTopSurfaceContoningPatternFilaments,
TextureMappingZone::MaxTopSurfaceContoningPatternFilaments);
settings.contoning_flat_surface_pattern_filaments = std::max(1, std::min(stack_layers, pattern_filaments));
}
settings.minimum_visibility_offset_factor = zone->minimum_visibility_offset_enabled ?
std::clamp((std::isfinite(zone->minimum_visibility_offset_pct) ?
zone->minimum_visibility_offset_pct :
@@ -2256,6 +2561,9 @@ std::optional<TexturePreviewSimulationSettings> texture_preview_simulation_setti
settings.component_ids = TextureMappingManager::effective_texture_component_ids(*zone, num_physical, physical_colors);
if (settings.component_ids.empty())
return std::nullopt;
settings.simulate_top_surface_lod =
texture_preview_simulate_top_surface_lod() &&
zone->top_surface_contoning_active();
const bool raw_values_mode = settings.mapping_mode == int(TextureMappingZone::TextureMappingRawValues);
settings.component_colors.reserve(settings.component_ids.size());
@@ -2318,6 +2626,13 @@ size_t texture_preview_simulation_signature(const ModelVolume &model_volume,
mix(std::hash<int>{}(settings.generic_solver_lookup_mode));
mix(std::hash<int>{}(settings.generic_solver_mode));
mix(std::hash<int>{}(settings.generic_solver_mix_model));
mix(std::hash<int>{}(settings.contoning_flat_surface_quantization ? 1 : 0));
if (settings.contoning_flat_surface_quantization) {
mix(std::hash<int>{}(settings.contoning_flat_surface_pattern_filaments));
mix(std::hash<int>{}(settings.simulate_top_surface_lod ? 1 : 0));
if (settings.simulate_top_surface_lod)
mix(std::hash<int>{}(int(std::lround(settings.top_surface_lod_pitch_mm * 100000.f))));
}
mix(std::hash<int>{}(int(std::lround(settings.contrast_pct * 100.f))));
mix(std::hash<int>{}(int(std::lround(settings.tone_gamma * 1000.f))));
mix(std::hash<int>{}(int(std::lround(settings.minimum_visibility_offset_factor * 100000.f))));
@@ -2363,15 +2678,35 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig
return result;
prepare_texture_preview_simulation_settings(settings);
const int contoning_flat_surface_pattern_filaments =
std::clamp(settings.contoning_flat_surface_pattern_filaments,
TextureMappingZone::MinTopSurfaceContoningPatternFilaments,
TextureMappingZone::MaxTopSurfaceContoningPatternFilaments);
const bool use_contoning_flat_surface_quantization =
settings.contoning_flat_surface_quantization &&
settings.mapping_mode != int(TextureMappingZone::TextureMappingRawValues) &&
!settings.component_colors.empty() &&
contoning_flat_surface_pattern_filaments > 0;
const std::vector<TexturePreviewMixCandidate> contoning_flat_surface_candidates =
use_contoning_flat_surface_quantization ?
build_contoning_flat_surface_mix_candidates(settings.component_colors, contoning_flat_surface_pattern_filaments) :
std::vector<TexturePreviewMixCandidate>{};
std::vector<TexturePreviewMixCandidateKdNode> contoning_flat_surface_nodes;
const int contoning_flat_surface_root = contoning_flat_surface_candidates.empty() ?
-1 :
build_generic_mix_candidate_kd_tree(contoning_flat_surface_candidates, contoning_flat_surface_nodes, true);
const bool use_raw_offsets =
!use_contoning_flat_surface_quantization &&
source_raw_component_channels.size() == settings.component_colors.size() &&
source_raw_offsets.size() >= size_t(width) * size_t(height) * size_t(source_raw_channels);
const bool use_binary_dithering =
!use_contoning_flat_surface_quantization &&
settings.dithering_enabled &&
settings.mapping_mode != int(TextureMappingZone::TextureMappingRawValues) &&
!is_halftone_dithering_method_for_texture_preview(settings.dithering_method) &&
!use_raw_offsets;
const bool use_halftone_dithering =
!use_contoning_flat_surface_quantization &&
settings.dithering_enabled &&
settings.mapping_mode != int(TextureMappingZone::TextureMappingRawValues) &&
is_halftone_dithering_method_for_texture_preview(settings.dithering_method) &&
@@ -2404,15 +2739,57 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig
return key;
};
auto component_coverage_at_source = [&](double src_x, double src_y, size_t component_idx) {
const double source_step_x = double(width) / double(std::max(1u, result.width));
const double source_step_y = double(height) / double(std::max(1u, result.height));
const bool use_contoning_flat_surface_lod_supersampling =
use_contoning_flat_surface_quantization &&
settings.simulate_top_surface_lod &&
(source_step_x > 1.05 || source_step_y > 1.05);
auto sample_blended_source_color_at_source = [&](double sample_src_x, double sample_src_y) {
const std::array<unsigned char, 4> source_rgba_sample =
sample_texture_preview_rgba_bilinear_at_source(source_rgba, width, height, src_x, src_y);
const ColorRGBA blended_source_color =
composite_texture_mapping_color_over_background_for_preview(ColorRGBA(float(source_rgba_sample[0]) / 255.f,
float(source_rgba_sample[1]) / 255.f,
float(source_rgba_sample[2]) / 255.f,
float(source_rgba_sample[3]) / 255.f),
background_color);
sample_texture_preview_rgba_bilinear_at_source(source_rgba, width, height, sample_src_x, sample_src_y);
return composite_texture_mapping_color_over_background_for_preview(ColorRGBA(float(source_rgba_sample[0]) / 255.f,
float(source_rgba_sample[1]) / 255.f,
float(source_rgba_sample[2]) / 255.f,
float(source_rgba_sample[3]) / 255.f),
background_color);
};
auto sample_blended_source_color_for_texel = [&](double src_x, double src_y) {
if (!use_contoning_flat_surface_lod_supersampling)
return sample_blended_source_color_at_source(src_x, src_y);
const double radius_x = source_step_x * 0.35;
const double radius_y = source_step_y * 0.35;
const std::array<std::array<double, 2>, 9> offsets = {
std::array<double, 2>{ 0.0, 0.0 },
std::array<double, 2>{ -radius_x, -radius_y },
std::array<double, 2>{ 0.0, -radius_y },
std::array<double, 2>{ radius_x, -radius_y },
std::array<double, 2>{ -radius_x, 0.0 },
std::array<double, 2>{ radius_x, 0.0 },
std::array<double, 2>{ -radius_x, radius_y },
std::array<double, 2>{ 0.0, radius_y },
std::array<double, 2>{ radius_x, radius_y }
};
float r = 0.f;
float g = 0.f;
float b = 0.f;
float a = 0.f;
for (const std::array<double, 2> &offset : offsets) {
const ColorRGBA color = sample_blended_source_color_at_source(src_x + offset[0], src_y + offset[1]);
r += color.r();
g += color.g();
b += color.b();
a += color.a();
}
const float inv_count = 1.f / float(offsets.size());
return ColorRGBA(r * inv_count, g * inv_count, b * inv_count, a * inv_count);
};
auto component_coverage_at_source = [&](double src_x, double src_y, size_t component_idx) {
const ColorRGBA blended_source_color = sample_blended_source_color_at_source(src_x, src_y);
const std::array<float, 4> sampled_rgba = {
blended_source_color.r(),
blended_source_color.g(),
@@ -2481,14 +2858,7 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig
for (unsigned int x = 0; x < result.width; ++x) {
const double src_x = (double(x) + 0.5) * double(width) / double(std::max(1u, result.width)) - 0.5;
const double src_y = (double(y) + 0.5) * double(height) / double(std::max(1u, result.height)) - 0.5;
const std::array<unsigned char, 4> source_rgba_sample =
sample_texture_preview_rgba_bilinear_at_source(source_rgba, width, height, src_x, src_y);
const ColorRGBA blended_source_color =
composite_texture_mapping_color_over_background_for_preview(ColorRGBA(float(source_rgba_sample[0]) / 255.f,
float(source_rgba_sample[1]) / 255.f,
float(source_rgba_sample[2]) / 255.f,
float(source_rgba_sample[3]) / 255.f),
background_color);
const ColorRGBA blended_source_color = sample_blended_source_color_for_texel(src_x, src_y);
const std::array<unsigned char, 3> source_rgb = {
to_u8(blended_source_color.r()),
to_u8(blended_source_color.g()),
@@ -2517,6 +2887,7 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig
1.f
};
std::vector<float> component_weights;
std::optional<std::array<float, 3>> forced_simulated_rgb;
if (use_raw_offsets) {
const std::vector<float> raw_sample =
sample_texture_preview_raw_offsets_bilinear(source_raw_offsets,
@@ -2531,7 +2902,15 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig
if (component_weights.size() == settings.component_colors.size())
component_weights = raw_offset_print_width_weights_for_texture_preview(settings, component_weights);
} else {
if (use_halftone_dithering) {
if (use_contoning_flat_surface_quantization) {
forced_simulated_rgb =
contoning_flat_surface_rgb_for_texture_preview(settings,
sample_rgba,
contoning_flat_surface_candidates,
contoning_flat_surface_nodes,
contoning_flat_surface_root,
contoning_flat_surface_pattern_filaments);
} else if (use_halftone_dithering) {
const std::vector<float> continuous_coverage =
component_weights_for_texture_preview(settings, sample_rgba, false);
uint32_t mask = 0;
@@ -2613,13 +2992,15 @@ TexturePreviewSimulationResult build_simulated_texture_preview_result(size_t sig
activity = 1.f;
}
const std::array<float, 3> simulated_rgb = activity > k_epsilon ?
const std::array<float, 3> simulated_rgb = forced_simulated_rgb.has_value() ?
*forced_simulated_rgb :
(activity > k_epsilon ?
mix_color_solver_components(settings.component_colors,
component_weights,
use_halftone_dithering ?
ColorSolverMixModel::PigmentPainter :
color_solver_mix_model_from_index(settings.generic_solver_mix_model)) :
std::array<float, 3>{ sample_rgba[0], sample_rgba[1], sample_rgba[2] };
std::array<float, 3>{ sample_rgba[0], sample_rgba[1], sample_rgba[2] });
const std::array<unsigned char, 4> out_rgba = {
to_u8(simulated_rgb[0]),
@@ -2908,10 +3289,16 @@ bool texture_preview_simulation_has_temporary_pending_impl()
return false;
}
size_t texture_preview_simulation_cache_key(const ModelVolume &model_volume, unsigned int filament_id)
size_t texture_preview_simulation_cache_key(const ModelVolume &model_volume,
unsigned int filament_id,
unsigned int variant = 0,
float texture_preview_mm_per_pixel = 0.f)
{
size_t key = reinterpret_cast<size_t>(&model_volume);
key ^= std::hash<unsigned int>{}(filament_id) + 0x9e3779b97f4a7c15ull + (key << 6) + (key >> 2);
key ^= std::hash<unsigned int>{}(variant) + 0x9e3779b97f4a7c15ull + (key << 6) + (key >> 2);
if (std::isfinite(texture_preview_mm_per_pixel) && texture_preview_mm_per_pixel > 0.f)
key ^= std::hash<int>{}(int(std::lround(texture_preview_mm_per_pixel * 100000.f))) + 0x9e3779b97f4a7c15ull + (key << 6) + (key >> 2);
return key;
}
@@ -2957,23 +3344,40 @@ void consume_temporary_simulated_texture_preview_result(TexturePreviewSimulation
}
const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const ModelVolume &model_volume,
const Transform3d &world_matrix,
unsigned int filament_id,
size_t num_physical,
const TextureMappingManager *texture_mgr,
size_t source_texture_signature,
const GUI::GLTexture &fallback_texture)
const GUI::GLTexture &fallback_texture,
bool contoning_flat_surface_quantization = false,
bool *simulated_ready = nullptr)
{
if (simulated_ready != nullptr)
*simulated_ready = false;
const TextureMappingZone *zone = zone_for_filament(filament_id, num_physical, texture_mgr);
if (contoning_flat_surface_quantization &&
(zone == nullptr || !is_image_zone(*zone) || !texture_preview_simulate_colors() || !zone->top_surface_contoning_active()))
return nullptr;
const std::vector<std::string> physical_colors = physical_filament_colors_for_texture_preview(num_physical);
std::optional<TexturePreviewSimulationSettings> settings =
texture_preview_simulation_settings_for_filament(filament_id, num_physical, texture_mgr, physical_colors);
if (!settings.has_value())
return &fallback_texture;
settings->texture_preview_mm_per_pixel = estimated_texture_preview_mm_per_pixel(model_volume);
return contoning_flat_surface_quantization ? nullptr : &fallback_texture;
settings->contoning_flat_surface_quantization = contoning_flat_surface_quantization;
settings->texture_preview_mm_per_pixel = estimated_texture_preview_mm_per_pixel(model_volume, &world_matrix);
if (settings->contoning_flat_surface_quantization && settings->simulate_top_surface_lod)
settings->top_surface_lod_pitch_mm =
contoning_flat_surface_lod_pitch_for_texture_preview(*zone, settings->component_ids);
const ColorRGBA background_color = texture_mapping_background_color_for_preview(model_volume);
const size_t simulation_signature = texture_preview_simulation_signature(model_volume, source_texture_signature, *settings);
auto &cache = texture_preview_simulation_cache();
const size_t cache_key = texture_preview_simulation_cache_key(model_volume, filament_id);
const size_t cache_key = texture_preview_simulation_cache_key(model_volume,
filament_id,
contoning_flat_surface_quantization ? 1u : 0u,
settings->texture_preview_mm_per_pixel);
std::shared_ptr<TexturePreviewSimulationCacheEntry> &entry_ref = cache[cache_key];
if (entry_ref == nullptr)
entry_ref = std::make_shared<TexturePreviewSimulationCacheEntry>();
@@ -2997,8 +3401,11 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model
entry.pending_job_state.reset();
}
if (entry.texture != nullptr && entry.uploaded_signature == simulation_signature && entry.texture->get_id() != 0)
if (entry.texture != nullptr && entry.uploaded_signature == simulation_signature && entry.texture->get_id() != 0) {
if (simulated_ready != nullptr)
*simulated_ready = true;
return entry.texture.get();
}
if (!entry.pending_future.valid()) {
entry.pending_signature = simulation_signature;
@@ -3064,10 +3471,13 @@ const GUI::GLTexture *simulated_texture_preview_texture_for_filament(const Model
if (temporary_signature != 0 &&
entry.texture != nullptr &&
entry.uploaded_signature == temporary_signature &&
entry.texture->get_id() != 0)
entry.texture->get_id() != 0) {
if (simulated_ready != nullptr)
*simulated_ready = true;
return entry.texture.get();
}
return &fallback_texture;
return contoning_flat_surface_quantization ? nullptr : &fallback_texture;
}
} // namespace
@@ -4405,7 +4815,7 @@ std::optional<SurfaceGradientPreviewSettings> surface_gradient_preview_settings_
std::clamp(zone.offset_fade_mode,
int(TextureMappingZone::OffsetFadeNone),
int(TextureMappingZone::OffsetFadeOutInReversed));
settings.limit_texture_resolution = zone.preview_limit_resolution;
settings.limit_texture_resolution = texture_preview_limit_resolution();
const float base_outer_width_mm = std::max(0.05f, surface_gradient_preview_config_float("texture_mapping_outer_wall_gradient_max_line_width", 0.95f));
const float min_outer_width_mm = std::clamp(surface_gradient_preview_config_float("texture_mapping_outer_wall_gradient_min_line_width", 0.32f),
0.05f,
@@ -5033,6 +5443,10 @@ static size_t texture_preview_settings_signature_impl(size_t num_physical,
signature_mix_float(texture_preview_config_float("texture_mapping_outer_wall_gradient_global_strength", 100.f), 100.f);
signature_mix_float(texture_preview_config_float("texture_mapping_outer_wall_gradient_max_line_width", 0.95f), 1000.f);
signature_mix_float(texture_preview_config_float("texture_mapping_outer_wall_gradient_min_line_width", 0.32f), 1000.f);
signature_mix_float(texture_preview_opacity_factor(), 10000.f);
signature_mix(std::hash<int>{}(texture_preview_simulate_colors() ? 1 : 0));
signature_mix(std::hash<int>{}(texture_preview_limit_resolution() ? 1 : 0));
signature_mix(std::hash<int>{}(texture_preview_simulate_top_surface_lod() ? 1 : 0));
if (texture_mgr == nullptr)
return signature;
@@ -5066,6 +5480,11 @@ static size_t texture_preview_settings_signature_impl(size_t num_physical,
signature_mix(std::hash<int>{}(zone.texture_mapping_mode));
signature_mix(std::hash<int>{}(zone.filament_color_mode));
signature_mix(std::hash<int>{}(zone.force_sequential_filaments ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_image_printing_enabled ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_image_printing_method));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_stack_layers));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_pattern_filaments));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_color_lower_surfaces ? 1 : 0));
signature_mix(std::hash<int>{}(zone.nonlinear_offset_adjustment ? 1 : 0));
signature_mix(std::hash<int>{}(zone.compact_offset_mode ? 1 : 0));
signature_mix(std::hash<int>{}(zone.use_legacy_fixed_color_mode ? 1 : 0));
@@ -5080,9 +5499,6 @@ static size_t texture_preview_settings_signature_impl(size_t num_physical,
signature_mix(std::hash<int>{}(zone.generic_solver_lookup_mode));
signature_mix(std::hash<int>{}(zone.generic_solver_mode));
signature_mix(std::hash<int>{}(TextureMappingZone::DefaultGenericSolverMixModel));
signature_mix(std::hash<int>{}(zone.preview_simulate_colors ? 1 : 0));
signature_mix(std::hash<int>{}(zone.preview_limit_resolution ? 1 : 0));
signature_mix_float(zone.preview_opacity_pct, 100.f);
signature_mix_float(zone.contrast_pct, 100.f);
signature_mix_float(zone.tone_gamma);
for (const float strength_pct : zone.filament_strengths_pct)
@@ -5123,7 +5539,7 @@ static bool active_texture_preview_zone_ids_contains(const std::vector<unsigned
static bool texture_preview_zone_uses_halftone_model(const TextureMappingZone &zone)
{
if (!zone.enabled || zone.deleted || !zone.is_image_texture() || !zone.preview_simulate_colors)
if (!zone.enabled || zone.deleted || !zone.is_image_texture() || !texture_preview_simulate_colors())
return false;
const int mapping_mode = std::clamp(zone.texture_mapping_mode,
int(TextureMappingZone::TextureMappingFilamentBlending),
@@ -5136,6 +5552,29 @@ static bool texture_preview_zone_uses_halftone_model(const TextureMappingZone &z
is_halftone_dithering_method_for_texture_preview(method);
}
static bool texture_preview_zone_uses_contoning_flat_surface_texture(const TextureMappingZone &zone)
{
return zone.enabled &&
!zone.deleted &&
zone.is_image_texture() &&
texture_preview_simulate_colors() &&
zone.texture_mapping_mode != int(TextureMappingZone::TextureMappingRawValues) &&
zone.top_surface_contoning_active();
}
static bool texture_preview_contoning_flat_surface_texture_for_filament(unsigned int filament_id,
size_t num_physical,
const TextureMappingManager *texture_mgr,
bool &include_bottom)
{
include_bottom = false;
const TextureMappingZone *zone = zone_for_filament(filament_id, num_physical, texture_mgr);
if (zone == nullptr || !texture_preview_zone_uses_contoning_flat_surface_texture(*zone))
return false;
include_bottom = zone->top_surface_contoning_color_lower_surfaces;
return true;
}
static bool texture_preview_gradient_zone_uses_model(const TextureMappingZone &zone, size_t num_physical)
{
if (!zone.enabled || zone.deleted || !zone.is_surface_gradient())
@@ -5176,6 +5615,11 @@ static void texture_preview_mix_zone_baked_model_settings(size_t &signature,
signature_mix(std::hash<int>{}(zone.texture_mapping_mode));
signature_mix(std::hash<int>{}(zone.filament_color_mode));
signature_mix(std::hash<int>{}(zone.force_sequential_filaments ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_image_printing_enabled ? 1 : 0));
signature_mix(std::hash<int>{}(zone.top_surface_image_printing_method));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_stack_layers));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_pattern_filaments));
signature_mix(std::hash<int>{}(zone.top_surface_contoning_color_lower_surfaces ? 1 : 0));
signature_mix(std::hash<int>{}(zone.nonlinear_offset_adjustment ? 1 : 0));
signature_mix(std::hash<int>{}(zone.compact_offset_mode ? 1 : 0));
signature_mix(std::hash<int>{}(zone.use_legacy_fixed_color_mode ? 1 : 0));
@@ -5190,8 +5634,6 @@ static void texture_preview_mix_zone_baked_model_settings(size_t &signature,
signature_mix(std::hash<int>{}(zone.generic_solver_lookup_mode));
signature_mix(std::hash<int>{}(zone.generic_solver_mode));
signature_mix(std::hash<int>{}(TextureMappingZone::DefaultGenericSolverMixModel));
signature_mix(std::hash<int>{}(zone.preview_simulate_colors ? 1 : 0));
signature_mix(std::hash<int>{}(zone.preview_limit_resolution ? 1 : 0));
signature_mix_float(zone.contrast_pct, 100.f);
signature_mix_float(zone.tone_gamma);
signature_mix(std::hash<int>{}(zone.linear_gradient_mode));
@@ -5286,7 +5728,7 @@ size_t texture_preview_model_settings_signature(size_t num_physical,
const bool halftone_model = texture_preview_zone_uses_halftone_model(zone);
const bool simulated_vertex_color_model =
image_zone &&
zone.preview_simulate_colors &&
texture_preview_simulate_colors() &&
(has_texture_mapping_color_preview_data || (!has_texture_preview_data && has_vertex_color_preview_data));
signature_mix(std::hash<int>{}(halftone_model ? 1 : 0));
signature_mix(std::hash<int>{}(simulated_vertex_color_model ? 1 : 0));
@@ -5339,9 +5781,13 @@ void render_model_texture_preview_models(
set_color_match_preview_uniforms(*shader, color_match);
glsafe(::glActiveTexture(GL_TEXTURE0));
shader->set_uniform("uniform_texture", 0);
shader->set_uniform("contoning_flat_surface_texture", 1);
shader->set_uniform("contoning_flat_surface_texture_enabled", false);
shader->set_uniform("contoning_flat_surface_include_bottom", false);
const size_t texture_signature = model_volume_texture_preview_signature(model_volume);
GLuint bound_texture_id = 0;
GLuint bound_contoning_flat_texture_id = 0;
const bool color_match_active = color_match != nullptr && color_match->active;
for (size_t idx = 0; idx < models.size(); ++idx) {
if (color_match_active && !color_match_preview_allows_filament(color_match, filament_ids[idx]))
@@ -5361,6 +5807,7 @@ void render_model_texture_preview_models(
const GUI::GLTexture *preview_texture = force_original_texture ?
&texture :
simulated_texture_preview_texture_for_filament(model_volume,
model_matrix,
filament_ids[idx],
num_physical,
texture_mgr,
@@ -5370,16 +5817,50 @@ void render_model_texture_preview_models(
continue;
if (preview_texture->get_id() != bound_texture_id) {
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glBindTexture(GL_TEXTURE_2D, preview_texture->get_id()));
bound_texture_id = preview_texture->get_id();
}
bool include_contoning_bottom = false;
bool contoning_flat_ready = false;
const bool use_contoning_flat_texture =
!color_match_active &&
!force_original_texture &&
texture_preview_contoning_flat_surface_texture_for_filament(filament_ids[idx],
num_physical,
texture_mgr,
include_contoning_bottom);
const GUI::GLTexture *contoning_flat_texture = use_contoning_flat_texture ?
simulated_texture_preview_texture_for_filament(model_volume,
model_matrix,
filament_ids[idx],
num_physical,
texture_mgr,
texture_signature,
texture,
true,
&contoning_flat_ready) :
nullptr;
const bool contoning_flat_enabled =
contoning_flat_ready && contoning_flat_texture != nullptr && contoning_flat_texture->get_id() != 0;
if (contoning_flat_enabled && contoning_flat_texture->get_id() != bound_contoning_flat_texture_id) {
glsafe(::glActiveTexture(GL_TEXTURE1));
glsafe(::glBindTexture(GL_TEXTURE_2D, contoning_flat_texture->get_id()));
bound_contoning_flat_texture_id = contoning_flat_texture->get_id();
glsafe(::glActiveTexture(GL_TEXTURE0));
}
shader->set_uniform("contoning_flat_surface_texture_enabled", contoning_flat_enabled);
shader->set_uniform("contoning_flat_surface_include_bottom", contoning_flat_enabled && include_contoning_bottom);
shader->set_uniform("texture_preview_mix", color_match_active ? 1.f : mix);
shader->set_uniform("invalid_texture_mapping", invalid);
models[idx].set_color(colors[idx]);
models[idx].render();
}
glsafe(::glActiveTexture(GL_TEXTURE1));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
shader->stop_using();
restore_render_state(render_state);
@@ -5426,6 +5907,7 @@ void render_model_texture_preview_model(
const GUI::GLTexture *preview_texture = force_original_texture ?
&texture :
simulated_texture_preview_texture_for_filament(model_volume,
model_matrix,
filament_id,
num_physical,
texture_mgr,
@@ -5450,6 +5932,36 @@ void render_model_texture_preview_model(
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glBindTexture(GL_TEXTURE_2D, preview_texture->get_id()));
shader->set_uniform("uniform_texture", 0);
shader->set_uniform("contoning_flat_surface_texture", 1);
bool include_contoning_bottom = false;
bool contoning_flat_ready = false;
const bool use_contoning_flat_texture =
!color_match_active &&
!force_original_texture &&
texture_preview_contoning_flat_surface_texture_for_filament(filament_id,
num_physical,
texture_mgr,
include_contoning_bottom);
const GUI::GLTexture *contoning_flat_texture = use_contoning_flat_texture ?
simulated_texture_preview_texture_for_filament(model_volume,
model_matrix,
filament_id,
num_physical,
texture_mgr,
texture_signature,
texture,
true,
&contoning_flat_ready) :
nullptr;
const bool contoning_flat_enabled =
contoning_flat_ready && contoning_flat_texture != nullptr && contoning_flat_texture->get_id() != 0;
if (contoning_flat_enabled) {
glsafe(::glActiveTexture(GL_TEXTURE1));
glsafe(::glBindTexture(GL_TEXTURE_2D, contoning_flat_texture->get_id()));
glsafe(::glActiveTexture(GL_TEXTURE0));
}
shader->set_uniform("contoning_flat_surface_texture_enabled", contoning_flat_enabled);
shader->set_uniform("contoning_flat_surface_include_bottom", contoning_flat_enabled && include_contoning_bottom);
shader->set_uniform("texture_preview_mix", color_match_active ? 1.f : mix);
shader->set_uniform("invalid_texture_mapping", invalid);
model.set_color(color);
@@ -5458,6 +5970,9 @@ void render_model_texture_preview_model(
else
model.render(render_range);
glsafe(::glActiveTexture(GL_TEXTURE1));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
glsafe(::glActiveTexture(GL_TEXTURE0));
glsafe(::glBindTexture(GL_TEXTURE_2D, 0));
shader->stop_using();
restore_render_state(render_state);

View File

@@ -621,7 +621,9 @@ static bool assign_imported_texture_mapping_zone(Model &model)
return false;
project_config.option<ConfigOptionString>("texture_mapping_definitions", true);
project_config.option<ConfigOptionString>("texture_mapping_global_settings", true);
bundle->texture_mapping_zones.load_entries(project_config.opt_string("texture_mapping_definitions"), color_opt->values);
bundle->texture_mapping_global_settings.load(project_config.opt_string("texture_mapping_global_settings"));
const unsigned int zone_id = bundle->texture_mapping_zones.ensure_image_texture_zone(color_opt->values.size(), color_opt->values);
if (zone_id == 0)
return false;
@@ -636,6 +638,15 @@ static bool assign_imported_texture_mapping_zone(Model &model)
opt->value = serialized;
else
print_config.set_key_value("texture_mapping_definitions", new ConfigOptionString(serialized));
const std::string global_serialized = bundle->texture_mapping_global_settings.serialize();
if (ConfigOptionString *opt = project_config.option<ConfigOptionString>("texture_mapping_global_settings"))
opt->value = global_serialized;
else
project_config.set_key_value("texture_mapping_global_settings", new ConfigOptionString(global_serialized));
if (ConfigOptionString *opt = print_config.option<ConfigOptionString>("texture_mapping_global_settings"))
opt->value = global_serialized;
else
print_config.set_key_value("texture_mapping_global_settings", new ConfigOptionString(global_serialized));
for (ModelObject *object : model.objects) {
if (!model_object_has_imported_texture_mapping_data(object))
@@ -760,6 +771,7 @@ static void load_texture_mapping_definitions(PresetBundle &bundle, const std::st
set_texture_mapping_definitions(bundle, serialized);
const ConfigOptionStrings *color_opt = bundle.project_config.option<ConfigOptionStrings>("filament_colour", false);
bundle.texture_mapping_zones.load_entries(serialized, color_opt != nullptr ? color_opt->values : std::vector<std::string>());
set_texture_mapping_definitions(bundle, bundle.texture_mapping_zones.serialize_entries());
}
static void sync_model_texture_mapping_definitions(Model &model, const std::string &serialized)
@@ -1867,10 +1879,8 @@ public:
const std::vector<float> &component_transmission_distances_mm,
int transmission_distance_calibration_mode,
float tone_gamma,
float preview_opacity_pct,
bool force_sequential_filaments,
bool auto_adjust_filament_selection,
bool preview_limit_resolution,
bool reduce_outer_surface_texture,
bool seam_hiding,
bool nonlinear_offset_adjustment,
@@ -2125,13 +2135,28 @@ public:
m_force_sequential_filaments_checkbox->SetValue(force_sequential_filaments);
filament_root->Add(m_force_sequential_filaments_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
filament_root->AddStretchSpacer(1);
auto *filament_calibration_note =
new wxStaticText(filament_page,
wxID_ANY,
_L("NOTE: Filament/TD calibration is not currently used in top-surface coloring"),
wxDefaultPosition,
wxSize(FromDIP(390), -1));
filament_calibration_note->Wrap(FromDIP(390));
filament_root->Add(filament_calibration_note, 0, wxEXPAND | wxLEFT | wxRIGHT | wxBOTTOM, gap);
filament_page->Bind(wxEVT_SIZE, [this, filament_page, filament_calibration_note, gap](wxSizeEvent &evt) {
const int wrap_width = std::max(FromDIP(120), filament_page->GetClientSize().GetWidth() - gap * 2);
filament_calibration_note->Wrap(wrap_width);
evt.Skip();
});
auto *preview_box = new wxStaticBoxSizer(wxVERTICAL, preview_page, _L("3D Preview"));
auto *preview_opacity_row = new wxBoxSizer(wxHORIZONTAL);
preview_opacity_row->Add(new wxStaticText(preview_page, wxID_ANY, _L("Texture opacity")),
0,
wxALIGN_CENTER_VERTICAL | wxRIGHT,
gap);
const int opacity = std::clamp(int(std::lround(preview_opacity_pct)), 0, 100);
const int opacity = std::clamp(int(std::lround(m_global_settings.preview_opacity_pct)), 0, 100);
m_preview_opacity_slider = new wxSlider(preview_page, wxID_ANY, opacity, 0, 100, wxDefaultPosition, wxSize(FromDIP(180), -1), wxSL_HORIZONTAL | wxSL_AUTOTICKS);
m_preview_opacity_spin = new wxSpinCtrl(preview_page, wxID_ANY, wxEmptyString, wxDefaultPosition, wxSize(FromDIP(70), -1),
wxSP_ARROW_KEYS | wxALIGN_RIGHT, 0, 100, opacity);
@@ -2151,8 +2176,11 @@ public:
m_auto_adjust_filament_selection_checkbox->SetValue(auto_adjust_filament_selection);
preview_box->Add(m_auto_adjust_filament_selection_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
m_preview_limit_resolution_checkbox = new wxCheckBox(preview_page, wxID_ANY, _L("Limit color simulation texture resolution"));
m_preview_limit_resolution_checkbox->SetValue(preview_limit_resolution);
m_preview_limit_resolution_checkbox->SetValue(m_global_settings.preview_limit_resolution);
preview_box->Add(m_preview_limit_resolution_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
m_preview_top_surface_lod_checkbox = new wxCheckBox(preview_page, wxID_ANY, _L("Simulate level of detail for top-surface coloring"));
m_preview_top_surface_lod_checkbox->SetValue(m_global_settings.preview_simulate_top_surface_lod);
preview_box->Add(m_preview_top_surface_lod_checkbox, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
auto *minimum_visibility_offset_row = new wxBoxSizer(wxHORIZONTAL);
m_minimum_visibility_offset_checkbox = new wxCheckBox(preview_page, wxID_ANY, _L("Minimum visibility offset"));
m_minimum_visibility_offset_checkbox->SetValue(minimum_visibility_offset_enabled);
@@ -2872,6 +2900,7 @@ public:
bool force_sequential_filaments() const { return m_force_sequential_filaments_checkbox && m_force_sequential_filaments_checkbox->GetValue(); }
bool auto_adjust_filament_selection() const { return m_auto_adjust_filament_selection_checkbox == nullptr || m_auto_adjust_filament_selection_checkbox->GetValue(); }
bool preview_limit_resolution() const { return m_preview_limit_resolution_checkbox == nullptr || m_preview_limit_resolution_checkbox->GetValue(); }
bool preview_simulate_top_surface_lod() const { return m_preview_top_surface_lod_checkbox != nullptr && m_preview_top_surface_lod_checkbox->GetValue(); }
bool reduce_outer_surface_texture() const { return false; }
bool seam_hiding() const { return m_seam_hiding_checkbox && m_seam_hiding_checkbox->GetValue(); }
bool nonlinear_offset_adjustment() const { return m_nonlinear_offset_adjustment_checkbox && m_nonlinear_offset_adjustment_checkbox->GetValue(); }
@@ -3131,6 +3160,9 @@ public:
TextureMappingGlobalSettings global_settings() const
{
TextureMappingGlobalSettings settings = m_global_settings;
settings.preview_opacity_pct = preview_opacity_pct();
settings.preview_limit_resolution = preview_limit_resolution();
settings.preview_simulate_top_surface_lod = preview_simulate_top_surface_lod();
settings.enabled = m_prime_tower_mapping_enabled_checkbox != nullptr && m_prime_tower_mapping_enabled_checkbox->GetValue();
settings.preserve_aspect_ratio =
m_prime_tower_preserve_aspect_ratio_checkbox != nullptr && m_prime_tower_preserve_aspect_ratio_checkbox->GetValue();
@@ -3612,6 +3644,7 @@ private:
wxCheckBox *m_force_sequential_filaments_checkbox {nullptr};
wxCheckBox *m_auto_adjust_filament_selection_checkbox {nullptr};
wxCheckBox *m_preview_limit_resolution_checkbox {nullptr};
wxCheckBox *m_preview_top_surface_lod_checkbox {nullptr};
wxCheckBox *m_reduce_outer_surface_texture_checkbox {nullptr};
wxCheckBox *m_seam_hiding_checkbox {nullptr};
wxCheckBox *m_nonlinear_offset_adjustment_checkbox {nullptr};
@@ -5633,6 +5666,15 @@ Sidebar::Sidebar(Plater *parent)
opt->value = serialized;
else
bundle->project_config.set_key_value("texture_mapping_definitions", new ConfigOptionString(serialized));
const std::string global_serialized = bundle->texture_mapping_global_settings.serialize();
if (ConfigOptionString *opt = print_cfg->option<ConfigOptionString>("texture_mapping_global_settings"))
opt->value = global_serialized;
else
print_cfg->set_key_value("texture_mapping_global_settings", new ConfigOptionString(global_serialized));
if (ConfigOptionString *opt = bundle->project_config.option<ConfigOptionString>("texture_mapping_global_settings"))
opt->value = global_serialized;
else
bundle->project_config.set_key_value("texture_mapping_global_settings", new ConfigOptionString(global_serialized));
if (auto *print_tab = wxGetApp().get_tab(Preset::TYPE_PRINT))
print_tab->update_dirty();
if (wxGetApp().plater() != nullptr)
@@ -5654,6 +5696,7 @@ Sidebar::Sidebar(Plater *parent)
bundle->project_config.opt_string("texture_mapping_definitions") :
std::string();
bundle->texture_mapping_zones.load_entries(serialized, colors);
bundle->texture_mapping_global_settings.load(bundle->project_config.opt_string("texture_mapping_global_settings"));
bundle->texture_mapping_zones.add_zone(colors.size(),
colors,
colors.size() < 2 ? int(TextureMappingZone::LinearGradient) : int(TextureMappingZone::ImageTexture));
@@ -7549,13 +7592,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
auto is_preview_only_texture_row_change = [](const TextureMappingZone &before, const TextureMappingZone &after) {
TextureMappingZone before_gcode = before;
TextureMappingZone after_gcode = after;
before_gcode.preview_opacity_pct = TextureMappingZone::DefaultPreviewOpacityPct;
before_gcode.preview_simulate_colors = TextureMappingZone::DefaultPreviewSimulateColors;
before_gcode.preview_limit_resolution = TextureMappingZone::DefaultPreviewLimitResolution;
before_gcode.show_linear_gradient_direction_arrow = false;
after_gcode.preview_opacity_pct = TextureMappingZone::DefaultPreviewOpacityPct;
after_gcode.preview_simulate_colors = TextureMappingZone::DefaultPreviewSimulateColors;
after_gcode.preview_limit_resolution = TextureMappingZone::DefaultPreviewLimitResolution;
after_gcode.show_linear_gradient_direction_arrow = false;
return before != after && before_gcode == after_gcode;
};
@@ -7634,6 +7671,19 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
notify_change(affects_scene);
};
auto persist_preview_simulate_colors = [this, bundle, set_config_string, notify_change, refresh_texture_mapping_preview](bool value) {
if (bundle == nullptr)
return;
const bool changed = bundle->texture_mapping_global_settings.preview_simulate_colors != value;
bundle->texture_mapping_global_settings.preview_simulate_colors = value;
set_config_string("texture_mapping_global_settings", bundle->texture_mapping_global_settings.serialize());
if (changed) {
notify_change(false);
refresh_texture_mapping_preview();
CallAfter([this]() { update_texture_mapping_panel(false); });
}
};
for (const size_t zone_index : visible_zone_indices) {
if (zone_index >= zones.size())
continue;
@@ -7994,7 +8044,10 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
editor_sizer->Add(contrast_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
::CheckBox *preview_colors_chk = nullptr;
auto *preview_colors_row = make_sidebar_checkbox_row(editor, preview_colors_chk, _L("Preview Result Colors"), entry.preview_simulate_colors);
auto *preview_colors_row = make_sidebar_checkbox_row(editor,
preview_colors_chk,
_L("Preview Result Colors"),
bundle->texture_mapping_global_settings.preview_simulate_colors);
editor_sizer->Add(preview_colors_row, 0, wxEXPAND | wxLEFT | wxRIGHT | wxTOP, gap);
auto *button_row = new wxBoxSizer(wxHORIZONTAL);
@@ -8008,7 +8061,7 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
auto apply_controls = [zone_index, mgr_ptr, num_physical, filament_checks, surface_choice,
linear_gradient_stops_bar, linear_gradient_mode_choice, linear_gradient_radius_spin,
linear_gradient_radius_percent_chk,
surface_pattern_from_selection, linear_gradient_mode_from_selection, mode_choice, preview_colors_chk,
surface_pattern_from_selection, linear_gradient_mode_from_selection, mode_choice,
show_direction_arrow_chk, contrast_spin, apply_zone]() {
if (mgr_ptr == nullptr)
return;
@@ -8058,8 +8111,6 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.filament_color_mode = mode_choice != nullptr ?
texture_mapping_color_mode_from_selection(mode_choice->GetSelection(), updated.filament_color_mode) :
updated.filament_color_mode;
updated.preview_simulate_colors =
updated.is_image_texture() && preview_colors_chk != nullptr && preview_colors_chk->GetValue();
updated.show_linear_gradient_direction_arrow = show_direction_arrow_chk != nullptr && show_direction_arrow_chk->GetValue();
updated.contrast_pct = contrast_spin != nullptr ? std::clamp(float(contrast_spin->GetValue()), 25.f, 300.f) : updated.contrast_pct;
updated.high_resolution_sampling = true;
@@ -8174,7 +8225,6 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
linear_gradient_mode_from_selection,
mode_choice,
surface_choice,
preview_colors_chk,
contrast_spin,
set_sidebar_checkbox_value,
surface_pattern_from_selection,
@@ -8235,8 +8285,6 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.filament_color_mode = mode_choice != nullptr ?
texture_mapping_color_mode_from_selection(mode_choice->GetSelection(), updated.filament_color_mode) :
updated.filament_color_mode;
updated.preview_simulate_colors =
updated.is_image_texture() && preview_colors_chk != nullptr && preview_colors_chk->GetValue();
updated.contrast_pct = contrast_spin != nullptr ? std::clamp(float(contrast_spin->GetValue()), 25.f, 300.f) : updated.contrast_pct;
updated.high_resolution_sampling = true;
@@ -8264,8 +8312,9 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
apply_zone(std::move(updated));
};
mode_choice->Bind(wxEVT_COMBOBOX, on_mode_choice);
preview_colors_chk->Bind(wxEVT_TOGGLEBUTTON, [apply_controls](wxCommandEvent &evt) {
apply_controls();
preview_colors_chk->Bind(wxEVT_TOGGLEBUTTON, [preview_colors_chk, persist_preview_simulate_colors](wxCommandEvent &evt) {
if (preview_colors_chk != nullptr)
persist_preview_simulate_colors(preview_colors_chk->GetValue());
evt.Skip();
});
if (show_direction_arrow_chk != nullptr)
@@ -8621,10 +8670,8 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
transmission_distances,
updated.transmission_distance_calibration_mode,
updated.tone_gamma,
updated.preview_opacity_pct,
updated.force_sequential_filaments,
updated.auto_adjust_filament_selection,
updated.preview_limit_resolution,
updated.reduce_outer_surface_texture,
updated.seam_hiding,
updated.nonlinear_offset_adjustment,
@@ -8682,7 +8729,6 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.texture_mapping_mode = dlg.texture_mapping_mode();
updated.tone_gamma = dlg.tone_gamma();
updated.transmission_distance_calibration_mode = dlg.transmission_distance_calibration_mode();
updated.preview_opacity_pct = dlg.preview_opacity_pct();
updated.force_sequential_filaments = dlg.force_sequential_filaments();
if (updated.force_sequential_filaments && ids.size() >= 2) {
updated.component_ids = encode_texture_mapping_component_ids(ids);
@@ -8690,7 +8736,6 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.component_b = ids[1];
}
updated.auto_adjust_filament_selection = dlg.auto_adjust_filament_selection();
updated.preview_limit_resolution = dlg.preview_limit_resolution();
updated.reduce_outer_surface_texture = dlg.reduce_outer_surface_texture();
updated.seam_hiding = dlg.seam_hiding();
updated.nonlinear_offset_adjustment = dlg.nonlinear_offset_adjustment();
@@ -8752,8 +8797,18 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
updated.generic_solver_mode = dlg.generic_solver_mode();
updated.generic_solver_mix_model = dlg.generic_solver_mix_model();
const TextureMappingGlobalSettings dlg_global_settings = dlg.global_settings();
auto prime_tower_only_settings = [](TextureMappingGlobalSettings settings) {
settings.preview_opacity_pct = TextureMappingZone::DefaultPreviewOpacityPct;
settings.preview_simulate_colors = TextureMappingZone::DefaultPreviewSimulateColors;
settings.preview_limit_resolution = TextureMappingZone::DefaultPreviewLimitResolution;
settings.preview_simulate_top_surface_lod = TextureMappingZone::DefaultPreviewSimulateTopSurfaceLod;
return settings.serialize();
};
const bool global_settings_changed =
dlg_global_settings.serialize() != bundle->texture_mapping_global_settings.serialize();
const bool prime_tower_settings_changed =
prime_tower_only_settings(dlg_global_settings) !=
prime_tower_only_settings(bundle->texture_mapping_global_settings);
if (global_settings_changed)
bundle->texture_mapping_global_settings = dlg_global_settings;
Model &model = wxGetApp().model();
@@ -8792,10 +8847,10 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager)
std::abs(updated.filament_transmission_distances_mm.back()) <= 1e-6f)
updated.filament_transmission_distances_mm.pop_back();
const bool affects_scene = apply_zone(std::move(updated));
const bool prime_tower_preview_changed = global_settings_changed || prime_tower_images_changed;
if (prime_tower_preview_changed)
notify_change(true);
if (affects_scene || prime_tower_preview_changed)
const bool prime_tower_preview_changed = prime_tower_settings_changed || prime_tower_images_changed;
if (global_settings_changed || prime_tower_images_changed)
notify_change(prime_tower_preview_changed);
if (affects_scene || global_settings_changed || prime_tower_images_changed)
refresh_texture_mapping_preview();
CallAfter([this]() { update_texture_mapping_panel(false); });
};