diff --git a/src/libslic3r/Model.cpp b/src/libslic3r/Model.cpp index 5273e165395..061fa285f63 100644 --- a/src/libslic3r/Model.cpp +++ b/src/libslic3r/Model.cpp @@ -4307,6 +4307,78 @@ static void color_facets_append_sampled_triangle(TriangleColorSplittingData data.colors_rgba.emplace_back(cc); } +static bool color_facets_append_existing_or_sampled_triangle( + const TriangleColorSplittingData &old_data, + TriangleColorSplittingData &new_data, + const TextureMappingColorSampler &sampler, + const TextureMappingColorLeafResamplePredicate &resample_leaf, + size_t source_triangle, + int bitstream_end, + size_t color_end, + int &bit_idx, + size_t &color_idx, + const std::array &vertices, + const std::array &barycentrics, + int depth, + int min_depth, + int max_depth, + float split_color_threshold) +{ + if (bit_idx + 3 >= bitstream_end) + return false; + + int code = 0; + for (int bit = 0; bit < 4; ++bit) + code |= int(old_data.bitstream[size_t(bit_idx++)]) << bit; + + const int split_sides = code & 0b11; + if (split_sides == 0) { + if (color_idx >= color_end || color_idx >= old_data.colors_rgba.size()) + return false; + + const uint32_t rgba = old_data.colors_rgba[color_idx++]; + if (resample_leaf && resample_leaf(source_triangle, vertices, barycentrics, rgba)) { + color_facets_append_sampled_triangle(new_data, + sampler, + source_triangle, + vertices, + barycentrics, + depth, + min_depth, + max_depth, + split_color_threshold); + } else { + color_facets_append_nibble(new_data.bitstream, 0u); + new_data.colors_rgba.emplace_back(rgba); + } + return true; + } + + const int special_side = (code >> 2) & 0b11; + color_facets_append_nibble(new_data.bitstream, unsigned(code)); + const std::array, 4> child_vertices = color_facets_split_triangle(vertices, split_sides, special_side); + const std::array, 4> child_barycentrics = color_facets_split_triangle(barycentrics, split_sides, special_side); + for (int child_idx = split_sides; child_idx >= 0; --child_idx) { + if (!color_facets_append_existing_or_sampled_triangle(old_data, + new_data, + sampler, + resample_leaf, + source_triangle, + bitstream_end, + color_end, + bit_idx, + color_idx, + child_vertices[size_t(child_idx)], + child_barycentrics[size_t(child_idx)], + depth + 1, + min_depth, + max_depth, + split_color_threshold)) + return false; + } + return true; +} + static void color_facets_extract_triangle(const TriangleColorSplittingData &data, int bitstream_end, size_t color_end, @@ -4445,12 +4517,18 @@ bool ColorFacetsAnnotation::set_from_triangle_sampler(const ModelVolume const TextureMappingColorSampler &sampler, int max_depth, float split_color_threshold, - const TextureMappingColorSubdivisionDepths &subdivision_depths) + const TextureMappingColorSubdivisionDepths &subdivision_depths, + const std::vector *resample_triangles, + const TextureMappingColorLeafResamplePredicate &resample_leaf) { TriangleColorSplittingData new_data; new_data.metadata_json = m_data.metadata_json; const indexed_triangle_set &its = mv.mesh().its; new_data.triangles_to_split.reserve(its.indices.size()); + if (resample_triangles != nullptr) { + new_data.bitstream.reserve(m_data.bitstream.size()); + new_data.colors_rgba.reserve(m_data.colors_rgba.size()); + } const std::array root_barycentrics = { Vec3f(1.f, 0.f, 0.f), @@ -4461,6 +4539,60 @@ bool ColorFacetsAnnotation::set_from_triangle_sampler(const ModelVolume max_depth = std::clamp(max_depth, 0, 7); split_color_threshold = std::max(split_color_threshold, 0.f); + size_t preserved_mapping_idx = 0; + auto existing_triangle_range = [this, &preserved_mapping_idx](size_t tri_idx, + int &bitstream_start, + int &bitstream_end, + int &color_start, + size_t &color_end) { + while (preserved_mapping_idx < m_data.triangles_to_split.size() && + m_data.triangles_to_split[preserved_mapping_idx].triangle_idx < int(tri_idx)) + ++preserved_mapping_idx; + + if (preserved_mapping_idx >= m_data.triangles_to_split.size() || + m_data.triangles_to_split[preserved_mapping_idx].triangle_idx != int(tri_idx)) + return false; + + const auto mapping_it = m_data.triangles_to_split.begin() + preserved_mapping_idx; + const auto next_it = std::next(mapping_it); + bitstream_start = mapping_it->bitstream_start_idx; + bitstream_end = next_it == m_data.triangles_to_split.end() ? + int(m_data.bitstream.size()) : + next_it->bitstream_start_idx; + color_start = mapping_it->color_start_idx; + color_end = next_it == m_data.triangles_to_split.end() ? + m_data.colors_rgba.size() : + size_t(next_it->color_start_idx); + + if (bitstream_start < 0 || + bitstream_start >= bitstream_end || + size_t(bitstream_end) > m_data.bitstream.size() || + color_start < 0 || + size_t(color_start) >= color_end || + color_end > m_data.colors_rgba.size()) + return false; + + return true; + }; + + auto append_preserved_triangle = [this, &new_data, &existing_triangle_range](size_t tri_idx) { + int bitstream_start = 0; + int bitstream_end = 0; + int color_start = 0; + size_t color_end = 0; + if (!existing_triangle_range(tri_idx, bitstream_start, bitstream_end, color_start, color_end)) + return false; + + new_data.triangles_to_split.emplace_back(int(tri_idx), int(new_data.bitstream.size()), int(new_data.colors_rgba.size())); + new_data.bitstream.insert(new_data.bitstream.end(), + m_data.bitstream.begin() + bitstream_start, + m_data.bitstream.begin() + bitstream_end); + new_data.colors_rgba.insert(new_data.colors_rgba.end(), + m_data.colors_rgba.begin() + color_start, + m_data.colors_rgba.begin() + color_end); + return true; + }; + for (size_t tri_idx = 0; tri_idx < its.indices.size(); ++tri_idx) { const auto &tri = its.indices[tri_idx]; if (tri[0] < 0 || tri[1] < 0 || tri[2] < 0) @@ -4476,6 +4608,16 @@ bool ColorFacetsAnnotation::set_from_triangle_sampler(const ModelVolume its.vertices[size_t(tri[2])].cast() }; + const bool should_resample_triangle = + resample_triangles == nullptr || + tri_idx >= resample_triangles->size() || + (*resample_triangles)[tri_idx]; + if (resample_triangles != nullptr && + tri_idx < resample_triangles->size() && + !(*resample_triangles)[tri_idx] && + append_preserved_triangle(tri_idx)) + continue; + int triangle_min_depth = 0; int triangle_max_depth = max_depth; if (subdivision_depths) { @@ -4487,6 +4629,36 @@ bool ColorFacetsAnnotation::set_from_triangle_sampler(const ModelVolume } new_data.triangles_to_split.emplace_back(int(tri_idx), int(new_data.bitstream.size()), int(new_data.colors_rgba.size())); + if (resample_leaf && should_resample_triangle) { + int bitstream_start = 0; + int bitstream_end = 0; + int color_start = 0; + size_t color_end = 0; + if (existing_triangle_range(tri_idx, bitstream_start, bitstream_end, color_start, color_end)) { + int bit_idx = bitstream_start; + size_t color_idx = size_t(color_start); + const size_t new_bitstream_start = new_data.bitstream.size(); + const size_t new_color_start = new_data.colors_rgba.size(); + if (color_facets_append_existing_or_sampled_triangle(m_data, + new_data, + sampler, + resample_leaf, + tri_idx, + bitstream_end, + color_end, + bit_idx, + color_idx, + vertices, + root_barycentrics, + 0, + triangle_min_depth, + triangle_max_depth, + split_color_threshold)) + continue; + new_data.bitstream.resize(new_bitstream_start); + new_data.colors_rgba.resize(new_color_start); + } + } color_facets_append_sampled_triangle(new_data, sampler, tri_idx, @@ -4666,12 +4838,29 @@ bool model_mmu_segmentation_data_changed(const ModelObject& mo, const ModelObjec }); } +template +static bool model_volume_imported_vector_matches(const ModelVolumeImportedVector &lhs, const ModelVolumeImportedVector &rhs) +{ + return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); +} + +static bool model_volume_texture_mapping_data_matches(const ModelVolume &mv_old, const ModelVolume &mv_new) +{ + return mv_old.texture_mapping_color_facets.timestamp_matches(mv_new.texture_mapping_color_facets) && + model_volume_imported_vector_matches(mv_old.imported_vertex_colors_rgba, mv_new.imported_vertex_colors_rgba) && + model_volume_imported_vector_matches(mv_old.imported_texture_uvs_per_face, mv_new.imported_texture_uvs_per_face) && + model_volume_imported_vector_matches(mv_old.imported_texture_uv_valid, mv_new.imported_texture_uv_valid) && + model_volume_imported_vector_matches(mv_old.imported_texture_rgba, mv_new.imported_texture_rgba) && + mv_old.imported_texture_width == mv_new.imported_texture_width && + mv_old.imported_texture_height == mv_new.imported_texture_height; +} + bool model_texture_mapping_color_data_changed(const ModelObject& mo, const ModelObject& mo_new) { return model_property_changed(mo, mo_new, [](const ModelVolumeType t) { return t == ModelVolumeType::MODEL_PART; }, [](const ModelVolume &mv_old, const ModelVolume &mv_new) { - return mv_old.texture_mapping_color_facets.timestamp_matches(mv_new.texture_mapping_color_facets); + return model_volume_texture_mapping_data_matches(mv_old, mv_new); }); } diff --git a/src/libslic3r/Model.hpp b/src/libslic3r/Model.hpp index 44834fa961e..6e605b6d526 100644 --- a/src/libslic3r/Model.hpp +++ b/src/libslic3r/Model.hpp @@ -846,6 +846,8 @@ struct ColorFacetTriangle using TextureMappingColorSampler = std::function; using TextureMappingColorSubdivisionDepths = std::function(size_t, const std::array &)>; +using TextureMappingColorLeafResamplePredicate = + std::function &, const std::array &, uint32_t)>; class ColorFacetsAnnotation final : public ObjectWithTimestamp { public: @@ -886,7 +888,9 @@ public: const TextureMappingColorSampler &sampler, int max_depth = 2, float split_color_threshold = 0.045f, - const TextureMappingColorSubdivisionDepths &subdivision_depths = {}); + const TextureMappingColorSubdivisionDepths &subdivision_depths = {}, + const std::vector *resample_triangles = nullptr, + const TextureMappingColorLeafResamplePredicate &resample_leaf = {}); void get_facet_triangles(const ModelVolume &mv, std::vector &facets) const; private: diff --git a/src/libslic3r/PrintApply.cpp b/src/libslic3r/PrintApply.cpp index 00b790d497b..71c5c5f55c1 100644 --- a/src/libslic3r/PrintApply.cpp +++ b/src/libslic3r/PrintApply.cpp @@ -80,6 +80,12 @@ static inline void model_volume_list_copy_configs(ModelObject &model_object_dst, mv_dst.mmu_segmentation_facets.assign(mv_src.mmu_segmentation_facets); assert(mv_dst.texture_mapping_color_facets.id() == mv_src.texture_mapping_color_facets.id()); mv_dst.texture_mapping_color_facets.assign(mv_src.texture_mapping_color_facets); + mv_dst.imported_vertex_colors_rgba = mv_src.imported_vertex_colors_rgba; + mv_dst.imported_texture_uvs_per_face = mv_src.imported_texture_uvs_per_face; + mv_dst.imported_texture_uv_valid = mv_src.imported_texture_uv_valid; + mv_dst.imported_texture_rgba = mv_src.imported_texture_rgba; + mv_dst.imported_texture_width = mv_src.imported_texture_width; + mv_dst.imported_texture_height = mv_src.imported_texture_height; assert(mv_dst.fuzzy_skin_facets.id() == mv_src.fuzzy_skin_facets.id()); mv_dst.fuzzy_skin_facets.assign(mv_src.fuzzy_skin_facets); //FIXME what to do with the materials? diff --git a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp index f8f5012c67e..c46f2f5b1fa 100644 --- a/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp +++ b/src/slic3r/GUI/Gizmos/GLGizmoMmuSegmentation.cpp @@ -706,6 +706,36 @@ static float distance_between_segments(const Vec3f &p1, const Vec3f &q1, const V return (p1 + d1 * s - (p2 + d2 * t)).norm(); } +static bool rgb_point_in_triangle(const Vec3f &point, const std::array &triangle) +{ + Vec3f weights = Vec3f::Zero(); + const float tolerance = -1e-4f; + return barycentric_weights_for_region_vertex_colors(point, triangle[0], triangle[1], triangle[2], weights) && + weights.x() >= tolerance && + weights.y() >= tolerance && + weights.z() >= tolerance; +} + +static bool rgb_triangles_overlap(const std::array &lhs, const std::array &rhs) +{ + for (const Vec3f &point : lhs) + if (rgb_point_in_triangle(point, rhs)) + return true; + for (const Vec3f &point : rhs) + if (rgb_point_in_triangle(point, lhs)) + return true; + + const float edge_tolerance = 1e-4f; + for (size_t lhs_idx = 0; lhs_idx < 3; ++lhs_idx) + for (size_t rhs_idx = 0; rhs_idx < 3; ++rhs_idx) + if (distance_between_segments(lhs[lhs_idx], + lhs[(lhs_idx + 1) % 3], + rhs[rhs_idx], + rhs[(rhs_idx + 1) % 3]) <= edge_tolerance) + return true; + return false; +} + static Vec3f transform_point(const Transform3d &matrix, const Vec3f &point) { return (matrix * point.cast()).cast(); @@ -963,27 +993,43 @@ static std::vector rgb_brush_candidate_source_triangles( return candidates; } -static int rgb_leaf_count_depth(size_t leaf_count) +static int rgb_color_tree_max_depth(const TriangleColorSplittingData &data, int bitstream_end, int &bit_idx, int depth) { - int depth = 0; - size_t depth_leaf_count = 1; - while (depth_leaf_count < leaf_count && depth < 7) { - depth_leaf_count *= 4; - ++depth; - } - return depth; + if (bit_idx + 3 >= bitstream_end) + return std::clamp(depth, 0, 7); + + int code = 0; + for (int bit = 0; bit < 4; ++bit) + code |= int(data.bitstream[size_t(bit_idx++)]) << bit; + + const int split_sides = code & 0b11; + if (split_sides == 0) + return std::clamp(depth, 0, 7); + + int max_depth = std::clamp(depth + 1, 0, 7); + for (int child_idx = split_sides; child_idx >= 0; --child_idx) + max_depth = std::max(max_depth, rgb_color_tree_max_depth(data, bitstream_end, bit_idx, depth + 1)); + return std::clamp(max_depth, 0, 7); } -static std::vector rgb_existing_source_triangle_depths(const std::vector &facets, size_t triangle_count) +static std::vector rgb_existing_source_triangle_depths(const TriangleColorSplittingData &data, size_t triangle_count) { - std::vector leaf_counts(triangle_count, 0); - for (const ColorFacetTriangle &facet : facets) - if (facet.source_triangle >= 0 && size_t(facet.source_triangle) < leaf_counts.size()) - ++leaf_counts[size_t(facet.source_triangle)]; - std::vector depths(triangle_count, 0); - for (size_t tri_idx = 0; tri_idx < leaf_counts.size(); ++tri_idx) - depths[tri_idx] = rgb_leaf_count_depth(leaf_counts[tri_idx]); + for (auto mapping_it = data.triangles_to_split.begin(); mapping_it != data.triangles_to_split.end(); ++mapping_it) { + if (mapping_it->triangle_idx < 0 || size_t(mapping_it->triangle_idx) >= depths.size()) + continue; + + const auto next_it = std::next(mapping_it); + const int bitstream_start = mapping_it->bitstream_start_idx; + const int bitstream_end = next_it == data.triangles_to_split.end() ? + int(data.bitstream.size()) : + next_it->bitstream_start_idx; + if (bitstream_start < 0 || bitstream_start >= bitstream_end || size_t(bitstream_end) > data.bitstream.size()) + continue; + + int bit_idx = bitstream_start; + depths[size_t(mapping_it->triangle_idx)] = rgb_color_tree_max_depth(data, bitstream_end, bit_idx, 0); + } return depths; } @@ -1076,7 +1122,7 @@ static bool apply_rgb_stroke_to_volume(ModelVolume const int safe_max_depth = texture_mapping_depth_for_budget(volume.mesh().its.indices.size(), 7, 1800000); const float brush_subdivision_target = true_color_brush_subdivision_target(brush_radius); const std::vector existing_source_triangle_depths = - rgb_existing_source_triangle_depths(existing_facets, volume.mesh().its.indices.size()); + rgb_existing_source_triangle_depths(volume.texture_mapping_color_facets.get_data(), volume.mesh().its.indices.size()); TextureMappingColorSubdivisionDepths subdivision_depths = [mesh_span, safe_max_depth, @@ -1103,7 +1149,49 @@ static bool apply_rgb_stroke_to_volume(ModelVolume return std::make_pair(min_depth, max_depth); }; - return volume.texture_mapping_color_facets.set_from_triangle_sampler(volume, sampler, safe_max_depth, 0.012f, subdivision_depths); + TextureMappingColorLeafResamplePredicate resample_leaf = + [opacity, + brush_radius, + use_brush_path, + &brush_stroke_points_world, + &world_matrix, + &stroke_by_source_triangle, + &stroke_facets](size_t tri_idx, const std::array &vertices, const std::array &, uint32_t) { + if (opacity <= 0.f || brush_radius <= EPSILON) + return false; + + if (use_brush_path) { + const std::array world_vertices = transform_triangle(world_matrix, vertices); + for (size_t point_idx = 0; point_idx < brush_stroke_points_world.size(); ++point_idx) { + const Vec3f segment_a = brush_stroke_points_world[point_idx]; + const Vec3f segment_b = point_idx + 1 < brush_stroke_points_world.size() ? + brush_stroke_points_world[point_idx + 1] : + brush_stroke_points_world[point_idx]; + if (triangle_intersects_brush_segment(world_vertices, segment_a, segment_b, brush_radius)) + return true; + } + return false; + } + + auto found = stroke_by_source_triangle.find(int(tri_idx)); + if (found == stroke_by_source_triangle.end()) + return false; + for (const size_t facet_idx : found->second) { + if (facet_idx >= stroke_facets.size()) + continue; + if (rgb_triangles_overlap(vertices, stroke_facets[facet_idx].vertices)) + return true; + } + return false; + }; + + return volume.texture_mapping_color_facets.set_from_triangle_sampler(volume, + sampler, + safe_max_depth, + 0.012f, + subdivision_depths, + &brush_candidate_triangles, + resample_leaf); } static bool build_volume_rgb_data(const ModelVolume &volume, const ColorRGBA &background, ColorFacetsAnnotation &out) @@ -6228,7 +6316,7 @@ bool GLGizmoImageProjection::project_to_vertex_colors(ModelObject *object) const OverlayRect rect = overlay_rect(); ProjectionContext context; - context.view_projection = (camera.get_projection_matrix() * camera.get_view_matrix()).matrix(); + context.view_projection = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); context.canvas_width = std::max(1, viewport[2]); context.canvas_height = std::max(1, viewport[3]); context.overlay_left = rect.left; @@ -6355,7 +6443,7 @@ bool GLGizmoImageProjection::project_to_image_texture(ModelObject *object) const OverlayRect rect = overlay_rect(); ProjectionContext context; - context.view_projection = (camera.get_projection_matrix() * camera.get_view_matrix()).matrix(); + context.view_projection = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); context.canvas_width = std::max(1, viewport[2]); context.canvas_height = std::max(1, viewport[3]); context.overlay_left = rect.left; @@ -6522,7 +6610,7 @@ bool GLGizmoImageProjection::project_to_rgb_data(ModelObject *object) const OverlayRect rect = overlay_rect(); ProjectionContext context; - context.view_projection = (camera.get_projection_matrix() * camera.get_view_matrix()).matrix(); + context.view_projection = camera.get_projection_matrix().matrix() * camera.get_view_matrix().matrix(); context.canvas_width = std::max(1, viewport[2]); context.canvas_height = std::max(1, viewport[3]); context.overlay_left = rect.left;