diff --git a/scripts/startup/bl_ui/properties_data_mesh.py b/scripts/startup/bl_ui/properties_data_mesh.py index f510c37f9bb..61a552bd3dd 100644 --- a/scripts/startup/bl_ui/properties_data_mesh.py +++ b/scripts/startup/bl_ui/properties_data_mesh.py @@ -426,9 +426,7 @@ class DATA_PT_remesh(MeshButtonsPanel, Panel): col = layout.column(heading="Preserve") col.prop(mesh, "use_remesh_preserve_volume", text="Volume") - col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask") - col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets") - col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Color Attributes") + col.prop(mesh, "use_remesh_preserve_attributes", text="Attributes") col.operator("object.voxel_remesh", text="Voxel Remesh") else: diff --git a/scripts/startup/bl_ui/space_view3d_toolbar.py b/scripts/startup/bl_ui/space_view3d_toolbar.py index 1f4f886c5d3..d2ea04f8f14 100644 --- a/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1045,9 +1045,7 @@ class VIEW3D_PT_sculpt_voxel_remesh(Panel, View3DPaintPanel): col = layout.column(heading="Preserve", align=True) col.prop(mesh, "use_remesh_preserve_volume", text="Volume") - col.prop(mesh, "use_remesh_preserve_paint_mask", text="Paint Mask") - col.prop(mesh, "use_remesh_preserve_sculpt_face_sets", text="Face Sets") - col.prop(mesh, "use_remesh_preserve_vertex_colors", text="Color Attributes") + col.prop(mesh, "use_remesh_preserve_attributes", text="Attributes") layout.operator("object.voxel_remesh", text="Remesh") diff --git a/source/blender/blenkernel/BKE_mesh_remesh_voxel.hh b/source/blender/blenkernel/BKE_mesh_remesh_voxel.hh index 0d37e935669..621a0ddcacf 100644 --- a/source/blender/blenkernel/BKE_mesh_remesh_voxel.hh +++ b/source/blender/blenkernel/BKE_mesh_remesh_voxel.hh @@ -21,7 +21,6 @@ Mesh *BKE_mesh_remesh_quadriflow(const Mesh *mesh, void (*update_cb)(void *, float progress, int *cancel), void *update_cb_data); -/* Data reprojection functions */ -void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, const Mesh *source); -void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source); -void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, const Mesh *source); +namespace blender::bke { +void mesh_remesh_reproject_attributes(const Mesh &src, Mesh &dst); +} \ No newline at end of file diff --git a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc index c3b518017b8..ee20022f2a9 100644 --- a/source/blender/blenkernel/intern/mesh_remesh_voxel.cc +++ b/source/blender/blenkernel/intern/mesh_remesh_voxel.cc @@ -17,6 +17,7 @@ #include "BLI_array.hh" #include "BLI_array_utils.hh" +#include "BLI_enumerable_thread_specific.hh" #include "BLI_index_range.hh" #include "BLI_math_vector.h" #include "BLI_span.hh" @@ -277,191 +278,329 @@ Mesh *BKE_mesh_remesh_voxel(const Mesh *mesh, #endif } -void BKE_mesh_remesh_reproject_paint_mask(Mesh *target, const Mesh *source) +namespace blender::bke { + +static void calc_edge_centers(const Span positions, + const Span edges, + MutableSpan edge_centers) { - BVHTreeFromMesh bvhtree = {nullptr}; - BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); - const Span target_positions = target->vert_positions(); - const float *source_mask = (const float *)CustomData_get_layer_named( - &source->vert_data, CD_PROP_FLOAT, ".sculpt_mask"); - if (source_mask == nullptr) { - return; + for (const int i : edges.index_range()) { + edge_centers[i] = math::midpoint(positions[edges[i][0]], positions[edges[i][1]]); } +} - float *target_mask; - if (CustomData_has_layer_named(&target->vert_data, CD_PROP_FLOAT, ".sculpt_mask")) { - target_mask = (float *)CustomData_get_layer_named( - &target->vert_data, CD_PROP_FLOAT, ".sculpt_mask"); - } - else { - target_mask = (float *)CustomData_add_layer_named( - &target->vert_data, CD_PROP_FLOAT, CD_CONSTRUCT, target->totvert, ".sculpt_mask"); +static void calc_face_centers(const Span positions, + const OffsetIndices faces, + const Span corner_verts, + MutableSpan face_centers) +{ + for (const int i : faces.index_range()) { + face_centers[i] = mesh::face_center_calc(positions, corner_verts.slice(faces[i])); } +} - blender::threading::parallel_for(IndexRange(target->totvert), 4096, [&](const IndexRange range) { - for (const int i : range) { - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - BLI_bvhtree_find_nearest( - bvhtree.tree, target_positions[i], &nearest, bvhtree.nearest_callback, &bvhtree); - if (nearest.index != -1) { - target_mask[i] = source_mask[nearest.index]; +static void find_nearest_tris(const Span positions, + BVHTreeFromMesh &bvhtree, + MutableSpan tris) +{ + for (const int i : positions.index_range()) { + BVHTreeNearest nearest; + nearest.index = -1; + nearest.dist_sq = FLT_MAX; + BLI_bvhtree_find_nearest( + bvhtree.tree, positions[i], &nearest, bvhtree.nearest_callback, &bvhtree); + tris[i] = nearest.index; + } +} + +static void find_nearest_tris_parallel(const Span positions, + BVHTreeFromMesh &bvhtree, + MutableSpan tris) +{ + threading::parallel_for(tris.index_range(), 512, [&](const IndexRange range) { + find_nearest_tris(positions.slice(range), bvhtree, tris.slice(range)); + }); +} + +static void find_nearest_verts(const Span positions, + const Span corner_verts, + const Span src_tris, + const Span dst_positions, + const Span nearest_vert_tris, + MutableSpan nearest_verts) +{ + threading::parallel_for(dst_positions.index_range(), 512, [&](const IndexRange range) { + for (const int dst_vert : range) { + const float3 &dst_position = dst_positions[dst_vert]; + const MLoopTri &src_tri = src_tris[nearest_vert_tris[dst_vert]]; + + std::array distances; + for (const int i : IndexRange(3)) { + const int src_vert = corner_verts[src_tri.tri[i]]; + distances[i] = math::distance_squared(positions[src_vert], dst_position); } + + const int min = std::min_element(distances.begin(), distances.end()) - distances.begin(); + nearest_verts[dst_vert] = corner_verts[src_tri.tri[min]]; } }); - free_bvhtree_from_mesh(&bvhtree); } -void BKE_remesh_reproject_sculpt_face_sets(Mesh *target, const Mesh *source) +static void find_nearest_faces(const Span src_tri_faces, + const Span dst_positions, + const OffsetIndices dst_faces, + const Span dst_corner_verts, + BVHTreeFromMesh &bvhtree, + MutableSpan nearest_faces) { - using namespace blender; - using namespace blender::bke; - const AttributeAccessor src_attributes = source->attributes(); - MutableAttributeAccessor dst_attributes = target->attributes_for_write(); - const Span target_positions = target->vert_positions(); - const OffsetIndices target_faces = target->faces(); - const Span target_corner_verts = target->corner_verts(); + struct TLS { + Vector face_centers; + Vector tri_indices; + }; + threading::EnumerableThreadSpecific all_tls; + threading::parallel_for(dst_faces.index_range(), 512, [&](const IndexRange range) { + TLS &tls = all_tls.local(); + Vector &face_centers = tls.face_centers; + face_centers.reinitialize(range.size()); + calc_face_centers(dst_positions, dst_faces.slice(range), dst_corner_verts, face_centers); - const VArray src_face_sets = *src_attributes.lookup(".sculpt_face_set", ATTR_DOMAIN_FACE); - if (!src_face_sets) { - return; - } - SpanAttributeWriter dst_face_sets = dst_attributes.lookup_or_add_for_write_only_span( - ".sculpt_face_set", ATTR_DOMAIN_FACE); - if (!dst_face_sets) { - return; - } + Vector &tri_indices = tls.tri_indices; + tri_indices.reinitialize(range.size()); + find_nearest_tris(face_centers, bvhtree, tri_indices); - const VArraySpan src(src_face_sets); - MutableSpan dst = dst_face_sets.span; - - const blender::Span looptri_faces = source->looptri_faces(); - BVHTreeFromMesh bvhtree = {nullptr}; - BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_LOOPTRI, 2); - - blender::threading::parallel_for( - IndexRange(target->faces_num), 2048, [&](const IndexRange range) { - for (const int i : range) { - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - const float3 from_co = mesh::face_center_calc( - target_positions, target_corner_verts.slice(target_faces[i])); - BLI_bvhtree_find_nearest( - bvhtree.tree, from_co, &nearest, bvhtree.nearest_callback, &bvhtree); - if (nearest.index != -1) { - dst[i] = src[looptri_faces[nearest.index]]; - } - else { - dst[i] = 1; - } - } - }); - free_bvhtree_from_mesh(&bvhtree); - dst_face_sets.finish(); + array_utils::gather(src_tri_faces, tri_indices.as_span(), nearest_faces.slice(range)); + }); } -void BKE_remesh_reproject_vertex_paint(Mesh *target, const Mesh *source) +static void find_nearest_corners(const Span src_positions, + const OffsetIndices src_faces, + const Span src_corner_verts, + const Span src_tri_faces, + const Span dst_positions, + const Span dst_corner_verts, + const Span nearest_vert_tris, + MutableSpan nearest_corners) { - using namespace blender; - using namespace blender::bke; - const AttributeAccessor src_attributes = source->attributes(); - MutableAttributeAccessor dst_attributes = target->attributes_for_write(); + threading::parallel_for(nearest_corners.index_range(), 512, [&](const IndexRange range) { + Vector distances; + for (const int dst_corner : range) { + const int dst_vert = dst_corner_verts[dst_corner]; + const float3 &dst_position = dst_positions[dst_vert]; + const int src_tri = nearest_vert_tris[dst_vert]; + const IndexRange src_face = src_faces[src_tri_faces[src_tri]]; + const Span src_face_verts = src_corner_verts.slice(src_face); + + /* Find the corner in the face that's closest in the closest face. */ + distances.reinitialize(src_face_verts.size()); + for (const int i : src_face_verts.index_range()) { + const int src_vert = src_face_verts[i]; + distances[i] = math::distance_squared(src_positions[src_vert], dst_position); + } + + const int min = std::min_element(distances.begin(), distances.end()) - distances.begin(); + nearest_corners[dst_corner] = src_face[min]; + } + }); +} + +static void find_nearest_edges(const Span src_positions, + const Span src_edges, + const OffsetIndices src_faces, + const Span src_corner_edges, + const Span src_tri_faces, + const Span dst_positions, + const Span dst_edges, + BVHTreeFromMesh &bvhtree, + MutableSpan nearest_edges) +{ + struct TLS { + Vector edge_centers; + Vector tri_indices; + Vector face_indices; + Vector distances; + }; + threading::EnumerableThreadSpecific all_tls; + threading::parallel_for(nearest_edges.index_range(), 512, [&](const IndexRange range) { + TLS &tls = all_tls.local(); + Vector &edge_centers = tls.edge_centers; + edge_centers.reinitialize(range.size()); + calc_edge_centers(dst_positions, dst_edges.slice(range), edge_centers); + + Vector &tri_indices = tls.tri_indices; + tri_indices.reinitialize(range.size()); + find_nearest_tris_parallel(edge_centers, bvhtree, tri_indices); + + Vector &face_indices = tls.face_indices; + face_indices.reinitialize(range.size()); + array_utils::gather(src_tri_faces, tri_indices.as_span(), face_indices.as_mutable_span()); + + /* Find the source edge that's closest to the destination edge in the nearest face. Search + * through the whole face instead of just the triangle because the triangle has edges that + * might not be actual mesh edges. */ + Vector distances; + for (const int i : range.index_range()) { + const int dst_edge = range[i]; + const float3 &dst_position = edge_centers[i]; + + const int src_face = face_indices[i]; + const Span src_face_edges = src_corner_edges.slice(src_faces[src_face]); + + distances.reinitialize(src_face_edges.size()); + for (const int i : src_face_edges.index_range()) { + const int2 src_edge = src_edges[src_face_edges[i]]; + const float3 src_center = math::midpoint(src_positions[src_edge[0]], + src_positions[src_edge[1]]); + distances[i] = math::distance_squared(src_center, dst_position); + } + + const int min = std::min_element(distances.begin(), distances.end()) - distances.begin(); + nearest_edges[dst_edge] = src_face_edges[min]; + } + }); +} + +static void gather_attributes(const Span ids, + const AttributeAccessor src_attributes, + const eAttrDomain domain, + const Span index_map, + MutableAttributeAccessor dst_attributes) +{ + for (const AttributeIDRef &id : ids) { + const GVArraySpan src = *src_attributes.lookup(id, domain); + const eCustomDataType type = cpp_type_to_custom_data_type(src.type()); + GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span(id, domain, type); + attribute_math::gather(src, index_map, dst.span); + dst.finish(); + } +} + +void mesh_remesh_reproject_attributes(const Mesh &src, Mesh &dst) +{ + /* Gather attributes to tranfer for each domain. This makes it possible to skip + * building index maps and even the main BVH tree if there are no attributes. */ + const AttributeAccessor src_attributes = src.attributes(); Vector point_ids; + Vector edge_ids; + Vector face_ids; Vector corner_ids; - source->attributes().for_all([&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { - if (CD_TYPE_AS_MASK(meta_data.data_type) & CD_MASK_COLOR_ALL) { - if (meta_data.domain == ATTR_DOMAIN_POINT) { + src_attributes.for_all([&](const AttributeIDRef &id, const AttributeMetaData &meta_data) { + if (ELEM(id.name(), "position", ".edge_verts", ".corner_vert", ".corner_edge")) { + return true; + } + switch (meta_data.domain) { + case ATTR_DOMAIN_POINT: point_ids.append(id); - } - else if (meta_data.domain == ATTR_DOMAIN_CORNER) { + break; + case ATTR_DOMAIN_EDGE: + edge_ids.append(id); + break; + case ATTR_DOMAIN_FACE: + face_ids.append(id); + break; + case ATTR_DOMAIN_CORNER: corner_ids.append(id); - } + break; + default: + BLI_assert_unreachable(); + break; } return true; }); - if (point_ids.is_empty() && corner_ids.is_empty()) { + if (point_ids.is_empty() && edge_ids.is_empty() && face_ids.is_empty() && corner_ids.is_empty()) + { return; } - GroupedSpan source_lmap; - GroupedSpan target_lmap; - BVHTreeFromMesh bvhtree = {nullptr}; - threading::parallel_invoke( - [&]() { BKE_bvhtree_from_mesh_get(&bvhtree, source, BVHTREE_FROM_VERTS, 2); }, - [&]() { source_lmap = source->vert_to_corner_map(); }, - [&]() { target_lmap = target->vert_to_corner_map(); }); + const Span src_positions = src.vert_positions(); + const OffsetIndices src_faces = src.faces(); + const Span src_corner_verts = src.corner_verts(); + const Span src_tris = src.looptris(); - const Span target_positions = target->vert_positions(); - Array nearest_src_verts(target_positions.size()); - threading::parallel_for(target_positions.index_range(), 1024, [&](const IndexRange range) { - for (const int i : range) { - BVHTreeNearest nearest; - nearest.index = -1; - nearest.dist_sq = FLT_MAX; - BLI_bvhtree_find_nearest( - bvhtree.tree, target_positions[i], &nearest, bvhtree.nearest_callback, &bvhtree); - nearest_src_verts[i] = nearest.index; + /* The main idea in the following code is to trade some complexity in sampling for the benefit of + * only using and building a single BVH tree. Since sculpt mode doesn't generally deal with loose + * vertices and edges, we use the standard "triangles" BVH which won't contain them. Also, only + * relying on a single BVH should reduce memory usage, and work better if the BVH and PBVH are + * ever merged. + * + * One key decision is separating building transfer index maps from actually transferring any + * attribute data. This is important to keep attribute storage independent from the specifics of + * the decisions made here, which mainly results in easier refactoring, more generic code, and + * possibly improved performance from lower cache usage in the "complex" sampling part of the + * algorithm and the copying itself. */ + BVHTreeFromMesh bvhtree{}; + BKE_bvhtree_from_mesh_get(&bvhtree, &src, BVHTREE_FROM_LOOPTRI, 2); + + const Span dst_positions = dst.vert_positions(); + const OffsetIndices dst_faces = dst.faces(); + const Span dst_corner_verts = dst.corner_verts(); + + MutableAttributeAccessor dst_attributes = dst.attributes_for_write(); + + if (!point_ids.is_empty() || !corner_ids.is_empty()) { + Array vert_nearest_tris(dst_positions.size()); + find_nearest_tris_parallel(dst_positions, bvhtree, vert_nearest_tris); + + if (!point_ids.is_empty()) { + Array map(dst.totvert); + find_nearest_verts( + src_positions, src_corner_verts, src_tris, dst_positions, vert_nearest_tris, map); + gather_attributes(point_ids, src_attributes, ATTR_DOMAIN_POINT, map, dst_attributes); } - }); - for (const AttributeIDRef &id : point_ids) { - const GVArraySpan src = *src_attributes.lookup(id, ATTR_DOMAIN_POINT); - GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( - id, ATTR_DOMAIN_POINT, cpp_type_to_custom_data_type(src.type())); - attribute_math::gather(src, nearest_src_verts, dst.span); - dst.finish(); - } - - if (!corner_ids.is_empty()) { - for (const AttributeIDRef &id : corner_ids) { - const GVArraySpan src = *src_attributes.lookup(id, ATTR_DOMAIN_CORNER); - GSpanAttributeWriter dst = dst_attributes.lookup_or_add_for_write_only_span( - id, ATTR_DOMAIN_CORNER, cpp_type_to_custom_data_type(src.type())); - - threading::parallel_for(target_positions.index_range(), 1024, [&](const IndexRange range) { - src.type().to_static_type_tag([&](auto type_tag) { - using T = typename decltype(type_tag)::type; - if constexpr (std::is_void_v) { - BLI_assert_unreachable(); - } - else { - const Span src_typed = src.typed(); - MutableSpan dst_typed = dst.span.typed(); - for (const int dst_vert : range) { - /* Find the average value at the corners of the closest vertex on the - * source mesh. */ - const int src_vert = nearest_src_verts[dst_vert]; - T value; - typename blender::bke::attribute_math::DefaultMixer mixer({&value, 1}); - for (const int corner : source_lmap[src_vert]) { - mixer.mix_in(0, src_typed[corner]); - } - - dst_typed.fill_indices(target_lmap[dst_vert], value); - } - } - }); - }); - - dst.finish(); + if (!corner_ids.is_empty()) { + const Span src_tri_faces = src.looptri_faces(); + Array map(dst.totloop); + find_nearest_corners(src_positions, + src_faces, + src_corner_verts, + src_tri_faces, + dst_positions, + dst_corner_verts, + vert_nearest_tris, + map); + gather_attributes(corner_ids, src_attributes, ATTR_DOMAIN_CORNER, map, dst_attributes); } } - /* Make sure active/default color attribute (names) are brought over. */ - if (source->active_color_attribute) { - BKE_id_attributes_active_color_set(&target->id, source->active_color_attribute); + if (!edge_ids.is_empty()) { + const Span src_edges = src.edges(); + const Span src_corner_edges = src.corner_edges(); + const Span src_tri_faces = src.looptri_faces(); + const Span dst_edges = dst.edges(); + Array map(dst.totedge); + find_nearest_edges(src_positions, + src_edges, + src_faces, + src_corner_edges, + src_tri_faces, + dst_positions, + dst_edges, + bvhtree, + map); + gather_attributes(edge_ids, src_attributes, ATTR_DOMAIN_EDGE, map, dst_attributes); } - if (source->default_color_attribute) { - BKE_id_attributes_default_color_set(&target->id, source->default_color_attribute); + + if (!face_ids.is_empty()) { + const Span src_tri_faces = src.looptri_faces(); + Array map(dst.faces_num); + find_nearest_faces(src_tri_faces, dst_positions, dst_faces, dst_corner_verts, bvhtree, map); + gather_attributes(face_ids, src_attributes, ATTR_DOMAIN_FACE, map, dst_attributes); + } + + if (src.active_color_attribute) { + BKE_id_attributes_active_color_set(&dst.id, src.active_color_attribute); + } + if (src.default_color_attribute) { + BKE_id_attributes_default_color_set(&dst.id, src.default_color_attribute); } free_bvhtree_from_mesh(&bvhtree); } +} // namespace blender::bke + Mesh *BKE_mesh_remesh_voxel_fix_poles(const Mesh *mesh) { const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(mesh); diff --git a/source/blender/blenlib/BLI_offset_indices.hh b/source/blender/blenlib/BLI_offset_indices.hh index dff35644137..003d4f0889e 100644 --- a/source/blender/blenlib/BLI_offset_indices.hh +++ b/source/blender/blenlib/BLI_offset_indices.hh @@ -86,7 +86,7 @@ template class OffsetIndices { OffsetIndices slice(const IndexRange range) const { BLI_assert(offsets_.index_range().drop_back(1).contains(range.last())); - return OffsetIndices(offsets_.slice(range.start(), range.one_after_last())); + return OffsetIndices(offsets_.slice(range.start(), range.size() + 1)); } Span data() const diff --git a/source/blender/blenloader/intern/versioning_280.cc b/source/blender/blenloader/intern/versioning_280.cc index 46be4aa6a01..a18f8124b73 100644 --- a/source/blender/blenloader/intern/versioning_280.cc +++ b/source/blender/blenloader/intern/versioning_280.cc @@ -4712,7 +4712,7 @@ void blo_do_versions_280(FileData *fd, Library * /*lib*/, Main *bmain) LISTBASE_FOREACH (Mesh *, me, &bmain->meshes) { me->flag &= ~(ME_FLAG_UNUSED_0 | ME_FLAG_UNUSED_1 | ME_FLAG_UNUSED_3 | ME_FLAG_UNUSED_4 | - ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_REMESH_REPROJECT_VERTEX_COLORS); + ME_FLAG_UNUSED_6 | ME_FLAG_UNUSED_7 | ME_REMESH_REPROJECT_ATTRIBUTES); } LISTBASE_FOREACH (Material *, mat, &bmain->materials) { diff --git a/source/blender/blenloader/intern/versioning_defaults.cc b/source/blender/blenloader/intern/versioning_defaults.cc index 1314b38ed84..94507fd7bd3 100644 --- a/source/blender/blenloader/intern/versioning_defaults.cc +++ b/source/blender/blenloader/intern/versioning_defaults.cc @@ -594,8 +594,7 @@ void BLO_update_defaults_startup_blend(Main *bmain, const char *app_template) /* Match default for new meshes. */ mesh->smoothresh_legacy = DEG2RADF(30); /* Match voxel remesher options for all existing meshes in templates. */ - mesh->flag |= ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_PAINT_MASK | - ME_REMESH_REPROJECT_SCULPT_FACE_SETS | ME_REMESH_REPROJECT_VERTEX_COLORS; + mesh->flag |= ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_ATTRIBUTES; /* For Sculpting template. */ if (app_template && STREQ(app_template, "Sculpting")) { diff --git a/source/blender/editors/object/object_remesh.cc b/source/blender/editors/object/object_remesh.cc index a030d7ee032..8c8266fdc8a 100644 --- a/source/blender/editors/object/object_remesh.cc +++ b/source/blender/editors/object/object_remesh.cc @@ -134,12 +134,6 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Output mesh will be all smooth or all flat shading. */ - const bke::AttributeAccessor attributes = mesh->attributes(); - const VArray sharp_faces = *attributes.lookup_or_default( - "sharp_face", ATTR_DOMAIN_FACE, false); - const bool smooth_normals = !sharp_faces[0]; - float isovalue = 0.0f; if (mesh->flag & ME_REMESH_REPROJECT_VOLUME) { isovalue = mesh->remesh_voxel_size * 0.3f; @@ -167,22 +161,12 @@ static int voxel_remesh_exec(bContext *C, wmOperator *op) BKE_shrinkwrap_remesh_target_project(new_mesh, mesh, ob); } - if (mesh->flag & ME_REMESH_REPROJECT_PAINT_MASK) { - BKE_mesh_remesh_reproject_paint_mask(new_mesh, mesh); - } - - if (mesh->flag & ME_REMESH_REPROJECT_SCULPT_FACE_SETS) { - BKE_remesh_reproject_sculpt_face_sets(new_mesh, mesh); - } - - if (mesh->flag & ME_REMESH_REPROJECT_VERTEX_COLORS) { - BKE_remesh_reproject_vertex_paint(new_mesh, mesh); + if (mesh->flag & ME_REMESH_REPROJECT_ATTRIBUTES) { + bke::mesh_remesh_reproject_attributes(*mesh, *new_mesh); } BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob); - BKE_mesh_smooth_flag_set(static_cast(ob->data), smooth_normals); - if (ob->mode == OB_MODE_SCULPT) { ED_sculpt_undo_geometry_end(ob); } @@ -670,7 +654,7 @@ struct QuadriFlowJob { bool use_preserve_boundary; bool use_mesh_curvature; - bool preserve_paint_mask; + bool preserve_attributes; bool smooth_normals; int success; @@ -902,8 +886,8 @@ static void quadriflow_start_job(void *customdata, wmJobWorkerStatus *worker_sta ED_sculpt_undo_geometry_begin(ob, qj->op); } - if (qj->preserve_paint_mask) { - BKE_mesh_remesh_reproject_paint_mask(new_mesh, mesh); + if (qj->preserve_attributes) { + blender::bke::mesh_remesh_reproject_attributes(*mesh, *new_mesh); } BKE_mesh_nomain_to_mesh(new_mesh, mesh, ob); @@ -969,7 +953,7 @@ static int quadriflow_remesh_exec(bContext *C, wmOperator *op) job->use_mesh_curvature = RNA_boolean_get(op->ptr, "use_mesh_curvature"); #endif - job->preserve_paint_mask = RNA_boolean_get(op->ptr, "preserve_paint_mask"); + job->preserve_attributes = RNA_boolean_get(op->ptr, "preserve_attributes"); job->smooth_normals = RNA_boolean_get(op->ptr, "smooth_normals"); /* Update the target face count if symmetry is enabled */ @@ -1149,10 +1133,10 @@ void OBJECT_OT_quadriflow_remesh(wmOperatorType *ot) "Take the mesh curvature into account when remeshing"); #endif RNA_def_boolean(ot->srna, - "preserve_paint_mask", + "preserve_attributes", false, - "Preserve Paint Mask", - "Reproject the paint mask onto the new mesh"); + "Preserve Attributes", + "Reproject attributes onto the new mesh"); RNA_def_boolean(ot->srna, "smooth_normals", diff --git a/source/blender/makesdna/DNA_mesh_defaults.h b/source/blender/makesdna/DNA_mesh_defaults.h index 15bf7cb4a83..c7e17c70b60 100644 --- a/source/blender/makesdna/DNA_mesh_defaults.h +++ b/source/blender/makesdna/DNA_mesh_defaults.h @@ -23,7 +23,7 @@ .remesh_voxel_adaptivity = 0.0f, \ .face_sets_color_seed = 0, \ .face_sets_color_default = 1, \ - .flag = ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_PAINT_MASK | ME_REMESH_REPROJECT_SCULPT_FACE_SETS | ME_REMESH_REPROJECT_VERTEX_COLORS, \ + .flag = ME_REMESH_REPROJECT_VOLUME | ME_REMESH_REPROJECT_ATTRIBUTES, \ .editflag = ME_EDIT_MIRROR_VERTEX_GROUPS \ } diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 9cf25e6af9b..f57d89a6936 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -452,7 +452,7 @@ enum { ME_AUTOSMOOTH_LEGACY = 1 << 5, /* deprecated */ ME_FLAG_UNUSED_6 = 1 << 6, /* cleared */ ME_FLAG_UNUSED_7 = 1 << 7, /* cleared */ - ME_REMESH_REPROJECT_VERTEX_COLORS = 1 << 8, + ME_REMESH_REPROJECT_ATTRIBUTES = 1 << 8, ME_DS_EXPAND = 1 << 9, ME_SCULPT_DYNAMIC_TOPOLOGY = 1 << 10, /** @@ -461,10 +461,10 @@ enum { * to improve performance and it only takes one bit, it is stored in the mesh instead. */ ME_NO_OVERLAPPING_TOPOLOGY = 1 << 11, - ME_REMESH_REPROJECT_PAINT_MASK = 1 << 12, + ME_FLAG_UNUSED_8 = 1 << 12, /* deprecated */ ME_REMESH_FIX_POLES = 1 << 13, ME_REMESH_REPROJECT_VOLUME = 1 << 14, - ME_REMESH_REPROJECT_SCULPT_FACE_SETS = 1 << 15, + ME_FLAG_UNUSED_9 = 1 << 15, /* deprecated */ }; #ifdef DNA_DEPRECATED_ALLOW diff --git a/source/blender/makesrna/intern/rna_mesh.cc b/source/blender/makesrna/intern/rna_mesh.cc index 20c8f49c4ae..87cc4d1765c 100644 --- a/source/blender/makesrna/intern/rna_mesh.cc +++ b/source/blender/makesrna/intern/rna_mesh.cc @@ -3148,7 +3148,7 @@ static void rna_def_mesh(BlenderRNA *brna) prop = RNA_def_property(srna, "use_remesh_fix_poles", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, nullptr, "flag", ME_REMESH_FIX_POLES); - RNA_def_property_ui_text(prop, "Fix Poles", "Produces less poles and a better topology flow"); + RNA_def_property_ui_text(prop, "Fix Poles", "Produces fewer poles and a better topology flow"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); @@ -3161,23 +3161,9 @@ static void rna_def_mesh(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); - prop = RNA_def_property(srna, "use_remesh_preserve_paint_mask", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", ME_REMESH_REPROJECT_PAINT_MASK); - RNA_def_property_ui_text(prop, "Preserve Paint Mask", "Keep the current mask on the new mesh"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); - - prop = RNA_def_property(srna, "use_remesh_preserve_sculpt_face_sets", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", ME_REMESH_REPROJECT_SCULPT_FACE_SETS); - RNA_def_property_ui_text( - prop, "Preserve Face Sets", "Keep the current Face Sets on the new mesh"); - RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); - RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE); - - prop = RNA_def_property(srna, "use_remesh_preserve_vertex_colors", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, nullptr, "flag", ME_REMESH_REPROJECT_VERTEX_COLORS); - RNA_def_property_ui_text( - prop, "Preserve Vertex Colors", "Keep the current vertex colors on the new mesh"); + prop = RNA_def_property(srna, "use_remesh_preserve_attributes", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, nullptr, "flag", ME_REMESH_REPROJECT_ATTRIBUTES); + RNA_def_property_ui_text(prop, "Preserve Attributes", "Transfer all attributes to the new mesh"); RNA_def_property_update(prop, 0, "rna_Mesh_update_draw"); RNA_def_property_flag(prop, PROP_NO_DEG_UPDATE);