From 14e61db633406e201124b2990ddbbafa4ef396da Mon Sep 17 00:00:00 2001 From: sentientstardust Date: Fri, 22 May 2026 07:40:41 +0100 Subject: [PATCH] Render preview for 2D gradients in shader. Misc bugfixes. - Make sure prime tower preview is updated when settings change - Manage color data dialog now closes with escape key - Textured objects using halftone dithering now show their original texture while preview is being generated - Show checkboard error overlay in world space (when zone has invalid config). --- .../110/painted_surface_gradient_preview.fs | 285 ++++++++++++ .../110/painted_surface_gradient_preview.vs | 46 ++ .../shaders/110/painted_texture_preview.fs | 15 +- .../shaders/110/painted_texture_preview.vs | 2 + .../110/painted_vertex_color_preview.fs | 15 +- .../110/painted_vertex_color_preview.vs | 2 + .../140/painted_surface_gradient_preview.fs | 287 ++++++++++++ .../140/painted_surface_gradient_preview.vs | 46 ++ .../shaders/140/painted_texture_preview.fs | 15 +- .../shaders/140/painted_texture_preview.vs | 2 + .../140/painted_vertex_color_preview.fs | 15 +- .../140/painted_vertex_color_preview.vs | 2 + src/slic3r/GUI/3DScene.cpp | 26 +- src/slic3r/GUI/GLShadersManager.cpp | 1 + .../GUI/Gizmos/GLGizmoMmuSegmentation.cpp | 14 +- src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp | 26 +- src/slic3r/GUI/MMUPaintedTexturePreview.cpp | 423 ++++++------------ src/slic3r/GUI/MMUPaintedTexturePreview.hpp | 6 +- src/slic3r/GUI/Plater.cpp | 34 +- 19 files changed, 931 insertions(+), 331 deletions(-) create mode 100644 resources/shaders/110/painted_surface_gradient_preview.fs create mode 100644 resources/shaders/110/painted_surface_gradient_preview.vs create mode 100644 resources/shaders/140/painted_surface_gradient_preview.fs create mode 100644 resources/shaders/140/painted_surface_gradient_preview.vs diff --git a/resources/shaders/110/painted_surface_gradient_preview.fs b/resources/shaders/110/painted_surface_gradient_preview.fs new file mode 100644 index 00000000000..e8a0c95d4e1 --- /dev/null +++ b/resources/shaders/110/painted_surface_gradient_preview.fs @@ -0,0 +1,285 @@ +#version 110 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); +const float INVALID_TEXTURE_CHECKER_SCALE = 0.2; +const int MAX_GRADIENT_COMPONENTS = 10; +const float EPSILON = 0.000001; + +struct PrintVolumeDetection +{ + int type; + vec4 xy_data; + vec2 z_data; +}; + +uniform vec4 uniform_color; +uniform float texture_preview_mix; +uniform bool invalid_texture_mapping; +uniform PrintVolumeDetection print_volume; + +uniform int gradient_component_count; +uniform vec3 gradient_component_colors[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_distances_mm[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_angles_deg[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_strength_factors[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_minimum_offset_factors[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_max_component_distance_mm; +uniform float gradient_max_width_delta_limit_mm; +uniform int gradient_angle_mode; +uniform bool gradient_rotation_enabled; +uniform float gradient_rotations; +uniform float gradient_repeats; +uniform bool gradient_reverse_repeats; +uniform bool gradient_clockwise; +uniform int gradient_fade_mode; +uniform vec3 gradient_center; +uniform float gradient_z_min; +uniform float gradient_z_max; + +varying vec2 intensity; +varying vec3 clipping_planes_dots; +varying vec4 world_pos; +varying vec3 world_normal; + +float normalize_angle(float angle) +{ + float out_angle = mod(angle, 360.0); + if (out_angle < 0.0) + out_angle += 360.0; + return out_angle; +} + +float angular_distance_deg(float a, float b) +{ + float d = abs(normalize_angle(a) - normalize_angle(b)); + return min(d, 360.0 - d); +} + +float angular_distance_cw(float from_deg, float to_deg) +{ + float d = normalize_angle(to_deg) - normalize_angle(from_deg); + if (d < 0.0) + d += 360.0; + return d; +} + +float repeated_rotation_progress(float progress01, float repeats, bool reverse_repeats) +{ + float p = clamp(progress01, 0.0, 1.0); + float r = max(1.0, repeats); + if (r <= 1.0 + EPSILON) + return p; + + float repeated_pos = p * r; + float segment_idx = floor(repeated_pos); + float local = repeated_pos - segment_idx; + + if (p >= 1.0 - EPSILON) { + segment_idx = max(0.0, ceil(r) - 1.0); + local = 1.0; + } + + if (reverse_repeats && mod(segment_idx, 2.0) >= 1.0) + local = 1.0 - local; + return clamp(local, 0.0, 1.0); +} + +float offset_fade_factor(int fade_mode, float progress01) +{ + float p = clamp(progress01, 0.0, 1.0); + if (fade_mode == 1) + return p; + if (fade_mode == 2) + return 1.0 - p; + if (fade_mode == 3) + return 1.0 - abs(2.0 * p - 1.0); + if (fade_mode == 4) + return abs(2.0 * p - 1.0); + if (fade_mode == 5) + return 2.0 * p - 1.0; + return 1.0; +} + +float component_angular_influence(int component_idx, float theta_deg) +{ + int count = min(gradient_component_count, MAX_GRADIENT_COMPONENTS); + if (count <= 0) + return 0.0; + if (count == 1) + return 1.0; + + float self_angle = normalize_angle(gradient_angles_deg[component_idx]); + float prev_angle = self_angle; + float next_angle = self_angle; + float prev_to_self_deg = 360.0; + float self_to_next_deg = 360.0; + + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + if (i >= count || i == component_idx) + continue; + float other_angle = normalize_angle(gradient_angles_deg[i]); + float prev_distance = angular_distance_cw(other_angle, self_angle); + float next_distance = angular_distance_cw(self_angle, other_angle); + if (prev_distance < prev_to_self_deg) { + prev_to_self_deg = prev_distance; + prev_angle = other_angle; + } + if (next_distance < self_to_next_deg) { + self_to_next_deg = next_distance; + next_angle = other_angle; + } + } + + if (prev_to_self_deg <= 0.001 || self_to_next_deg <= 0.001) { + float total_weight = 0.0; + float active_weight = 0.0; + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + if (i >= count) + continue; + float weight = max(0.0, 1.0 - angular_distance_deg(theta_deg, gradient_angles_deg[i]) / 180.0); + total_weight += weight; + if (i == component_idx) + active_weight += weight; + } + if (total_weight <= EPSILON) + return 0.0; + return clamp(active_weight / total_weight, 0.0, 1.0); + } + + float theta_norm = normalize_angle(theta_deg); + float prev_to_theta_deg = angular_distance_cw(prev_angle, theta_norm); + if (prev_to_theta_deg <= prev_to_self_deg + 0.0001) + return clamp(prev_to_theta_deg / prev_to_self_deg, 0.0, 1.0); + + float self_to_theta_deg = angular_distance_cw(self_angle, theta_norm); + if (self_to_theta_deg <= self_to_next_deg + 0.0001) + return clamp(1.0 - self_to_theta_deg / self_to_next_deg, 0.0, 1.0); + + return 0.0; +} + +float variable_width_delta(float inset_strength, float max_width_delta_limit_mm, float minimum_offset_factor, float strength_factor) +{ + if (max_width_delta_limit_mm <= 0.0) + return 0.0; + + float desired_width_factor = 1.0 - clamp(inset_strength, 0.0, 1.0); + float min_width_factor = clamp(minimum_offset_factor, 0.0, 1.0); + float adjusted_width_factor = min_width_factor + desired_width_factor * clamp(strength_factor, 0.0, 1.0) * (1.0 - min_width_factor); + return clamp(max_width_delta_limit_mm * (1.0 - adjusted_width_factor), 0.0, max_width_delta_limit_mm); +} + +vec3 surface_gradient_color() +{ + int count = min(gradient_component_count, MAX_GRADIENT_COMPONENTS); + if (count <= 0) + return uniform_color.rgb; + + float z_span = gradient_z_max - gradient_z_min; + float z_progress = z_span > EPSILON ? clamp((world_pos.z - gradient_z_min) / z_span, 0.0, 1.0) : 0.0; + + float rotation_deg = 0.0; + if (gradient_rotation_enabled) { + float repeated = repeated_rotation_progress(z_progress, max(1.0, gradient_repeats), gradient_reverse_repeats); + float direction = gradient_clockwise ? -1.0 : 1.0; + rotation_deg = direction * 360.0 * gradient_rotations * repeated; + } + + vec2 direction_vec = vec2(0.0); + if (gradient_angle_mode == 1) + direction_vec = world_normal.xy; + if (dot(direction_vec, direction_vec) <= EPSILON) { + vec3 radial = world_pos.xyz - gradient_center; + direction_vec = radial.xy; + } + if (dot(direction_vec, direction_vec) <= EPSILON) + direction_vec = vec2(1.0, 0.0); + + float theta_deg = normalize_angle(degrees(atan(direction_vec.y, direction_vec.x)) - rotation_deg); + float fade_factor = abs(offset_fade_factor(gradient_fade_mode, z_progress)); + float influences[MAX_GRADIENT_COMPONENTS]; + float edge_reaches[MAX_GRADIENT_COMPONENTS]; + float min_reach = 1000000.0; + float max_reach = -1000000.0; + + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + influences[i] = 0.0; + edge_reaches[i] = 0.0; + if (i < count) + influences[i] = component_angular_influence(i, theta_deg); + } + + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + if (i >= count) + continue; + float raw_inset_mm = 0.0; + for (int j = 0; j < MAX_GRADIENT_COMPONENTS; ++j) { + if (j >= count || i == j) + continue; + raw_inset_mm += gradient_distances_mm[j] * influences[j]; + } + float inset_strength = clamp(raw_inset_mm / max(gradient_max_component_distance_mm, EPSILON), 0.0, 1.0); + float width_delta_mm = variable_width_delta(inset_strength * fade_factor, + gradient_max_width_delta_limit_mm, + gradient_minimum_offset_factors[i], + gradient_strength_factors[i]); + edge_reaches[i] = clamp(gradient_max_width_delta_limit_mm - width_delta_mm, 0.0, gradient_max_width_delta_limit_mm); + min_reach = min(min_reach, edge_reaches[i]); + max_reach = max(max_reach, edge_reaches[i]); + } + + vec3 mixed_color = vec3(0.0); + float total_weight = 0.0; + float reach_span = max_reach - min_reach; + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + if (i >= count) + continue; + float weight = reach_span > EPSILON ? clamp((edge_reaches[i] - min_reach) / reach_span, 0.0, 1.0) : 1.0; + mixed_color += gradient_component_colors[i] * weight; + total_weight += weight; + } + if (total_weight <= EPSILON) + return gradient_component_colors[0]; + return clamp(mixed_color / total_weight, 0.0, 1.0); +} + +float invalid_texture_mapping_checker() +{ + vec3 normal_axes = abs(world_normal); + vec2 checker_pos = world_pos.xy; + if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z) + checker_pos = world_pos.yz; + else if (normal_axes.y > normal_axes.z) + checker_pos = world_pos.xz; + return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0); +} + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + vec4 color = uniform_color; + float mix_factor = clamp(texture_preview_mix, 0.0, 1.0); + color.rgb = mix(color.rgb, surface_gradient_color(), mix_factor); + if (invalid_texture_mapping) { + float checker = invalid_texture_mapping_checker(); + vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker); + color.rgb = mix(color.rgb, checker_color, 0.62); + } + + vec3 pv_check_min = ZERO; + vec3 pv_check_max = ZERO; + if (print_volume.type == 0) { + pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x); + pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y); + } + else if (print_volume.type == 1) { + float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy); + pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x); + pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); + } + + color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb; + gl_FragColor = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); +} diff --git a/resources/shaders/110/painted_surface_gradient_preview.vs b/resources/shaders/110/painted_surface_gradient_preview.vs new file mode 100644 index 00000000000..1ee4e53be49 --- /dev/null +++ b/resources/shaders/110/painted_surface_gradient_preview.vs @@ -0,0 +1,46 @@ +#version 110 + +#define INTENSITY_CORRECTION 0.6 + +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; +uniform vec2 z_range; +uniform vec4 clipping_plane; + +attribute vec3 v_position; +attribute vec3 v_normal; + +varying vec2 intensity; +varying vec3 clipping_planes_dots; +varying vec4 world_pos; +varying vec3 world_normal; + +void main() +{ + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + world_pos = volume_world_matrix * vec4(v_position, 1.0); + world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0))); + gl_Position = projection_matrix * position; + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); +} diff --git a/resources/shaders/110/painted_texture_preview.fs b/resources/shaders/110/painted_texture_preview.fs index d41fbf5d513..d117db29602 100644 --- a/resources/shaders/110/painted_texture_preview.fs +++ b/resources/shaders/110/painted_texture_preview.fs @@ -2,6 +2,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; struct PrintVolumeDetection { @@ -19,6 +20,7 @@ uniform PrintVolumeDetection print_volume; varying vec2 intensity; varying vec3 clipping_planes_dots; varying vec4 world_pos; +varying vec3 world_normal; varying vec2 tex_coord; float texture_preview_coord(float uv) @@ -34,6 +36,17 @@ vec2 texture_preview_coord(vec2 uv) return vec2(texture_preview_coord(uv.x), texture_preview_coord(uv.y)); } +float invalid_texture_mapping_checker() +{ + vec3 normal_axes = abs(world_normal); + vec2 checker_pos = world_pos.xy; + if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z) + checker_pos = world_pos.yz; + else if (normal_axes.y > normal_axes.z) + checker_pos = world_pos.xz; + return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0); +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -44,7 +57,7 @@ void main() float mix_factor = clamp(texture_preview_mix, 0.0, 1.0); color.rgb = mix(color.rgb, texture_color.rgb, mix_factor); if (invalid_texture_mapping) { - float checker = mod(floor(tex_coord.x * 24.0) + floor(tex_coord.y * 24.0), 2.0); + float checker = invalid_texture_mapping_checker(); vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker); color.rgb = mix(color.rgb, checker_color, 0.62); } diff --git a/resources/shaders/110/painted_texture_preview.vs b/resources/shaders/110/painted_texture_preview.vs index b56566dac6f..908e102ffa9 100644 --- a/resources/shaders/110/painted_texture_preview.vs +++ b/resources/shaders/110/painted_texture_preview.vs @@ -26,6 +26,7 @@ attribute vec2 v_tex_coord; varying vec2 intensity; varying vec3 clipping_planes_dots; varying vec4 world_pos; +varying vec3 world_normal; varying vec2 tex_coord; void main() @@ -41,6 +42,7 @@ void main() intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; world_pos = volume_world_matrix * vec4(v_position, 1.0); + world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0))); tex_coord = v_tex_coord; gl_Position = projection_matrix * position; clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); diff --git a/resources/shaders/110/painted_vertex_color_preview.fs b/resources/shaders/110/painted_vertex_color_preview.fs index 399f0b8b7d8..88b585f118d 100644 --- a/resources/shaders/110/painted_vertex_color_preview.fs +++ b/resources/shaders/110/painted_vertex_color_preview.fs @@ -1,6 +1,7 @@ #version 110 const vec3 ZERO = vec3(0.0, 0.0, 0.0); +const float INVALID_TEXTURE_CHECKER_SCALE = 0.2; struct PrintVolumeDetection { @@ -17,8 +18,20 @@ uniform PrintVolumeDetection print_volume; varying vec2 intensity; varying vec3 clipping_planes_dots; varying vec4 world_pos; +varying vec3 world_normal; varying vec4 vertex_color; +float invalid_texture_mapping_checker() +{ + vec3 normal_axes = abs(world_normal); + vec2 checker_pos = world_pos.xy; + if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z) + checker_pos = world_pos.yz; + else if (normal_axes.y > normal_axes.z) + checker_pos = world_pos.xz; + return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0); +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -28,7 +41,7 @@ void main() float mix_factor = clamp(texture_preview_mix, 0.0, 1.0); color.rgb = mix(color.rgb, vertex_color.rgb, mix_factor); if (invalid_texture_mapping) { - float checker = mod(floor(world_pos.x * 4.0) + floor(world_pos.y * 4.0) + floor(world_pos.z * 4.0), 2.0); + float checker = invalid_texture_mapping_checker(); vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker); color.rgb = mix(color.rgb, checker_color, 0.62); } diff --git a/resources/shaders/110/painted_vertex_color_preview.vs b/resources/shaders/110/painted_vertex_color_preview.vs index 822daad707c..b7c3f8a4781 100644 --- a/resources/shaders/110/painted_vertex_color_preview.vs +++ b/resources/shaders/110/painted_vertex_color_preview.vs @@ -26,6 +26,7 @@ attribute vec4 v_color; varying vec2 intensity; varying vec3 clipping_planes_dots; varying vec4 world_pos; +varying vec3 world_normal; varying vec4 vertex_color; void main() @@ -41,6 +42,7 @@ void main() intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; world_pos = volume_world_matrix * vec4(v_position, 1.0); + world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0))); vertex_color = v_color; gl_Position = projection_matrix * position; clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); diff --git a/resources/shaders/140/painted_surface_gradient_preview.fs b/resources/shaders/140/painted_surface_gradient_preview.fs new file mode 100644 index 00000000000..1b99abacba0 --- /dev/null +++ b/resources/shaders/140/painted_surface_gradient_preview.fs @@ -0,0 +1,287 @@ +#version 140 + +const vec3 ZERO = vec3(0.0, 0.0, 0.0); +const float INVALID_TEXTURE_CHECKER_SCALE = 0.2; +const int MAX_GRADIENT_COMPONENTS = 10; +const float EPSILON = 0.000001; + +struct PrintVolumeDetection +{ + int type; + vec4 xy_data; + vec2 z_data; +}; + +uniform vec4 uniform_color; +uniform float texture_preview_mix; +uniform bool invalid_texture_mapping; +uniform PrintVolumeDetection print_volume; + +uniform int gradient_component_count; +uniform vec3 gradient_component_colors[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_distances_mm[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_angles_deg[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_strength_factors[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_minimum_offset_factors[MAX_GRADIENT_COMPONENTS]; +uniform float gradient_max_component_distance_mm; +uniform float gradient_max_width_delta_limit_mm; +uniform int gradient_angle_mode; +uniform bool gradient_rotation_enabled; +uniform float gradient_rotations; +uniform float gradient_repeats; +uniform bool gradient_reverse_repeats; +uniform bool gradient_clockwise; +uniform int gradient_fade_mode; +uniform vec3 gradient_center; +uniform float gradient_z_min; +uniform float gradient_z_max; + +in vec2 intensity; +in vec3 clipping_planes_dots; +in vec4 world_pos; +in vec3 world_normal; + +out vec4 out_color; + +float normalize_angle(float angle) +{ + float out_angle = mod(angle, 360.0); + if (out_angle < 0.0) + out_angle += 360.0; + return out_angle; +} + +float angular_distance_deg(float a, float b) +{ + float d = abs(normalize_angle(a) - normalize_angle(b)); + return min(d, 360.0 - d); +} + +float angular_distance_cw(float from_deg, float to_deg) +{ + float d = normalize_angle(to_deg) - normalize_angle(from_deg); + if (d < 0.0) + d += 360.0; + return d; +} + +float repeated_rotation_progress(float progress01, float repeats, bool reverse_repeats) +{ + float p = clamp(progress01, 0.0, 1.0); + float r = max(1.0, repeats); + if (r <= 1.0 + EPSILON) + return p; + + float repeated_pos = p * r; + float segment_idx = floor(repeated_pos); + float local = repeated_pos - segment_idx; + + if (p >= 1.0 - EPSILON) { + segment_idx = max(0.0, ceil(r) - 1.0); + local = 1.0; + } + + if (reverse_repeats && mod(segment_idx, 2.0) >= 1.0) + local = 1.0 - local; + return clamp(local, 0.0, 1.0); +} + +float offset_fade_factor(int fade_mode, float progress01) +{ + float p = clamp(progress01, 0.0, 1.0); + if (fade_mode == 1) + return p; + if (fade_mode == 2) + return 1.0 - p; + if (fade_mode == 3) + return 1.0 - abs(2.0 * p - 1.0); + if (fade_mode == 4) + return abs(2.0 * p - 1.0); + if (fade_mode == 5) + return 2.0 * p - 1.0; + return 1.0; +} + +float component_angular_influence(int component_idx, float theta_deg) +{ + int count = min(gradient_component_count, MAX_GRADIENT_COMPONENTS); + if (count <= 0) + return 0.0; + if (count == 1) + return 1.0; + + float self_angle = normalize_angle(gradient_angles_deg[component_idx]); + float prev_angle = self_angle; + float next_angle = self_angle; + float prev_to_self_deg = 360.0; + float self_to_next_deg = 360.0; + + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + if (i >= count || i == component_idx) + continue; + float other_angle = normalize_angle(gradient_angles_deg[i]); + float prev_distance = angular_distance_cw(other_angle, self_angle); + float next_distance = angular_distance_cw(self_angle, other_angle); + if (prev_distance < prev_to_self_deg) { + prev_to_self_deg = prev_distance; + prev_angle = other_angle; + } + if (next_distance < self_to_next_deg) { + self_to_next_deg = next_distance; + next_angle = other_angle; + } + } + + if (prev_to_self_deg <= 0.001 || self_to_next_deg <= 0.001) { + float total_weight = 0.0; + float active_weight = 0.0; + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + if (i >= count) + continue; + float weight = max(0.0, 1.0 - angular_distance_deg(theta_deg, gradient_angles_deg[i]) / 180.0); + total_weight += weight; + if (i == component_idx) + active_weight += weight; + } + if (total_weight <= EPSILON) + return 0.0; + return clamp(active_weight / total_weight, 0.0, 1.0); + } + + float theta_norm = normalize_angle(theta_deg); + float prev_to_theta_deg = angular_distance_cw(prev_angle, theta_norm); + if (prev_to_theta_deg <= prev_to_self_deg + 0.0001) + return clamp(prev_to_theta_deg / prev_to_self_deg, 0.0, 1.0); + + float self_to_theta_deg = angular_distance_cw(self_angle, theta_norm); + if (self_to_theta_deg <= self_to_next_deg + 0.0001) + return clamp(1.0 - self_to_theta_deg / self_to_next_deg, 0.0, 1.0); + + return 0.0; +} + +float variable_width_delta(float inset_strength, float max_width_delta_limit_mm, float minimum_offset_factor, float strength_factor) +{ + if (max_width_delta_limit_mm <= 0.0) + return 0.0; + + float desired_width_factor = 1.0 - clamp(inset_strength, 0.0, 1.0); + float min_width_factor = clamp(minimum_offset_factor, 0.0, 1.0); + float adjusted_width_factor = min_width_factor + desired_width_factor * clamp(strength_factor, 0.0, 1.0) * (1.0 - min_width_factor); + return clamp(max_width_delta_limit_mm * (1.0 - adjusted_width_factor), 0.0, max_width_delta_limit_mm); +} + +vec3 surface_gradient_color() +{ + int count = min(gradient_component_count, MAX_GRADIENT_COMPONENTS); + if (count <= 0) + return uniform_color.rgb; + + float z_span = gradient_z_max - gradient_z_min; + float z_progress = z_span > EPSILON ? clamp((world_pos.z - gradient_z_min) / z_span, 0.0, 1.0) : 0.0; + + float rotation_deg = 0.0; + if (gradient_rotation_enabled) { + float repeated = repeated_rotation_progress(z_progress, max(1.0, gradient_repeats), gradient_reverse_repeats); + float direction = gradient_clockwise ? -1.0 : 1.0; + rotation_deg = direction * 360.0 * gradient_rotations * repeated; + } + + vec2 direction_vec = vec2(0.0); + if (gradient_angle_mode == 1) + direction_vec = world_normal.xy; + if (dot(direction_vec, direction_vec) <= EPSILON) { + vec3 radial = world_pos.xyz - gradient_center; + direction_vec = radial.xy; + } + if (dot(direction_vec, direction_vec) <= EPSILON) + direction_vec = vec2(1.0, 0.0); + + float theta_deg = normalize_angle(degrees(atan(direction_vec.y, direction_vec.x)) - rotation_deg); + float fade_factor = abs(offset_fade_factor(gradient_fade_mode, z_progress)); + float influences[MAX_GRADIENT_COMPONENTS]; + float edge_reaches[MAX_GRADIENT_COMPONENTS]; + float min_reach = 1000000.0; + float max_reach = -1000000.0; + + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + influences[i] = 0.0; + edge_reaches[i] = 0.0; + if (i < count) + influences[i] = component_angular_influence(i, theta_deg); + } + + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + if (i >= count) + continue; + float raw_inset_mm = 0.0; + for (int j = 0; j < MAX_GRADIENT_COMPONENTS; ++j) { + if (j >= count || i == j) + continue; + raw_inset_mm += gradient_distances_mm[j] * influences[j]; + } + float inset_strength = clamp(raw_inset_mm / max(gradient_max_component_distance_mm, EPSILON), 0.0, 1.0); + float width_delta_mm = variable_width_delta(inset_strength * fade_factor, + gradient_max_width_delta_limit_mm, + gradient_minimum_offset_factors[i], + gradient_strength_factors[i]); + edge_reaches[i] = clamp(gradient_max_width_delta_limit_mm - width_delta_mm, 0.0, gradient_max_width_delta_limit_mm); + min_reach = min(min_reach, edge_reaches[i]); + max_reach = max(max_reach, edge_reaches[i]); + } + + vec3 mixed_color = vec3(0.0); + float total_weight = 0.0; + float reach_span = max_reach - min_reach; + for (int i = 0; i < MAX_GRADIENT_COMPONENTS; ++i) { + if (i >= count) + continue; + float weight = reach_span > EPSILON ? clamp((edge_reaches[i] - min_reach) / reach_span, 0.0, 1.0) : 1.0; + mixed_color += gradient_component_colors[i] * weight; + total_weight += weight; + } + if (total_weight <= EPSILON) + return gradient_component_colors[0]; + return clamp(mixed_color / total_weight, 0.0, 1.0); +} + +float invalid_texture_mapping_checker() +{ + vec3 normal_axes = abs(world_normal); + vec2 checker_pos = world_pos.xy; + if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z) + checker_pos = world_pos.yz; + else if (normal_axes.y > normal_axes.z) + checker_pos = world_pos.xz; + return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0); +} + +void main() +{ + if (any(lessThan(clipping_planes_dots, ZERO))) + discard; + + vec4 color = uniform_color; + float mix_factor = clamp(texture_preview_mix, 0.0, 1.0); + color.rgb = mix(color.rgb, surface_gradient_color(), mix_factor); + if (invalid_texture_mapping) { + float checker = invalid_texture_mapping_checker(); + vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker); + color.rgb = mix(color.rgb, checker_color, 0.62); + } + + vec3 pv_check_min = ZERO; + vec3 pv_check_max = ZERO; + if (print_volume.type == 0) { + pv_check_min = world_pos.xyz - vec3(print_volume.xy_data.x, print_volume.xy_data.y, print_volume.z_data.x); + pv_check_max = world_pos.xyz - vec3(print_volume.xy_data.z, print_volume.xy_data.w, print_volume.z_data.y); + } + else if (print_volume.type == 1) { + float delta_radius = print_volume.xy_data.z - distance(world_pos.xy, print_volume.xy_data.xy); + pv_check_min = vec3(delta_radius, 0.0, world_pos.z - print_volume.z_data.x); + pv_check_max = vec3(0.0, 0.0, world_pos.z - print_volume.z_data.y); + } + + color.rgb = (any(lessThan(pv_check_min, ZERO)) || any(greaterThan(pv_check_max, ZERO))) ? mix(color.rgb, ZERO, 0.3333) : color.rgb; + out_color = vec4(vec3(intensity.y) + color.rgb * intensity.x, color.a); +} diff --git a/resources/shaders/140/painted_surface_gradient_preview.vs b/resources/shaders/140/painted_surface_gradient_preview.vs new file mode 100644 index 00000000000..d9351a7d836 --- /dev/null +++ b/resources/shaders/140/painted_surface_gradient_preview.vs @@ -0,0 +1,46 @@ +#version 140 + +#define INTENSITY_CORRECTION 0.6 + +const vec3 LIGHT_TOP_DIR = vec3(-0.4574957, 0.4574957, 0.7624929); +#define LIGHT_TOP_DIFFUSE (0.8 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SPECULAR (0.125 * INTENSITY_CORRECTION) +#define LIGHT_TOP_SHININESS 20.0 + +const vec3 LIGHT_FRONT_DIR = vec3(0.6985074, 0.1397015, 0.6985074); +#define LIGHT_FRONT_DIFFUSE (0.3 * INTENSITY_CORRECTION) + +#define INTENSITY_AMBIENT 0.3 + +uniform mat4 view_model_matrix; +uniform mat4 projection_matrix; +uniform mat3 view_normal_matrix; +uniform mat4 volume_world_matrix; +uniform vec2 z_range; +uniform vec4 clipping_plane; + +in vec3 v_position; +in vec3 v_normal; + +out vec2 intensity; +out vec3 clipping_planes_dots; +out vec4 world_pos; +out vec3 world_normal; + +void main() +{ + vec3 eye_normal = normalize(view_normal_matrix * v_normal); + + float NdotL = max(dot(eye_normal, LIGHT_TOP_DIR), 0.0); + intensity.x = INTENSITY_AMBIENT + NdotL * LIGHT_TOP_DIFFUSE; + vec4 position = view_model_matrix * vec4(v_position, 1.0); + intensity.y = LIGHT_TOP_SPECULAR * pow(max(dot(-normalize(position.xyz), reflect(-LIGHT_TOP_DIR, eye_normal)), 0.0), LIGHT_TOP_SHININESS); + + NdotL = max(dot(eye_normal, LIGHT_FRONT_DIR), 0.0); + intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; + + world_pos = volume_world_matrix * vec4(v_position, 1.0); + world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0))); + gl_Position = projection_matrix * position; + clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); +} diff --git a/resources/shaders/140/painted_texture_preview.fs b/resources/shaders/140/painted_texture_preview.fs index f19ca32ca7a..f59d43d52aa 100644 --- a/resources/shaders/140/painted_texture_preview.fs +++ b/resources/shaders/140/painted_texture_preview.fs @@ -2,6 +2,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; struct PrintVolumeDetection { @@ -19,6 +20,7 @@ uniform PrintVolumeDetection print_volume; in vec2 intensity; in vec3 clipping_planes_dots; in vec4 world_pos; +in vec3 world_normal; in vec2 tex_coord; out vec4 out_color; @@ -36,6 +38,17 @@ vec2 texture_preview_coord(vec2 uv) return vec2(texture_preview_coord(uv.x), texture_preview_coord(uv.y)); } +float invalid_texture_mapping_checker() +{ + vec3 normal_axes = abs(world_normal); + vec2 checker_pos = world_pos.xy; + if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z) + checker_pos = world_pos.yz; + else if (normal_axes.y > normal_axes.z) + checker_pos = world_pos.xz; + return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0); +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -46,7 +59,7 @@ void main() float mix_factor = clamp(texture_preview_mix, 0.0, 1.0); color.rgb = mix(color.rgb, texture_color.rgb, mix_factor); if (invalid_texture_mapping) { - float checker = mod(floor(tex_coord.x * 24.0) + floor(tex_coord.y * 24.0), 2.0); + float checker = invalid_texture_mapping_checker(); vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker); color.rgb = mix(color.rgb, checker_color, 0.62); } diff --git a/resources/shaders/140/painted_texture_preview.vs b/resources/shaders/140/painted_texture_preview.vs index 894ea84e897..6f2a0dbb553 100644 --- a/resources/shaders/140/painted_texture_preview.vs +++ b/resources/shaders/140/painted_texture_preview.vs @@ -26,6 +26,7 @@ in vec2 v_tex_coord; out vec2 intensity; out vec3 clipping_planes_dots; out vec4 world_pos; +out vec3 world_normal; out vec2 tex_coord; void main() @@ -41,6 +42,7 @@ void main() intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; world_pos = volume_world_matrix * vec4(v_position, 1.0); + world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0))); tex_coord = v_tex_coord; gl_Position = projection_matrix * position; clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); diff --git a/resources/shaders/140/painted_vertex_color_preview.fs b/resources/shaders/140/painted_vertex_color_preview.fs index b4f5684a4dd..37866b2cc8c 100644 --- a/resources/shaders/140/painted_vertex_color_preview.fs +++ b/resources/shaders/140/painted_vertex_color_preview.fs @@ -1,6 +1,7 @@ #version 140 const vec3 ZERO = vec3(0.0, 0.0, 0.0); +const float INVALID_TEXTURE_CHECKER_SCALE = 0.2; struct PrintVolumeDetection { @@ -17,10 +18,22 @@ uniform PrintVolumeDetection print_volume; in vec2 intensity; in vec3 clipping_planes_dots; in vec4 world_pos; +in vec3 world_normal; in vec4 vertex_color; out vec4 out_color; +float invalid_texture_mapping_checker() +{ + vec3 normal_axes = abs(world_normal); + vec2 checker_pos = world_pos.xy; + if (normal_axes.x > normal_axes.y && normal_axes.x > normal_axes.z) + checker_pos = world_pos.yz; + else if (normal_axes.y > normal_axes.z) + checker_pos = world_pos.xz; + return mod(floor(checker_pos.x * INVALID_TEXTURE_CHECKER_SCALE) + floor(checker_pos.y * INVALID_TEXTURE_CHECKER_SCALE), 2.0); +} + void main() { if (any(lessThan(clipping_planes_dots, ZERO))) @@ -30,7 +43,7 @@ void main() float mix_factor = clamp(texture_preview_mix, 0.0, 1.0); color.rgb = mix(color.rgb, vertex_color.rgb, mix_factor); if (invalid_texture_mapping) { - float checker = mod(floor(world_pos.x * 4.0) + floor(world_pos.y * 4.0) + floor(world_pos.z * 4.0), 2.0); + float checker = invalid_texture_mapping_checker(); vec3 checker_color = mix(vec3(0.0), vec3(1.0), checker); color.rgb = mix(color.rgb, checker_color, 0.62); } diff --git a/resources/shaders/140/painted_vertex_color_preview.vs b/resources/shaders/140/painted_vertex_color_preview.vs index 2fb384e8fdf..a0bf29fbe10 100644 --- a/resources/shaders/140/painted_vertex_color_preview.vs +++ b/resources/shaders/140/painted_vertex_color_preview.vs @@ -26,6 +26,7 @@ in vec4 v_color; out vec2 intensity; out vec3 clipping_planes_dots; out vec4 world_pos; +out vec3 world_normal; out vec4 vertex_color; void main() @@ -41,6 +42,7 @@ void main() intensity.x += NdotL * LIGHT_FRONT_DIFFUSE; world_pos = volume_world_matrix * vec4(v_position, 1.0); + world_normal = normalize(vec3(volume_world_matrix * vec4(v_normal, 0.0))); vertex_color = v_color; gl_Position = projection_matrix * position; clipping_planes_dots = vec3(dot(world_pos, clipping_plane), world_pos.z - z_range.x, z_range.y - world_pos.z); diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index 068a4288b34..e6ba4e61b5b 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -776,17 +776,6 @@ void GLVolume::simple_render(GLShaderProgram* shader, const bool has_active_texture_mapping_color_preview_data = has_texture_preview_state && model_volume_has_texture_mapping_color_preview_data(*model_volume); - if (has_texture_preview_state && !use_original_mesh_texture_preview && !has_active_texture_mapping_color_preview_data && has_texture_preview_data) { - build_mmu_texture_preview_models(*model_volume, - triangles_per_type, - state_colors, - base_filament_id, - num_physical, - texture_mgr, - mmuseg_texture_preview_models, - mmuseg_texture_preview_colors, - mmuseg_texture_preview_filament_ids); - } if (has_texture_preview_state) { build_mmu_vertex_color_preview_models(*model_volume, triangles_per_type, @@ -799,6 +788,18 @@ void GLVolume::simple_render(GLShaderProgram* shader, mmuseg_vertex_color_preview_colors, mmuseg_vertex_color_preview_filament_ids); } + if (has_texture_preview_state && !use_original_mesh_texture_preview && !has_active_texture_mapping_color_preview_data && has_texture_preview_data) { + build_mmu_texture_preview_models(*model_volume, + triangles_per_type, + state_colors, + base_filament_id, + num_physical, + texture_mgr, + mmuseg_texture_preview_models, + mmuseg_texture_preview_colors, + mmuseg_texture_preview_filament_ids, + &mmuseg_vertex_color_preview_filament_ids); + } mmuseg_ts = model_volume->mmu_segmentation_facets.timestamp(); mmuseg_texture_preview_visual_signature = preview_visual_signature; } @@ -1012,7 +1013,8 @@ void GLVolume::render_mmu_texture_preview(const Transform3d &view_matrix, print_volume_type, print_volume_xy, print_volume_z, - opaque); + opaque, + model_volume); } if (this->is_left_handed()) diff --git a/src/slic3r/GUI/GLShadersManager.cpp b/src/slic3r/GUI/GLShadersManager.cpp index 1ed6a63ce0a..9994b240278 100644 --- a/src/slic3r/GUI/GLShadersManager.cpp +++ b/src/slic3r/GUI/GLShadersManager.cpp @@ -83,6 +83,7 @@ std::pair GLShadersManager::init() #if !SLIC3R_OPENGL_ES valid &= append_shader("painted_texture_preview", { prefix + "painted_texture_preview.vs", prefix + "painted_texture_preview.fs" }); valid &= append_shader("painted_vertex_color_preview", { prefix + "painted_vertex_color_preview.vs", prefix + "painted_vertex_color_preview.fs" }); + valid &= append_shader("painted_surface_gradient_preview", { prefix + "painted_surface_gradient_preview.vs", prefix + "painted_surface_gradient_preview.fs" }); #endif // !SLIC3R_OPENGL_ES // Used to render painted triangles inside the multi-material gizmo. Triangle normals are computed inside fragment shader. // For Apple's on Arm CPU computed triangle normals inside fragment shader using dFdx and dFdy has the opposite direction. diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index 14ea60b1ad6..0bb5361a167 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -9427,7 +9427,18 @@ public: SetMinSize(GetSize()); CenterOnParent(); + SetEscapeId(wxID_CLOSE); Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { EndModal(wxID_CLOSE); }, wxID_CLOSE); + Bind(wxEVT_CHAR_HOOK, [this](wxKeyEvent &event) { + if (event.GetKeyCode() == WXK_ESCAPE) { + if (IsModal()) + EndModal(wxID_CLOSE); + else + Close(); + return; + } + event.Skip(); + }); m_background_picker->Bind(wxEVT_LEFT_DOWN, [this](wxMouseEvent &) { pick_background_color(); }); m_background_clear->Bind(wxEVT_BUTTON, [this](wxCommandEvent &) { clear_background_color(); }); } @@ -13160,7 +13171,8 @@ void GLGizmoTrueColorPainting::render_cached_rgb_data_preview(const ModelObject -1, std::array{ 0.f, 0.f, 0.f, 0.f }, std::array{ 0.f, 0.f }, - true); + true, + volume); } } diff --git a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp index 154e092c25e..d30cd366b60 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoPainterBase.cpp @@ -1847,17 +1847,6 @@ void TriangleSelectorPatch::update_render_data() num_physical, texture_mgr, m_texture_mapping_color_preview); - 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, - m_model_volume->extruder_id() > 0 ? unsigned(m_model_volume->extruder_id()) : 0u, - num_physical, - texture_mgr, - m_texture_preview_models, - m_texture_preview_colors, - m_texture_preview_filament_ids); - } build_mmu_vertex_color_preview_models(*m_model_volume, triangles_per_type, m_ebt_colors, @@ -1869,6 +1858,18 @@ void TriangleSelectorPatch::update_render_data() m_vertex_color_preview_filament_ids, nullptr, reinterpret_cast(this)); + 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, + m_model_volume->extruder_id() > 0 ? unsigned(m_model_volume->extruder_id()) : 0u, + num_physical, + texture_mgr, + m_texture_preview_models, + m_texture_preview_colors, + m_texture_preview_filament_ids, + &m_vertex_color_preview_filament_ids); + } m_texture_preview_visual_signature = visual_signature; } } @@ -2076,7 +2077,8 @@ void TriangleSelectorPatch::render_texture_preview(const Transform3d& m -1, std::array{ 0.f, 0.f, 0.f, 0.f }, std::array{ 0.f, 0.f }, - m_texture_preview_opaque); + m_texture_preview_opaque, + m_model_volume); } } diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp index 19442c339d4..daceea78610 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.cpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.cpp @@ -41,6 +41,7 @@ constexpr size_t k_simulated_texture_preview_max_pixels = 2048ull * 2048ull; constexpr unsigned int k_temporary_simulated_texture_preview_max_edge = 1024; constexpr size_t k_temporary_simulated_texture_preview_max_pixels = 1024ull * 1024ull; 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 const char *TEXTURE_MAPPING_BACKGROUND_COLOR_CONFIG_KEY = "texture_mapping_background_color"; struct TexturePreviewMixCandidate @@ -4079,101 +4080,6 @@ bool build_simulated_image_texture_halftone_preview_model_for_state( out_model); } -float normalize_angle(float angle) -{ - if (!std::isfinite(angle)) - return 0.f; - float out = std::fmod(angle, 360.f); - if (out < 0.f) - out += 360.f; - return out; -} - -float angular_distance_deg(float a, float b) -{ - const float d = std::abs(normalize_angle(a) - normalize_angle(b)); - return std::min(d, 360.f - d); -} - -float angular_distance_cw(float from_deg, float to_deg) -{ - float d = normalize_angle(to_deg) - normalize_angle(from_deg); - if (d < 0.f) - d += 360.f; - return d; -} - -float component_angular_influence(unsigned int component_id, - float theta_deg, - const std::vector &component_ids, - const std::vector &component_angles_deg) -{ - if (component_ids.empty() || component_ids.size() != component_angles_deg.size()) - return 0.f; - - const auto active_it = std::find(component_ids.begin(), component_ids.end(), component_id); - if (active_it == component_ids.end()) - return 0.f; - if (component_ids.size() == 1) - return 1.f; - - struct SortedComponentAngle { - float angle_deg { 0.f }; - size_t component_idx { 0 }; - }; - - std::vector sorted_angles; - sorted_angles.reserve(component_ids.size()); - for (size_t i = 0; i < component_ids.size(); ++i) - sorted_angles.push_back({ normalize_angle(component_angles_deg[i]), i }); - - std::sort(sorted_angles.begin(), sorted_angles.end(), [](const SortedComponentAngle &lhs, const SortedComponentAngle &rhs) { - return lhs.angle_deg < rhs.angle_deg; - }); - - const size_t active_component_idx = size_t(active_it - component_ids.begin()); - const auto sorted_active_it = std::find_if(sorted_angles.begin(), sorted_angles.end(), [active_component_idx](const SortedComponentAngle &entry) { - return entry.component_idx == active_component_idx; - }); - if (sorted_active_it == sorted_angles.end()) - return 0.f; - - const size_t sorted_pos = size_t(sorted_active_it - sorted_angles.begin()); - const size_t count = sorted_angles.size(); - const float prev_angle = sorted_angles[(sorted_pos + count - 1) % count].angle_deg; - const float self_angle = sorted_angles[sorted_pos].angle_deg; - const float next_angle = sorted_angles[(sorted_pos + 1) % count].angle_deg; - const float prev_to_self_deg = angular_distance_cw(prev_angle, self_angle); - const float self_to_next_deg = angular_distance_cw(self_angle, next_angle); - - if (prev_to_self_deg <= 1e-3f || self_to_next_deg <= 1e-3f) { - float total_weight = 0.f; - float active_weight = 0.f; - for (size_t i = 0; i < component_ids.size(); ++i) { - const float dist = angular_distance_deg(theta_deg, component_angles_deg[i]); - const float weight = std::max(0.f, 1.f - dist / 180.f); - total_weight += weight; - if (component_ids[i] == component_id) - active_weight += weight; - } - - if (total_weight <= k_epsilon) - return 0.f; - return std::clamp(active_weight / total_weight, 0.f, 1.f); - } - - const float theta_norm = normalize_angle(theta_deg); - const float prev_to_theta_deg = angular_distance_cw(prev_angle, theta_norm); - if (prev_to_theta_deg <= prev_to_self_deg + 1e-4f) - return std::clamp(prev_to_theta_deg / prev_to_self_deg, 0.f, 1.f); - - const float self_to_theta_deg = angular_distance_cw(self_angle, theta_norm); - if (self_to_theta_deg <= self_to_next_deg + 1e-4f) - return std::clamp(1.f - self_to_theta_deg / self_to_next_deg, 0.f, 1.f); - - return 0.f; -} - std::vector decode_surface_gradient_component_ids(const TextureMappingZone &zone, size_t num_physical) { std::vector ids; @@ -4206,145 +4112,6 @@ std::vector decode_surface_gradient_component_ids(const TextureMap return ids; } -float repeated_rotation_progress(float progress01, float repeats, bool reverse_repeats) -{ - const float p = clamp01(progress01); - const float r = std::max(1.f, repeats); - if (r <= 1.f + k_epsilon) - return p; - - float repeated_pos = p * r; - int segment_idx = int(std::floor(repeated_pos)); - float local = repeated_pos - float(segment_idx); - - if (p >= 1.f - k_epsilon) { - segment_idx = std::max(0, int(std::ceil(r)) - 1); - local = 1.f; - } - - if (reverse_repeats && (segment_idx % 2 == 1)) - local = 1.f - local; - return clamp01(local); -} - -float offset_fade_factor(int fade_mode, float progress01) -{ - const float p = clamp01(progress01); - switch (fade_mode) { - case int(TextureMappingZone::OffsetFadeInUp): - return p; - case int(TextureMappingZone::OffsetFadeOutUp): - return 1.f - p; - case int(TextureMappingZone::OffsetFadeInOut): - return 1.f - std::abs(2.f * p - 1.f); - case int(TextureMappingZone::OffsetFadeOutIn): - return std::abs(2.f * p - 1.f); - case int(TextureMappingZone::OffsetFadeOutInReversed): - return 2.f * p - 1.f; - default: - return 1.f; - } -} - -float variable_width_delta(float inset_strength, - float max_width_delta_limit_mm, - float minimum_offset_factor, - float strength_factor) -{ - if (!std::isfinite(max_width_delta_limit_mm) || max_width_delta_limit_mm <= 0.f) - return 0.f; - - const float desired_width_factor = 1.f - std::clamp(inset_strength, 0.f, 1.f); - const float min_width_factor = std::clamp(minimum_offset_factor, 0.f, 1.f); - const float adjusted_width_factor = - min_width_factor + desired_width_factor * std::clamp(strength_factor, 0.f, 1.f) * (1.f - min_width_factor); - - return std::clamp(max_width_delta_limit_mm * (1.f - adjusted_width_factor), 0.f, max_width_delta_limit_mm); -} - -ColorRGBA surface_gradient_preview_color_from_weights(const SurfaceGradientPreviewSettings &settings, - const std::vector &weights) -{ - const std::array rgb = mix_color_solver_components(settings.component_colors, weights, ColorSolverMixModel::PigmentPainter); - return { rgb[0], rgb[1], rgb[2], 1.f }; -} - -ColorRGBA surface_gradient_preview_color_at(const SurfaceGradientPreviewSettings &settings, - const Vec3f &position, - const Vec3f &normal) -{ - if (settings.component_ids.empty() || settings.component_ids.size() != settings.component_colors.size()) - return { 0.15f, 0.65f, 0.6f, 1.f }; - - const float z_span = settings.z_max - settings.z_min; - const float z_progress = z_span > k_epsilon ? - std::clamp((position.z() - settings.z_min) / z_span, 0.f, 1.f) : - 0.f; - - float rotation_deg = 0.f; - if (settings.rotation_enabled) { - const float repeated = repeated_rotation_progress(z_progress, std::max(1.f, settings.repeats), settings.reverse_repeats); - const float direction = settings.clockwise ? -1.f : 1.f; - rotation_deg = direction * 360.f * settings.rotations * repeated; - } - - std::vector rotated_angles = settings.angles_deg; - for (float &angle : rotated_angles) - angle = normalize_angle(angle + rotation_deg); - - Vec2f direction = Vec2f::Zero(); - if (settings.angle_mode == int(TextureMappingZone::OffsetAngleSurfaceNormal)) - direction = Vec2f(normal.x(), normal.y()); - - if (direction.squaredNorm() <= k_epsilon) { - const Vec3f radial = position - settings.center; - direction = Vec2f(radial.x(), radial.y()); - } - if (direction.squaredNorm() <= k_epsilon) - direction = Vec2f(1.f, 0.f); - - const float theta_deg = normalize_angle(float(Geometry::rad2deg(std::atan2(direction.y(), direction.x())))); - - const size_t component_count = settings.component_ids.size(); - std::vector influences(component_count, 0.f); - for (size_t i = 0; i < component_count; ++i) - influences[i] = component_angular_influence(settings.component_ids[i], theta_deg, settings.component_ids, rotated_angles); - - const float fade_factor = std::abs(offset_fade_factor(settings.fade_mode, z_progress)); - std::vector edge_reaches(component_count, 0.f); - for (size_t i = 0; i < component_count; ++i) { - float raw_inset_mm = 0.f; - for (size_t j = 0; j < component_count; ++j) { - if (i == j) - continue; - const float distance_mm = j < settings.distances_mm.size() ? settings.distances_mm[j] : 0.f; - raw_inset_mm += distance_mm * influences[j]; - } - - const float inset_strength = std::clamp(raw_inset_mm / std::max(settings.max_component_distance_mm, k_epsilon), 0.f, 1.f); - const float strength_factor = i < settings.strength_factors.size() ? settings.strength_factors[i] : 1.f; - const float minimum_offset_factor = i < settings.minimum_offset_factors.size() ? settings.minimum_offset_factors[i] : 0.f; - const float width_delta_mm = variable_width_delta(inset_strength * fade_factor, - settings.max_width_delta_limit_mm, - minimum_offset_factor, - strength_factor); - edge_reaches[i] = std::clamp(settings.max_width_delta_limit_mm - width_delta_mm, 0.f, settings.max_width_delta_limit_mm); - } - - const auto minmax_reach = std::minmax_element(edge_reaches.begin(), edge_reaches.end()); - std::vector weights(component_count, 0.f); - if (minmax_reach.first != edge_reaches.end() && (*minmax_reach.second - *minmax_reach.first) > k_epsilon) { - const float base_reach = *minmax_reach.first; - const float reach_span = *minmax_reach.second - base_reach; - for (size_t i = 0; i < component_count; ++i) - weights[i] = std::clamp((edge_reaches[i] - base_reach) / reach_span, 0.f, 1.f); - } else { - std::fill(weights.begin(), weights.end(), 1.f); - } - - return surface_gradient_preview_color_from_weights(settings, weights); -} - float surface_gradient_preview_config_float(const char *key, float fallback) { return texture_preview_config_float(key, fallback); @@ -4431,16 +4198,14 @@ std::optional surface_gradient_preview_settings_ return settings; } -bool build_surface_gradient_vertex_color_preview_model_for_state(const std::vector &state_triangles, - const SurfaceGradientPreviewSettings &settings, - const Transform3d &world_matrix, - GUI::GLModel &out_model) +bool build_surface_gradient_preview_model_for_state(const std::vector &state_triangles, + GUI::GLModel &out_model) { if (state_triangles.empty()) return false; GUI::GLModel::Geometry geometry; - geometry.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3N3C4 }; + geometry.format = { GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3N3 }; geometry.reserve_vertices(state_triangles.size() * 3); geometry.reserve_indices(state_triangles.size() * 3); @@ -4453,25 +4218,9 @@ bool build_surface_gradient_vertex_color_preview_model_for_state(const std::vect normal /= normal_len; const Vec3f offset = normal * k_preview_offset; - const Vec3f world_vertices[3] = { - (world_matrix * triangle.vertices[0].cast()).cast(), - (world_matrix * triangle.vertices[1].cast()).cast(), - (world_matrix * triangle.vertices[2].cast()).cast() - }; - Vec3f world_normal = (world_vertices[1] - world_vertices[0]).cross(world_vertices[2] - world_vertices[0]); - const float world_normal_len = world_normal.norm(); - if (world_normal_len <= k_epsilon) - world_normal = normal; - else - world_normal /= world_normal_len; - - const ColorRGBA c0 = surface_gradient_preview_color_at(settings, world_vertices[0], world_normal); - const ColorRGBA c1 = surface_gradient_preview_color_at(settings, world_vertices[1], world_normal); - const ColorRGBA c2 = surface_gradient_preview_color_at(settings, world_vertices[2], world_normal); - - geometry.add_vertex(triangle.vertices[0] + offset, normal, c0); - geometry.add_vertex(triangle.vertices[1] + offset, normal, c1); - geometry.add_vertex(triangle.vertices[2] + offset, normal, c2); + geometry.add_vertex(triangle.vertices[0] + offset, normal); + geometry.add_vertex(triangle.vertices[1] + offset, normal); + geometry.add_vertex(triangle.vertices[2] + offset, normal); geometry.add_triangle(vertex_index, vertex_index + 1, vertex_index + 2); vertex_index += 3; } @@ -4566,6 +4315,46 @@ void set_common_uniforms(GLShaderProgram &shader, shader.set_uniform("print_volume.z_data", print_volume_z); } +std::string gradient_array_uniform_name(const char *name, size_t idx) +{ + return std::string(name) + "[" + std::to_string(idx) + "]"; +} + +void set_surface_gradient_preview_uniforms(GLShaderProgram &shader, const SurfaceGradientPreviewSettings *settings) +{ + const size_t count = settings == nullptr ? size_t(0) : + std::min(settings->component_colors.size(), k_surface_gradient_preview_max_components); + shader.set_uniform("gradient_component_count", int(count)); + shader.set_uniform("gradient_max_component_distance_mm", settings != nullptr ? settings->max_component_distance_mm : 0.f); + shader.set_uniform("gradient_max_width_delta_limit_mm", settings != nullptr ? settings->max_width_delta_limit_mm : 0.f); + shader.set_uniform("gradient_angle_mode", settings != nullptr ? settings->angle_mode : int(TextureMappingZone::OffsetAngleObjectCenter)); + shader.set_uniform("gradient_rotation_enabled", settings != nullptr && settings->rotation_enabled); + shader.set_uniform("gradient_rotations", settings != nullptr ? settings->rotations : 1.f); + shader.set_uniform("gradient_repeats", settings != nullptr ? settings->repeats : 1.f); + shader.set_uniform("gradient_reverse_repeats", settings != nullptr && settings->reverse_repeats); + shader.set_uniform("gradient_clockwise", settings == nullptr || settings->clockwise); + shader.set_uniform("gradient_fade_mode", settings != nullptr ? settings->fade_mode : int(TextureMappingZone::OffsetFadeNone)); + const Vec3f center = settings != nullptr ? settings->center : Vec3f::Zero(); + shader.set_uniform("gradient_center", center); + shader.set_uniform("gradient_z_min", settings != nullptr ? settings->z_min : 0.f); + shader.set_uniform("gradient_z_max", settings != nullptr ? settings->z_max : 0.f); + + for (size_t idx = 0; idx < k_surface_gradient_preview_max_components; ++idx) { + const std::array color = settings != nullptr && idx < count ? + settings->component_colors[idx] : + std::array{ 0.f, 0.f, 0.f }; + const float distance_mm = settings != nullptr && idx < settings->distances_mm.size() ? settings->distances_mm[idx] : 0.f; + const float angle_deg = settings != nullptr && idx < settings->angles_deg.size() ? settings->angles_deg[idx] : 0.f; + const float strength = settings != nullptr && idx < settings->strength_factors.size() ? settings->strength_factors[idx] : 1.f; + const float minimum = settings != nullptr && idx < settings->minimum_offset_factors.size() ? settings->minimum_offset_factors[idx] : 0.f; + shader.set_uniform(gradient_array_uniform_name("gradient_component_colors", idx).c_str(), color); + shader.set_uniform(gradient_array_uniform_name("gradient_distances_mm", idx).c_str(), distance_mm); + shader.set_uniform(gradient_array_uniform_name("gradient_angles_deg", idx).c_str(), angle_deg); + shader.set_uniform(gradient_array_uniform_name("gradient_strength_factors", idx).c_str(), strength); + shader.set_uniform(gradient_array_uniform_name("gradient_minimum_offset_factors", idx).c_str(), minimum); + } +} + } // namespace bool model_volume_has_texture_preview_data(const ModelVolume &model_volume) @@ -4644,7 +4433,8 @@ bool build_mmu_texture_preview_models( const TextureMappingManager *texture_mgr, std::vector &out_models, std::vector &out_colors, - std::vector &out_filament_ids) + std::vector &out_filament_ids, + const std::vector *ready_simulated_filament_ids) { out_models.clear(); out_colors.clear(); @@ -4653,6 +4443,11 @@ bool build_mmu_texture_preview_models( return false; const std::vector physical_colors = physical_filament_colors_for_texture_preview(num_physical); + auto has_ready_simulated_preview = [ready_simulated_filament_ids](unsigned int filament_id) { + return ready_simulated_filament_ids != nullptr && + std::find(ready_simulated_filament_ids->begin(), ready_simulated_filament_ids->end(), filament_id) != + ready_simulated_filament_ids->end(); + }; 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); @@ -4661,7 +4456,9 @@ bool build_mmu_texture_preview_models( continue; std::optional simulation_settings = texture_preview_simulation_settings_for_filament(filament_id, num_physical, texture_mgr, physical_colors); - if (simulation_settings && texture_preview_uses_halftone_dithering(*simulation_settings)) + if (simulation_settings && + texture_preview_uses_halftone_dithering(*simulation_settings) && + (ready_simulated_filament_ids == nullptr || has_ready_simulated_preview(filament_id))) continue; GUI::GLModel model; @@ -4731,9 +4528,10 @@ bool build_mmu_vertex_color_preview_models( GUI::GLModel model; if (is_gradient_zone(*zone)) { std::optional settings = surface_gradient_preview_settings_for_zone(model_volume, world_matrix, *zone, num_physical); - if (!settings) + const bool invalid = texture_preview_settings_invalid_for_filament(filament_id, num_physical, texture_mgr); + if (!settings && !invalid) continue; - if (!build_surface_gradient_vertex_color_preview_model_for_state(triangles_per_type[state_id], *settings, world_matrix, model)) + if (!build_surface_gradient_preview_model_for_state(triangles_per_type[state_id], model)) continue; } else { const bool has_texture_mapping_color_preview = @@ -5218,12 +5016,16 @@ void render_model_texture_preview_models( if (mix <= 0.f && !invalid) continue; - const GUI::GLTexture *preview_texture = simulated_texture_preview_texture_for_filament(model_volume, - filament_ids[idx], - num_physical, - texture_mgr, - texture_signature, - texture); + const bool force_original_texture = + texture_preview_halftone_simulation_enabled_for_filament(filament_ids[idx], num_physical, texture_mgr); + const GUI::GLTexture *preview_texture = force_original_texture ? + &texture : + simulated_texture_preview_texture_for_filament(model_volume, + filament_ids[idx], + num_physical, + texture_mgr, + texture_signature, + texture); if (preview_texture == nullptr || preview_texture->get_id() == 0) continue; @@ -5275,12 +5077,16 @@ void render_model_texture_preview_model( return; const size_t texture_signature = model_volume_texture_preview_signature(model_volume); - const GUI::GLTexture *preview_texture = simulated_texture_preview_texture_for_filament(model_volume, - filament_id, - num_physical, - texture_mgr, - texture_signature, - texture); + const bool force_original_texture = + texture_preview_halftone_simulation_enabled_for_filament(filament_id, num_physical, texture_mgr); + const GUI::GLTexture *preview_texture = force_original_texture ? + &texture : + simulated_texture_preview_texture_for_filament(model_volume, + filament_id, + num_physical, + texture_mgr, + texture_signature, + texture); if (preview_texture == nullptr || preview_texture->get_id() == 0) return; @@ -5326,29 +5132,46 @@ void render_model_vertex_color_preview_models( int print_volume_type, const std::array &print_volume_xy, const std::array &print_volume_z, - bool opaque) + bool opaque, + const ModelVolume *model_volume) { if (models.empty() || colors.size() != models.size() || filament_ids.size() != models.size()) return; - GLShaderProgram *shader = GUI::wxGetApp().get_shader("painted_vertex_color_preview"); - if (shader == nullptr) + GLShaderProgram *vertex_shader = GUI::wxGetApp().get_shader("painted_vertex_color_preview"); + GLShaderProgram *gradient_shader = GUI::wxGetApp().get_shader("painted_surface_gradient_preview"); + if (vertex_shader == nullptr && gradient_shader == nullptr) return; const TexturePreviewRenderState render_state = begin_render_state(opaque); - shader->start_using(); - set_common_uniforms(*shader, - model_matrix, - view_matrix, - projection_matrix, - z_range, - clipping_plane, - print_volume_type, - print_volume_xy, - print_volume_z); + GLShaderProgram *active_shader = nullptr; + auto use_shader = [&](GLShaderProgram *shader) { + if (shader == nullptr) + return false; + if (active_shader == shader) + return true; + if (active_shader != nullptr) + active_shader->stop_using(); + active_shader = shader; + active_shader->start_using(); + set_common_uniforms(*active_shader, + model_matrix, + view_matrix, + projection_matrix, + z_range, + clipping_plane, + print_volume_type, + print_volume_xy, + print_volume_z); + return true; + }; for (size_t idx = 0; idx < models.size(); ++idx) { const bool raw_vertex_color_preview = filament_ids[idx] == 0; + const TextureMappingZone *zone = raw_vertex_color_preview ? + nullptr : + zone_for_filament(filament_ids[idx], num_physical, texture_mgr); + const bool gradient_preview = zone != nullptr && is_gradient_zone(*zone); const float mix = raw_vertex_color_preview ? 1.f : texture_preview_mix_for_filament(filament_ids[idx], num_physical, texture_mgr); @@ -5357,13 +5180,31 @@ void render_model_vertex_color_preview_models( texture_preview_settings_invalid_for_filament(filament_ids[idx], num_physical, texture_mgr); if (mix <= 0.f && !invalid) continue; - shader->set_uniform("texture_preview_mix", mix); - shader->set_uniform("invalid_texture_mapping", invalid); + + if (gradient_preview) { + if (model_volume == nullptr || gradient_shader == nullptr) + continue; + const std::optional settings = + surface_gradient_preview_settings_for_zone(*model_volume, model_matrix, *zone, num_physical); + if (!settings && !invalid) + continue; + if (!use_shader(gradient_shader)) + continue; + gradient_shader->set_uniform("texture_preview_mix", mix); + gradient_shader->set_uniform("invalid_texture_mapping", invalid); + set_surface_gradient_preview_uniforms(*gradient_shader, settings ? &*settings : nullptr); + } else { + if (vertex_shader == nullptr || !use_shader(vertex_shader)) + continue; + vertex_shader->set_uniform("texture_preview_mix", mix); + vertex_shader->set_uniform("invalid_texture_mapping", invalid); + } models[idx].set_color(colors[idx]); - models[idx].render(); + models[idx].render(active_shader); } - shader->stop_using(); + if (active_shader != nullptr) + active_shader->stop_using(); restore_render_state(render_state); } diff --git a/src/slic3r/GUI/MMUPaintedTexturePreview.hpp b/src/slic3r/GUI/MMUPaintedTexturePreview.hpp index c3999c68434..ecb1d285669 100644 --- a/src/slic3r/GUI/MMUPaintedTexturePreview.hpp +++ b/src/slic3r/GUI/MMUPaintedTexturePreview.hpp @@ -25,7 +25,8 @@ bool build_mmu_texture_preview_models( const TextureMappingManager *texture_mgr, std::vector &out_models, std::vector &out_colors, - std::vector &out_filament_ids); + std::vector &out_filament_ids, + const std::vector *ready_simulated_filament_ids = nullptr); bool build_texture_preview_model_for_state(const ModelVolume &model_volume, const std::vector &state_triangles, @@ -147,7 +148,8 @@ void render_model_vertex_color_preview_models( int print_volume_type = -1, const std::array &print_volume_xy = std::array{ 0.f, 0.f, 0.f, 0.f }, const std::array &print_volume_z = std::array{ 0.f, 0.f }, - bool opaque = false); + bool opaque = false, + const ModelVolume *model_volume = nullptr); } // namespace Slic3r diff --git a/src/slic3r/GUI/Plater.cpp b/src/slic3r/GUI/Plater.cpp index 5102a6586d9..4788161409d 100644 --- a/src/slic3r/GUI/Plater.cpp +++ b/src/slic3r/GUI/Plater.cpp @@ -1324,6 +1324,15 @@ private: bool m_remove_requested {false}; }; +static bool texture_mapping_prime_tower_images_equal(const TextureMappingPrimeTowerImage &lhs, + const TextureMappingPrimeTowerImage &rhs) +{ + return lhs.width == rhs.width && + lhs.height == rhs.height && + lhs.image_name == rhs.image_name && + lhs.rgba == rhs.rgba; +} + class TextureMappingAdvancedOptionsDialog : public wxDialog { public: @@ -6475,7 +6484,9 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) physical_colors, apply_zone, bundle, - set_config_string](wxCommandEvent &) { + set_config_string, + notify_change, + refresh_texture_mapping_preview](wxCommandEvent &) { if (zone_index >= mgr_ptr->zones().size()) return; TextureMappingZone updated = mgr_ptr->zones()[zone_index]; @@ -6579,8 +6590,14 @@ void Sidebar::update_texture_mapping_panel(bool sync_manager) dlg_global_settings.serialize() != bundle->texture_mapping_global_settings.serialize(); if (global_settings_changed) bundle->texture_mapping_global_settings = dlg_global_settings; - wxGetApp().model().texture_mapping_prime_tower_image = dlg.prime_tower_image(); - wxGetApp().model().texture_mapping_prime_tower_image_back = dlg.prime_tower_image_back(); + Model &model = wxGetApp().model(); + const bool prime_tower_images_changed = + !texture_mapping_prime_tower_images_equal(model.texture_mapping_prime_tower_image, dlg.prime_tower_image()) || + !texture_mapping_prime_tower_images_equal(model.texture_mapping_prime_tower_image_back, dlg.prime_tower_image_back()); + if (prime_tower_images_changed) { + model.texture_mapping_prime_tower_image = dlg.prime_tower_image(); + model.texture_mapping_prime_tower_image_back = dlg.prime_tower_image_back(); + } if (global_settings_changed) set_config_string("texture_mapping_global_settings", bundle->texture_mapping_global_settings.serialize()); if (updated.filament_strengths_pct.size() < palette.size()) @@ -6609,12 +6626,11 @@ 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)); - if (affects_scene && p->plater != nullptr && !p->plater->is_preview_shown()) { - if (GLCanvas3D *canvas = p->plater->get_view3D_canvas3D()) - canvas->reload_scene(true, true); - if (GLCanvas3D *canvas = p->plater->get_assmeble_canvas3D()) - canvas->reload_scene(true, true); - } + 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) + refresh_texture_mapping_preview(); CallAfter([this]() { update_texture_mapping_panel(false); }); });