diff --git a/src/slic3r/GUI/3DScene.cpp b/src/slic3r/GUI/3DScene.cpp index ffcec0d626d..44bef05be3b 100644 --- a/src/slic3r/GUI/3DScene.cpp +++ b/src/slic3r/GUI/3DScene.cpp @@ -1037,8 +1037,10 @@ void GLVolume::render_sinking_contours() static constexpr float prime_tower_preview_epsilon = 1e-6f; static constexpr float prime_tower_preview_offset = 0.001f; +static constexpr float prime_tower_preview_coord_epsilon = 1e-4f; -float prime_tower_preview_anchor_distance(const std::array &points, const Vec2f ¢er, float angle_deg) +template +float prime_tower_preview_anchor_distance(const Points &points, const Vec2f ¢er, float angle_deg) { float travelled = 0.f; float fallback_distance = 0.f; @@ -1104,7 +1106,8 @@ float prime_tower_preview_texture_v(float z, float texture_z_min, float texture_ return 1.f - v; } -float prime_tower_preview_anchor_angle(const std::array &points, float angle_deg) +template +float prime_tower_preview_anchor_angle(const Points &points, float angle_deg) { Vec2f min_pt(std::numeric_limits::max(), std::numeric_limits::max()); Vec2f max_pt(std::numeric_limits::lowest(), std::numeric_limits::lowest()); @@ -1239,6 +1242,387 @@ GUI::GLModel::Geometry prime_tower_image_preview_geometry(float width, return data; } +struct PrimeTowerPreviewRingEdgeGroup +{ + float z = 0.f; + std::vector points; + std::vector> edges; +}; + +struct PrimeTowerPreviewRing +{ + float z = 0.f; + std::vector points; + std::vector distances; + float total_length = 0.f; + float anchor_distance = 0.f; +}; + +int prime_tower_preview_find_z_group(std::vector &groups, float z) +{ + for (size_t i = 0; i < groups.size(); ++i) { + if (std::abs(groups[i].z - z) <= prime_tower_preview_coord_epsilon) + return int(i); + } + groups.emplace_back(); + groups.back().z = z; + return int(groups.size() - 1); +} + +int prime_tower_preview_find_point(std::vector &points, const Vec2f &point) +{ + for (size_t i = 0; i < points.size(); ++i) { + if ((points[i] - point).norm() <= prime_tower_preview_coord_epsilon) + return int(i); + } + points.emplace_back(point); + return int(points.size() - 1); +} + +void prime_tower_preview_add_edge(PrimeTowerPreviewRingEdgeGroup &group, int a, int b) +{ + if (a == b) + return; + for (const std::pair &edge : group.edges) + if ((edge.first == a && edge.second == b) || (edge.first == b && edge.second == a)) + return; + group.edges.emplace_back(a, b); +} + +size_t prime_tower_preview_lowest_point_index(const std::vector &points) +{ + size_t best = 0; + for (size_t i = 1; i < points.size(); ++i) { + if (points[i].y() < points[best].y() - prime_tower_preview_coord_epsilon || + (std::abs(points[i].y() - points[best].y()) <= prime_tower_preview_coord_epsilon && + points[i].x() < points[best].x())) { + best = i; + } + } + return best; +} + +float prime_tower_preview_polygon_area(const std::vector &points) +{ + double area = 0.; + for (size_t i = 0; i < points.size(); ++i) { + const Vec2f &a = points[i]; + const Vec2f &b = points[(i + 1) % points.size()]; + area += double(a.x()) * double(b.y()) - double(b.x()) * double(a.y()); + } + return float(0.5 * area); +} + +void prime_tower_preview_rotate_to_lowest_point(std::vector &points) +{ + if (points.empty()) + return; + const size_t start = prime_tower_preview_lowest_point_index(points); + std::rotate(points.begin(), points.begin() + start, points.end()); +} + +bool prime_tower_preview_build_loop(const PrimeTowerPreviewRingEdgeGroup &group, std::vector &loop) +{ + loop.clear(); + if (group.points.size() < 3 || group.edges.size() < 3) + return false; + + std::vector> adjacency(group.points.size()); + for (const std::pair &edge : group.edges) { + if (edge.first < 0 || edge.second < 0 || edge.first >= int(group.points.size()) || edge.second >= int(group.points.size())) + continue; + adjacency[size_t(edge.first)].emplace_back(edge.second); + adjacency[size_t(edge.second)].emplace_back(edge.first); + } + + const int start = int(prime_tower_preview_lowest_point_index(group.points)); + int prev = -1; + int current = start; + bool closed = false; + for (size_t guard = 0; guard <= group.points.size(); ++guard) { + loop.emplace_back(group.points[size_t(current)]); + const std::vector &neighbors = adjacency[size_t(current)]; + if (neighbors.empty()) + return false; + + int next = -1; + for (const int candidate : neighbors) { + if (candidate != prev) { + next = candidate; + break; + } + } + if (next < 0) + return false; + if (next == start) { + closed = true; + break; + } + prev = current; + current = next; + } + + if (!closed || loop.size() < 3) + return false; + if (prime_tower_preview_polygon_area(loop) < 0.f) + std::reverse(loop.begin(), loop.end()); + prime_tower_preview_rotate_to_lowest_point(loop); + return true; +} + +void prime_tower_preview_prepare_ring(PrimeTowerPreviewRing &ring, float angle_offset_deg) +{ + ring.distances.assign(ring.points.size(), 0.f); + ring.total_length = 0.f; + for (size_t i = 0; i < ring.points.size(); ++i) { + ring.distances[i] = ring.total_length; + ring.total_length += (ring.points[(i + 1) % ring.points.size()] - ring.points[i]).norm(); + } + + Vec2f min_pt(std::numeric_limits::max(), std::numeric_limits::max()); + Vec2f max_pt(std::numeric_limits::lowest(), std::numeric_limits::lowest()); + for (const Vec2f &point : ring.points) { + min_pt.x() = std::min(min_pt.x(), point.x()); + min_pt.y() = std::min(min_pt.y(), point.y()); + max_pt.x() = std::max(max_pt.x(), point.x()); + max_pt.y() = std::max(max_pt.y(), point.y()); + } + const Vec2f center = (min_pt + max_pt) * 0.5f; + ring.anchor_distance = + prime_tower_preview_anchor_distance(ring.points, center, prime_tower_preview_anchor_angle(ring.points, angle_offset_deg)); +} + +std::vector prime_tower_preview_extract_mesh_rings(const TriangleMesh &mesh, float angle_offset_deg) +{ + std::vector groups; + for (const stl_triangle_vertex_indices &face : mesh.its.indices) { + const Vec3f vertices[3] = { + mesh.its.vertices[size_t(face[0])], + mesh.its.vertices[size_t(face[1])], + mesh.its.vertices[size_t(face[2])] + }; + const float z_min = std::min({vertices[0].z(), vertices[1].z(), vertices[2].z()}); + const float z_max = std::max({vertices[0].z(), vertices[1].z(), vertices[2].z()}); + if (z_max - z_min <= prime_tower_preview_coord_epsilon) + continue; + + for (int edge_idx = 0; edge_idx < 3; ++edge_idx) { + const Vec3f &a = vertices[edge_idx]; + const Vec3f &b = vertices[(edge_idx + 1) % 3]; + if (std::abs(a.z() - b.z()) > prime_tower_preview_coord_epsilon) + continue; + PrimeTowerPreviewRingEdgeGroup &group = groups[size_t(prime_tower_preview_find_z_group(groups, 0.5f * (a.z() + b.z())))]; + const int a_idx = prime_tower_preview_find_point(group.points, Vec2f(a.x(), a.y())); + const int b_idx = prime_tower_preview_find_point(group.points, Vec2f(b.x(), b.y())); + prime_tower_preview_add_edge(group, a_idx, b_idx); + } + } + + std::sort(groups.begin(), groups.end(), [](const PrimeTowerPreviewRingEdgeGroup &lhs, const PrimeTowerPreviewRingEdgeGroup &rhs) { + return lhs.z < rhs.z; + }); + + std::vector rings; + rings.reserve(groups.size()); + for (const PrimeTowerPreviewRingEdgeGroup &group : groups) { + PrimeTowerPreviewRing ring; + ring.z = group.z; + if (!prime_tower_preview_build_loop(group, ring.points)) + continue; + prime_tower_preview_prepare_ring(ring, angle_offset_deg); + if (ring.total_length > prime_tower_preview_epsilon) + rings.emplace_back(std::move(ring)); + } + return rings; +} + +PrimeTowerPreviewRing prime_tower_preview_interpolate_ring(const PrimeTowerPreviewRing &lower, + const PrimeTowerPreviewRing &upper, + float z, + float angle_offset_deg) +{ + PrimeTowerPreviewRing ring; + ring.z = z; + const float t = std::clamp((z - lower.z) / (upper.z - lower.z), 0.f, 1.f); + ring.points.reserve(lower.points.size()); + for (size_t i = 0; i < lower.points.size(); ++i) + ring.points.emplace_back(lower.points[i] + (upper.points[i] - lower.points[i]) * t); + prime_tower_preview_prepare_ring(ring, angle_offset_deg); + return ring; +} + +std::vector prime_tower_preview_insert_z_cuts(const std::vector &rings, + float texture_z_min, + float texture_z_max, + float angle_offset_deg) +{ + if (rings.size() < 2) + return rings; + + std::vector out; + for (size_t i = 0; i + 1 < rings.size(); ++i) { + const PrimeTowerPreviewRing &lower = rings[i]; + const PrimeTowerPreviewRing &upper = rings[i + 1]; + if (lower.points.size() != upper.points.size() || upper.z - lower.z <= prime_tower_preview_epsilon) + return rings; + + if (out.empty()) + out.emplace_back(lower); + + std::vector cuts; + if (texture_z_min > lower.z + prime_tower_preview_coord_epsilon && texture_z_min < upper.z - prime_tower_preview_coord_epsilon) + cuts.emplace_back(texture_z_min); + if (texture_z_max > lower.z + prime_tower_preview_coord_epsilon && texture_z_max < upper.z - prime_tower_preview_coord_epsilon) + cuts.emplace_back(texture_z_max); + std::sort(cuts.begin(), cuts.end()); + cuts.erase(std::unique(cuts.begin(), cuts.end(), [](float lhs, float rhs) { + return std::abs(lhs - rhs) <= prime_tower_preview_coord_epsilon; + }), cuts.end()); + + for (const float cut : cuts) + out.emplace_back(prime_tower_preview_interpolate_ring(lower, upper, cut, angle_offset_deg)); + out.emplace_back(upper); + } + return out; +} + +void prime_tower_preview_add_raw_u_cuts(std::vector &cuts, float raw0, float raw1, int image_slot) +{ + if (std::abs(raw1 - raw0) <= prime_tower_preview_epsilon) + return; + const float boundary_step = image_slot == 0 ? 1.f : 0.5f; + const float raw_min = std::min(raw0, raw1); + const float raw_max = std::max(raw0, raw1); + const int first = int(std::floor(raw_min / boundary_step)) - 1; + const int last = int(std::ceil(raw_max / boundary_step)) + 1; + for (int boundary_idx = first; boundary_idx <= last; ++boundary_idx) { + const float boundary = float(boundary_idx) * boundary_step; + const float t = (boundary - raw0) / (raw1 - raw0); + if (t > prime_tower_preview_epsilon && t < 1.f - prime_tower_preview_epsilon) + cuts.emplace_back(t); + } +} + +float prime_tower_preview_texture_u_from_raw(float raw_u, float mid_raw_u, int image_slot) +{ + const float base = std::floor(mid_raw_u); + if (image_slot == 0) + return std::clamp(raw_u - base, 0.f, 1.f); + return image_slot == 1 ? std::clamp(2.f * (raw_u - base), 0.f, 1.f) : + std::clamp(2.f * (raw_u - base - 0.5f), 0.f, 1.f); +} + +GUI::GLModel::Geometry prime_tower_mesh_image_preview_geometry(const TriangleMesh &mesh, + float angle_offset_deg, + float texture_z_min, + float texture_z_max, + int image_slot) +{ + GUI::GLModel::Geometry data; + data.format = {GUI::GLModel::Geometry::EPrimitiveType::Triangles, GUI::GLModel::Geometry::EVertexLayout::P3N3T2}; + + std::vector rings = + prime_tower_preview_insert_z_cuts(prime_tower_preview_extract_mesh_rings(mesh, angle_offset_deg), + texture_z_min, + texture_z_max, + angle_offset_deg); + if (rings.size() < 2) + return data; + + data.reserve_vertices(mesh.its.indices.size() * 6); + data.reserve_indices(mesh.its.indices.size() * 6); + + for (size_t ring_idx = 0; ring_idx + 1 < rings.size(); ++ring_idx) { + const PrimeTowerPreviewRing &lower = rings[ring_idx]; + const PrimeTowerPreviewRing &upper = rings[ring_idx + 1]; + if (lower.points.size() != upper.points.size() || lower.points.size() < 3) + continue; + + const size_t point_count = lower.points.size(); + for (size_t point_idx = 0; point_idx < point_count; ++point_idx) { + const size_t next_idx = (point_idx + 1) % point_count; + const Vec2f lower_a = lower.points[point_idx]; + const Vec2f lower_b = lower.points[next_idx]; + const Vec2f upper_a = upper.points[point_idx]; + const Vec2f upper_b = upper.points[next_idx]; + const float lower_len = (lower_b - lower_a).norm(); + const float upper_len = (upper_b - upper_a).norm(); + if (lower_len <= prime_tower_preview_epsilon && upper_len <= prime_tower_preview_epsilon) + continue; + + const float lower_raw0 = (lower.distances[point_idx] - lower.anchor_distance) / lower.total_length; + const float lower_raw1 = (lower.distances[point_idx] + lower_len - lower.anchor_distance) / lower.total_length; + const float upper_raw0 = (upper.distances[point_idx] - upper.anchor_distance) / upper.total_length; + const float upper_raw1 = (upper.distances[point_idx] + upper_len - upper.anchor_distance) / upper.total_length; + + std::vector cuts = {0.f, 1.f}; + prime_tower_preview_add_raw_u_cuts(cuts, lower_raw0, lower_raw1, image_slot); + prime_tower_preview_add_raw_u_cuts(cuts, upper_raw0, upper_raw1, image_slot); + std::sort(cuts.begin(), cuts.end()); + cuts.erase(std::unique(cuts.begin(), cuts.end(), [](float lhs, float rhs) { + return std::abs(lhs - rhs) <= prime_tower_preview_epsilon; + }), cuts.end()); + + for (size_t cut_idx = 0; cut_idx + 1 < cuts.size(); ++cut_idx) { + const float t0 = cuts[cut_idx]; + const float t1 = cuts[cut_idx + 1]; + if (t1 - t0 <= prime_tower_preview_epsilon) + continue; + + const float mid_t = 0.5f * (t0 + t1); + const float lower_mid_raw = lower_raw0 + (lower_raw1 - lower_raw0) * mid_t; + const float upper_mid_raw = upper_raw0 + (upper_raw1 - upper_raw0) * mid_t; + const float mid_raw = 0.5f * (lower_mid_raw + upper_mid_raw); + if (image_slot != 0) { + const float mid_u = mid_raw - std::floor(mid_raw); + if ((mid_u >= 0.5f ? 2 : 1) != image_slot) + continue; + } + + const Vec2f lower_p0 = lower_a + (lower_b - lower_a) * t0; + const Vec2f lower_p1 = lower_a + (lower_b - lower_a) * t1; + const Vec2f upper_p0 = upper_a + (upper_b - upper_a) * t0; + const Vec2f upper_p1 = upper_a + (upper_b - upper_a) * t1; + + Vec3f p0(lower_p0.x(), lower_p0.y(), lower.z); + Vec3f p1(lower_p1.x(), lower_p1.y(), lower.z); + Vec3f p2(upper_p1.x(), upper_p1.y(), upper.z); + Vec3f p3(upper_p0.x(), upper_p0.y(), upper.z); + Vec3f normal = (p1 - p0).cross(p3 - p0); + if (normal.norm() <= prime_tower_preview_epsilon) + continue; + normal.normalize(); + const Vec3f offset = normal * prime_tower_preview_offset; + p0 += offset; + p1 += offset; + p2 += offset; + p3 += offset; + + const float lower_raw_t0 = lower_raw0 + (lower_raw1 - lower_raw0) * t0; + const float lower_raw_t1 = lower_raw0 + (lower_raw1 - lower_raw0) * t1; + const float upper_raw_t0 = upper_raw0 + (upper_raw1 - upper_raw0) * t0; + const float upper_raw_t1 = upper_raw0 + (upper_raw1 - upper_raw0) * t1; + const float u0 = prime_tower_preview_texture_u_from_raw(lower_raw_t0, mid_raw, image_slot); + const float u1 = prime_tower_preview_texture_u_from_raw(lower_raw_t1, mid_raw, image_slot); + const float u2 = prime_tower_preview_texture_u_from_raw(upper_raw_t1, mid_raw, image_slot); + const float u3 = prime_tower_preview_texture_u_from_raw(upper_raw_t0, mid_raw, image_slot); + const float v0 = prime_tower_preview_texture_v(lower.z, texture_z_min, texture_z_max); + const float v1 = prime_tower_preview_texture_v(upper.z, texture_z_min, texture_z_max); + const unsigned int base = unsigned(data.vertices_count()); + + data.add_vertex(p0, normal, Vec2f(u0, v0)); + data.add_vertex(p1, normal, Vec2f(u1, v0)); + data.add_vertex(p2, normal, Vec2f(u2, v1)); + data.add_vertex(p3, normal, Vec2f(u3, v1)); + data.add_triangle(base, base + 1, base + 2); + data.add_triangle(base, base + 2, base + 3); + } + } + } + + return data; +} + void set_prime_tower_preview_uniforms(GLShaderProgram &shader, const Transform3d &model_matrix, const Transform3d &view_matrix, @@ -1327,6 +1711,61 @@ void GLWipeTowerVolume::set_prime_tower_image_preview(std::vector } } +void GLWipeTowerVolume::set_prime_tower_image_preview(std::vector image_rgba, + unsigned int image_width, + unsigned int image_height, + std::vector image_rgba_back, + unsigned int image_width_back, + unsigned int image_height_back, + float angle_offset_deg, + const TriangleMesh &mesh, + float texture_z_min, + float texture_z_max) +{ + auto reset_image = [](PrimeTowerPreviewImage &image) { + image.model.reset(); + image.texture.reset(); + image.rgba.clear(); + image.width = 0; + image.height = 0; + }; + auto valid_image = [](const std::vector &rgba, unsigned int width, unsigned int height) { + return width > 0 && height > 0 && rgba.size() >= size_t(width) * size_t(height) * 4; + }; + auto assign_image = [&](PrimeTowerPreviewImage &target, + std::vector rgba, + unsigned int image_w, + unsigned int image_h, + int image_slot) { + if (!valid_image(rgba, image_w, image_h)) + return; + + GUI::GLModel::Geometry image_geometry = + prime_tower_mesh_image_preview_geometry(mesh, angle_offset_deg, texture_z_min, texture_z_max, image_slot); + if (image_geometry.is_empty()) + return; + + target.rgba = std::move(rgba); + target.width = image_w; + target.height = image_h; + target.model.init_from(std::move(image_geometry)); + }; + + reset_image(m_prime_tower_image); + reset_image(m_prime_tower_image_back); + + const bool front_valid = valid_image(image_rgba, image_width, image_height); + const bool back_valid = valid_image(image_rgba_back, image_width_back, image_height_back); + if (front_valid && back_valid) { + assign_image(m_prime_tower_image, std::move(image_rgba), image_width, image_height, 1); + assign_image(m_prime_tower_image_back, std::move(image_rgba_back), image_width_back, image_height_back, 2); + } else if (front_valid) { + assign_image(m_prime_tower_image, std::move(image_rgba), image_width, image_height, 0); + } else if (back_valid) { + assign_image(m_prime_tower_image_back, std::move(image_rgba_back), image_width_back, image_height_back, 0); + } +} + void GLWipeTowerVolume::render() { if (!is_active) @@ -1662,8 +2101,17 @@ int GLVolumeCollection::load_wipe_tower_preview( return int(volumes.size() - 1); } -int GLVolumeCollection::load_real_wipe_tower_preview( - int obj_idx, float pos_x, float pos_y, const TriangleMesh& wt_mesh,const TriangleMesh &brim_mesh,bool render_brim, float rotation_angle, bool size_unknown, bool opengl_initialized) +int GLVolumeCollection::load_real_wipe_tower_preview(int obj_idx, + float pos_x, + float pos_y, + const TriangleMesh &wt_mesh, + const TriangleMesh &brim_mesh, + bool render_brim, + float rotation_angle, + bool size_unknown, + bool opengl_initialized, + float texture_z_min, + float texture_z_max) { int plate_idx = obj_idx - 1000; if (wt_mesh.its.vertices.empty()) return int(this->volumes.size() - 1); @@ -1689,6 +2137,30 @@ int GLVolumeCollection::load_real_wipe_tower_preview( v.model_per_colors.resize(1); v.model_per_colors[0].init_from(mesh); } + const TextureMappingGlobalSettings *texture_mapping_global_settings = GUI::wxGetApp().preset_bundle != nullptr ? + &GUI::wxGetApp().preset_bundle->texture_mapping_global_settings : + nullptr; + const TextureMappingPrimeTowerImage &prime_tower_image = GUI::wxGetApp().model().texture_mapping_prime_tower_image; + const TextureMappingPrimeTowerImage &prime_tower_image_back = GUI::wxGetApp().model().texture_mapping_prime_tower_image_back; + if (texture_mapping_global_settings != nullptr && + texture_mapping_global_settings->effective_enabled(prime_tower_image, prime_tower_image_back)) { + const BoundingBoxf3 mesh_bbox = wt_mesh.bounding_box(); + const bool valid_texture_z_range = texture_z_max > texture_z_min + prime_tower_preview_epsilon; + const float resolved_texture_z_min = valid_texture_z_range ? texture_z_min : mesh_bbox.min.z(); + const float resolved_texture_z_max = valid_texture_z_range ? texture_z_max : mesh_bbox.max.z(); + std::vector texture_data(prime_tower_image.rgba.begin(), prime_tower_image.rgba.end()); + std::vector texture_data_back(prime_tower_image_back.rgba.begin(), prime_tower_image_back.rgba.end()); + v.set_prime_tower_image_preview(std::move(texture_data), + prime_tower_image.width, + prime_tower_image.height, + std::move(texture_data_back), + prime_tower_image_back.width, + prime_tower_image_back.height, + texture_mapping_global_settings->angle_offset_deg, + wt_mesh, + resolved_texture_z_min, + resolved_texture_z_max); + } TriangleMesh wipe_tower_shell = mesh.convex_hull_3d(); v.model.init_from(wipe_tower_shell); v.mesh_raycaster = std::make_unique(std::make_shared(wipe_tower_shell)); diff --git a/src/slic3r/GUI/3DScene.hpp b/src/slic3r/GUI/3DScene.hpp index 51ef252b71f..39827a689f1 100644 --- a/src/slic3r/GUI/3DScene.hpp +++ b/src/slic3r/GUI/3DScene.hpp @@ -414,6 +414,16 @@ public: float height, float texture_z_min, float texture_z_max); + void set_prime_tower_image_preview(std::vector image_rgba, + unsigned int image_width, + unsigned int image_height, + std::vector image_rgba_back, + unsigned int image_width_back, + unsigned int image_height_back, + float angle_offset_deg, + const TriangleMesh &mesh, + float texture_z_min, + float texture_z_max); void render_prime_tower_image_preview(const Transform3d& view_matrix, const Transform3d& projection_matrix, const std::array& z_range, @@ -547,8 +557,17 @@ public: float brim_width, float texture_z_min = 0.f, float texture_z_max = 0.f); - int load_real_wipe_tower_preview( - int obj_idx, float pos_x, float pos_y,const TriangleMesh& wt_mesh,const TriangleMesh &brim_mesh,bool render_brim, float rotation_angle, bool size_unknown, bool opengl_initialized); + int load_real_wipe_tower_preview(int obj_idx, + float pos_x, + float pos_y, + const TriangleMesh &wt_mesh, + const TriangleMesh &brim_mesh, + bool render_brim, + float rotation_angle, + bool size_unknown, + bool opengl_initialized, + float texture_z_min = 0.f, + float texture_z_max = 0.f); GLVolume* new_toolpath_volume(const ColorRGBA& rgba); GLVolume* new_nontoolpath_volume(const ColorRGBA& rgba); diff --git a/src/slic3r/GUI/GLCanvas3D.cpp b/src/slic3r/GUI/GLCanvas3D.cpp index 2c2381cacf1..b0300250500 100644 --- a/src/slic3r/GUI/GLCanvas3D.cpp +++ b/src/slic3r/GUI/GLCanvas3D.cpp @@ -2921,10 +2921,29 @@ void GLCanvas3D::reload_scene(bool refresh_immediately, bool force_full_scene_re BoundingBoxf3 plate_bbox = wxGetApp().plater()->get_partplate_list().get_plate(plate_id)->get_build_volume(true); BoundingBox plate_bbox2d = BoundingBox(scaled(Vec2f(plate_bbox.min[0], plate_bbox.min[1])), scaled(Vec2f(plate_bbox.max[0], plate_bbox.max[1]))); Vec2f offset = WipeTower::move_box_inside_box(tower_bottom_bbox, plate_bbox2d, scaled(margin)); - int volume_idx_wipe_tower_new = m_volumes.load_real_wipe_tower_preview(1000 + plate_id, x + plate_origin(0), y + plate_origin(1), - current_print->wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh, - current_print->wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh, - true,a,/*!print->is_step_done(psWipeTower)*/ true, m_initialized); + float real_texture_z_min = texture_z_min; + float real_texture_z_max = texture_z_max; + bool have_texture_z = false; + for (const LayerTools &layer_tools : current_print->wipe_tower_data().tool_ordering.layer_tools()) { + if (!layer_tools.has_wipe_tower) + continue; + const float print_z = float(layer_tools.print_z); + real_texture_z_min = have_texture_z ? std::min(real_texture_z_min, print_z) : print_z; + real_texture_z_max = have_texture_z ? std::max(real_texture_z_max, print_z) : print_z; + have_texture_z = true; + } + int volume_idx_wipe_tower_new = m_volumes.load_real_wipe_tower_preview( + 1000 + plate_id, + x + plate_origin(0), + y + plate_origin(1), + current_print->wipe_tower_data().wipe_tower_mesh_data->real_wipe_tower_mesh, + current_print->wipe_tower_data().wipe_tower_mesh_data->real_brim_mesh, + true, + a, + /*!print->is_step_done(psWipeTower)*/ true, + m_initialized, + real_texture_z_min, + real_texture_z_max); int volume_idx_wipe_tower_old = volume_idxs_wipe_tower_old[plate_id]; if (volume_idx_wipe_tower_old != -1) map_glvolume_old_to_new[volume_idx_wipe_tower_old] = volume_idx_wipe_tower_new; }