From 1ea169d90e39647eac721cc1fee9927b0432bf97 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 18 Nov 2022 16:05:06 -0600 Subject: [PATCH] Mesh: Move loose edge flag to a separate cache As part of T95966, this patch moves loose edge information out of the flag on each edge and into a new lazily calculated cache in mesh runtime data. The number of loose edges is also cached, so further processing can be skipped completely when there are no loose edges. Previously the `ME_LOOSEEDGE` flag was updated on a "best effort" basis. In order to be sure that it was correct, you had to be sure to call `BKE_mesh_calc_edges_loose` first. Now the loose edge tag is always correct. It also doesn't have to be calculated eagerly in various places like the screw modifier where the complexity wasn't worth the theoretical performance benefit. The patch also adds a function to eagerly set the number of loose edges to zero to avoid building the cache. This is used by various primitive nodes, with the goal of improving drawing performance. This results in a few ms shaved off extracting draw data for some large meshes in my tests. In the Python API, `MeshEdge.is_loose` is no longer editable. No built-in addons set the value anyway. The upside is that addons can be sure the data is correct based on the mesh. **Tests** There is one test failure in the Python OBJ exporter: `export_obj_cube` that happens because of existing incorrect versioning. Opening the file in master, all the edges were set to "loose", which is fixed by this patch. Differential Revision: https://developer.blender.org/D16504 --- source/blender/blenkernel/BKE_mesh.h | 3 +- .../blenkernel/BKE_mesh_legacy_convert.h | 3 + source/blender/blenkernel/BKE_mesh_types.h | 25 ++++++ source/blender/blenkernel/intern/bvhutils.cc | 22 ++--- source/blender/blenkernel/intern/cloth.cc | 5 +- .../intern/curve_to_mesh_convert.cc | 2 - .../intern/geometry_component_mesh.cc | 34 +++++--- source/blender/blenkernel/intern/mesh.cc | 2 + .../blenkernel/intern/mesh_calc_edges.cc | 5 ++ .../blender/blenkernel/intern/mesh_convert.cc | 4 +- .../blenkernel/intern/mesh_legacy_convert.cc | 34 ++++++-- .../blender/blenkernel/intern/mesh_runtime.cc | 39 +++++++++ .../blenkernel/intern/mesh_validate.cc | 18 ---- .../blender/blenkernel/intern/subsurf_ccg.c | 4 - .../blenloader/intern/versioning_280.c | 6 -- source/blender/bmesh/intern/bmesh_construct.c | 3 +- .../draw_cache_extract_mesh_render_data.cc | 30 ++++--- source/blender/editors/include/ED_mesh.h | 2 + source/blender/editors/mesh/mesh_data.cc | 11 ++- .../blender/editors/object/object_modifier.cc | 4 +- .../geometry/intern/mesh_merge_by_distance.cc | 21 ++--- .../intern/lineart/lineart_cpu.cc | 85 ++++--------------- .../blender/io/collada/GeometryExporter.cpp | 18 ++-- source/blender/io/collada/MeshImporter.cpp | 1 - .../exporter/obj_export_file_writer.cc | 15 ++-- .../wavefront_obj/exporter/obj_export_mesh.cc | 5 -- .../wavefront_obj/exporter/obj_export_mesh.hh | 1 - .../io/wavefront_obj/exporter/obj_exporter.cc | 1 - .../wavefront_obj/importer/obj_import_mesh.cc | 1 - source/blender/makesdna/DNA_mesh_types.h | 14 +++ source/blender/makesdna/DNA_meshdata_types.h | 7 +- source/blender/makesrna/intern/rna_mesh.c | 12 ++- source/blender/modifiers/intern/MOD_mask.cc | 2 - source/blender/modifiers/intern/MOD_screw.c | 18 +--- .../geometry/nodes/node_geo_convex_hull.cc | 2 +- .../nodes/node_geo_delete_geometry.cc | 1 - .../nodes/node_geo_duplicate_elements.cc | 3 +- .../geometry/nodes/node_geo_extrude_mesh.cc | 11 +-- .../nodes/node_geo_mesh_primitive_circle.cc | 5 +- .../nodes/node_geo_mesh_primitive_cone.cc | 2 + .../nodes/node_geo_mesh_primitive_grid.cc | 7 +- .../nodes/node_geo_mesh_primitive_line.cc | 1 - .../node_geo_mesh_primitive_uv_sphere.cc | 2 + .../nodes/node_geo_points_to_vertices.cc | 2 + tests/python/CMakeLists.txt | 2 +- 45 files changed, 259 insertions(+), 236 deletions(-) diff --git a/source/blender/blenkernel/BKE_mesh.h b/source/blender/blenkernel/BKE_mesh.h index 1eadc3a39b0..d85d89b72b6 100644 --- a/source/blender/blenkernel/BKE_mesh.h +++ b/source/blender/blenkernel/BKE_mesh.h @@ -70,6 +70,8 @@ void BKE_mesh_tag_coords_changed(struct Mesh *mesh); */ void BKE_mesh_tag_coords_changed_uniformly(struct Mesh *mesh); +void BKE_mesh_tag_topology_changed(struct Mesh *mesh); + /* *** mesh.c *** */ struct BMesh *BKE_mesh_to_bmesh_ex(const struct Mesh *me, @@ -943,7 +945,6 @@ void BKE_mesh_strip_loose_faces(struct Mesh *me); void BKE_mesh_strip_loose_polysloops(struct Mesh *me); void BKE_mesh_strip_loose_edges(struct Mesh *me); -void BKE_mesh_calc_edges_loose(struct Mesh *mesh); /** * Calculate edges from polygons. */ diff --git a/source/blender/blenkernel/BKE_mesh_legacy_convert.h b/source/blender/blenkernel/BKE_mesh_legacy_convert.h index 5eae7bf3b22..e46942aa9e9 100644 --- a/source/blender/blenkernel/BKE_mesh_legacy_convert.h +++ b/source/blender/blenkernel/BKE_mesh_legacy_convert.h @@ -82,6 +82,9 @@ void BKE_mesh_legacy_convert_material_indices_to_mpoly(struct Mesh *mesh); */ void BKE_mesh_legacy_convert_mpoly_to_material_indices(struct Mesh *mesh); +/** Convert from runtime loose edge cache to legacy edge flag. */ +void BKE_mesh_legacy_convert_loose_edges_to_flag(struct Mesh *mesh); + #endif /** diff --git a/source/blender/blenkernel/BKE_mesh_types.h b/source/blender/blenkernel/BKE_mesh_types.h index 5f20a6c2e14..9be6049fe66 100644 --- a/source/blender/blenkernel/BKE_mesh_types.h +++ b/source/blender/blenkernel/BKE_mesh_types.h @@ -11,6 +11,8 @@ # include +# include "BLI_bit_vector.hh" +# include "BLI_shared_cache.hh" # include "BLI_span.hh" # include "DNA_customdata_types.h" @@ -61,6 +63,23 @@ typedef enum eMeshWrapperType { namespace blender::bke { +/** + * Cache of a mesh's loose edges, accessed with #Mesh::loose_edges(). * + */ +struct LooseEdgeCache { + /** + * A bitmap set to true for each loose edge, false if the edge is used by any face. + * Allocated only if there is at least one loose edge. + */ + blender::BitVector<> is_loose_bits; + /** + * The number of loose edges. If zero, the #is_loose_bits shouldn't be accessed. + * If less than zero, the cache has been accessed in an invalid way + * (i.e.directly instead of through #Mesh::loose_edges()). + */ + int count = -1; +}; + /** * \warning Typical access is done via #Mesh::looptris(). */ @@ -154,6 +173,12 @@ struct MeshRuntime { float (*vert_normals)[3] = nullptr; float (*poly_normals)[3] = nullptr; + /** + * A cache of data about the loose edges. Can be shared with other data-blocks with unchanged + * topology. Accessed with #Mesh::loose_edges(). + */ + SharedCache loose_edges_cache; + /** * A #BLI_bitmap containing tags for the center vertices of subdivided polygons, set by the * subdivision surface modifier and used by drawing code instead of polygon center face dots. diff --git a/source/blender/blenkernel/intern/bvhutils.cc b/source/blender/blenkernel/intern/bvhutils.cc index b897a990aab..b8756db4736 100644 --- a/source/blender/blenkernel/intern/bvhutils.cc +++ b/source/blender/blenkernel/intern/bvhutils.cc @@ -1159,22 +1159,12 @@ static BitVector<> loose_verts_map_get(const Span edges, return loose_verts_mask; } -static BitVector<> loose_edges_map_get(const Span edges, int *r_loose_edge_len) +static BitVector<> loose_edges_map_get(const Mesh &mesh, int *r_loose_edge_len) { - BitVector<> loose_edges_mask(edges.size()); - - int loose_edges_len = 0; - for (const int64_t i : edges.index_range()) { - const MEdge &edge = edges[i]; - if (edge.flag & ME_LOOSEEDGE) { - loose_edges_mask[i].set(); - loose_edges_len++; - } - } - - *r_loose_edge_len = loose_edges_len; - - return loose_edges_mask; + using namespace blender::bke; + const LooseEdgeCache &loose_edges = mesh.loose_edges(); + *r_loose_edge_len = loose_edges.count; + return loose_edges.is_loose_bits; } static BitVector<> looptri_no_hidden_map_get(const Span polys, @@ -1261,7 +1251,7 @@ BVHTree *BKE_bvhtree_from_mesh_get(struct BVHTreeFromMesh *data, break; case BVHTREE_FROM_LOOSEEDGES: - mask = loose_edges_map_get(edges, &mask_bits_act_len); + mask = loose_edges_map_get(*mesh, &mask_bits_act_len); ATTR_FALLTHROUGH; case BVHTREE_FROM_EDGES: data->tree = bvhtree_from_mesh_edges_create_tree( diff --git a/source/blender/blenkernel/intern/cloth.cc b/source/blender/blenkernel/intern/cloth.cc index a9f7c1a45ba..120e69b4e64 100644 --- a/source/blender/blenkernel/intern/cloth.cc +++ b/source/blender/blenkernel/intern/cloth.cc @@ -1456,6 +1456,7 @@ static bool find_internal_spring_target_vertex(BVHTreeFromMesh *treedata, static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) { + using namespace blender::bke; Cloth *cloth = clmd->clothObject; ClothSpring *spring = nullptr, *tspring = nullptr, *tspring2 = nullptr; uint struct_springs = 0, shear_springs = 0, bend_springs = 0, struct_springs_real = 0; @@ -1591,12 +1592,14 @@ static bool cloth_build_springs(ClothModifierData *clmd, Mesh *mesh) } /* Structural springs. */ + const LooseEdgeCache &loose_edges = mesh->loose_edges(); for (int i = 0; i < numedges; i++) { spring = (ClothSpring *)MEM_callocN(sizeof(ClothSpring), "cloth spring"); if (spring) { spring_verts_ordered_set(spring, medge[i].v1, medge[i].v2); - if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW && medge[i].flag & ME_LOOSEEDGE) { + if (clmd->sim_parms->flags & CLOTH_SIMSETTINGS_FLAG_SEW && loose_edges.count > 0 && + loose_edges.is_loose_bits[i]) { /* handle sewing (loose edges will be pulled together) */ spring->restlen = 0.0f; spring->lin_stiffness = 1.0f; diff --git a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc index 3a86068d8e8..e5cafd405df 100644 --- a/source/blender/blenkernel/intern/curve_to_mesh_convert.cc +++ b/source/blender/blenkernel/intern/curve_to_mesh_convert.cc @@ -46,14 +46,12 @@ static void fill_mesh_topology(const int vert_offset, MEdge &edge = edges[edge_offset + i]; edge.v1 = vert_offset + i; edge.v2 = vert_offset + i + 1; - edge.flag = ME_LOOSEEDGE; } if (main_cyclic && main_segment_num > 1) { MEdge &edge = edges[edge_offset + main_segment_num - 1]; edge.v1 = vert_offset + main_point_num - 1; edge.v2 = vert_offset; - edge.flag = ME_LOOSEEDGE; } return; } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index d148d59a48b..3f1cc84a107 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -338,9 +338,6 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, const Span polys = mesh.polys(); const Span loops = mesh.loops(); - /* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */ - Array loose_edges(mesh.totedge, true); - r_values.fill(true); for (const int poly_index : polys.index_range()) { const MPoly &poly = polys[poly_index]; @@ -352,22 +349,23 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, const MLoop &loop = loops[loop_i]; const int edge_index = loop.e; - loose_edges[edge_index] = false; - if (!old_values[loop_i] || !old_values[next_loop_i]) { r_values[edge_index] = false; } } } - /* Deselect loose edges without corners that are still selected from the 'true' default. */ - threading::parallel_for(IndexRange(mesh.totedge), 2048, [&](const IndexRange range) { - for (const int edge_index : range) { - if (loose_edges[edge_index]) { - r_values[edge_index] = false; + const bke::LooseEdgeCache &loose_edges = mesh.loose_edges(); + if (loose_edges.count > 0) { + /* Deselect loose edges without corners that are still selected from the 'true' default. */ + threading::parallel_for(IndexRange(mesh.totedge), 2048, [&](const IndexRange range) { + for (const int edge_index : range) { + if (loose_edges.is_loose_bits[edge_index]) { + r_values[edge_index] = false; + } } - } - }); + }); + } } static GVArray adapt_mesh_domain_corner_to_edge(const Mesh &mesh, const GVArray &varray) @@ -776,7 +774,9 @@ static GVArray adapt_mesh_domain_edge_to_face(const Mesh &mesh, const GVArray &v } // namespace blender::bke -static bool can_simple_adapt_for_single(const eAttrDomain from_domain, const eAttrDomain to_domain) +static bool can_simple_adapt_for_single(const Mesh &mesh, + const eAttrDomain from_domain, + const eAttrDomain to_domain) { /* For some domain combinations, a single value will always map directly. For others, there may * be loose elements on the result domain that should have the default value rather than the @@ -790,9 +790,15 @@ static bool can_simple_adapt_for_single(const eAttrDomain from_domain, const eAt return ELEM(to_domain, ATTR_DOMAIN_FACE, ATTR_DOMAIN_CORNER); case ATTR_DOMAIN_FACE: /* There may be loose vertices or edges not connected to faces. */ + if (to_domain == ATTR_DOMAIN_EDGE) { + return mesh.loose_edges().count == 0; + } return to_domain == ATTR_DOMAIN_CORNER; case ATTR_DOMAIN_CORNER: /* Only faces are always connected to corners. */ + if (to_domain == ATTR_DOMAIN_EDGE) { + return mesh.loose_edges().count == 0; + } return to_domain == ATTR_DOMAIN_FACE; default: BLI_assert_unreachable(); @@ -815,7 +821,7 @@ static blender::GVArray adapt_mesh_attribute_domain(const Mesh &mesh, return varray; } if (varray.is_single()) { - if (can_simple_adapt_for_single(from_domain, to_domain)) { + if (can_simple_adapt_for_single(mesh, from_domain, to_domain)) { BUFFER_FOR_CPP_TYPE_VALUE(varray.type(), value); varray.get_internal_single(value); return blender::GVArray::ForSingle( diff --git a/source/blender/blenkernel/intern/mesh.cc b/source/blender/blenkernel/intern/mesh.cc index 8d4e00ef1fd..924b5600a16 100644 --- a/source/blender/blenkernel/intern/mesh.cc +++ b/source/blender/blenkernel/intern/mesh.cc @@ -128,6 +128,7 @@ static void mesh_copy_data(Main *bmain, ID *id_dst, const ID *id_src, const int * when the source is persistent and edits to the destination don't change the bounds. It will be * "un-shared" as necessary when the positions are changed. */ mesh_dst->runtime->bounds_cache = mesh_src->runtime->bounds_cache; + mesh_dst->runtime->loose_edges_cache = mesh_src->runtime->loose_edges_cache; /* Only do tessface if we have no polys. */ const bool do_tessface = ((mesh_src->totface != 0) && (mesh_src->totpoly == 0)); @@ -252,6 +253,7 @@ static void mesh_blend_write(BlendWriter *writer, ID *id, const void *id_address BKE_mesh_legacy_bevel_weight_from_layers(mesh); BKE_mesh_legacy_face_set_from_generic(mesh, poly_layers); BKE_mesh_legacy_edge_crease_from_layers(mesh); + BKE_mesh_legacy_convert_loose_edges_to_flag(mesh); /* When converting to the old mesh format, don't save redundant attributes. */ names_to_skip.add_multiple_new({".hide_vert", ".hide_edge", diff --git a/source/blender/blenkernel/intern/mesh_calc_edges.cc b/source/blender/blenkernel/intern/mesh_calc_edges.cc index 4a4c2ebcbb0..ddca932c648 100644 --- a/source/blender/blenkernel/intern/mesh_calc_edges.cc +++ b/source/blender/blenkernel/intern/mesh_calc_edges.cc @@ -262,6 +262,11 @@ void BKE_mesh_calc_edges(Mesh *mesh, bool keep_existing_edges, const bool select } } + if (!keep_existing_edges) { + /* All edges are rebuilt from the faces, so there are no loose edges. */ + mesh->loose_edges_tag_none(); + } + /* Explicitly clear edge maps, because that way it can be parallelized. */ clear_hash_tables(edge_maps); } diff --git a/source/blender/blenkernel/intern/mesh_convert.cc b/source/blender/blenkernel/intern/mesh_convert.cc index 2255038a991..fd2bb67c648 100644 --- a/source/blender/blenkernel/intern/mesh_convert.cc +++ b/source/blender/blenkernel/intern/mesh_convert.cc @@ -223,7 +223,7 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba for (b = 1; b < dl->nr; b++) { medge->v1 = startvert + ofs + b - 1; medge->v2 = startvert + ofs + b; - medge->flag = ME_LOOSEEDGE | ME_EDGEDRAW; + medge->flag = ME_EDGEDRAW; medge++; } @@ -251,7 +251,7 @@ static Mesh *mesh_nurbs_displist_to_mesh(const Curve *cu, const ListBase *dispba else { medge->v2 = startvert + ofs + b + 1; } - medge->flag = ME_LOOSEEDGE | ME_EDGEDRAW; + medge->flag = ME_EDGEDRAW; medge++; } } diff --git a/source/blender/blenkernel/intern/mesh_legacy_convert.cc b/source/blender/blenkernel/intern/mesh_legacy_convert.cc index 23426f8c087..1673e972a32 100644 --- a/source/blender/blenkernel/intern/mesh_legacy_convert.cc +++ b/source/blender/blenkernel/intern/mesh_legacy_convert.cc @@ -154,9 +154,6 @@ static void mesh_calc_edges_mdata(const MVert * /*allvert*/, if (use_old == false || ed->is_draw) { med->flag = ME_EDGEDRAW; } - if (ed->is_loose) { - med->flag |= ME_LOOSEEDGE; - } /* order is swapped so extruding this edge as a surface won't flip face normals * with cyclic curves */ @@ -174,9 +171,6 @@ static void mesh_calc_edges_mdata(const MVert * /*allvert*/, med->v1 = ed->v1; med->v2 = ed->v2; med->flag = ME_EDGEDRAW; - if (ed->is_loose) { - med->flag |= ME_LOOSEEDGE; - } MEM_freeN(edsort); @@ -237,6 +231,7 @@ void BKE_mesh_calc_edges_legacy(Mesh *me, const bool use_old) medge = (MEdge *)CustomData_add_layer(&me->edata, CD_MEDGE, CD_ASSIGN, medge, totedge); me->totedge = totedge; + BKE_mesh_tag_topology_changed(me); BKE_mesh_strip_loose_faces(me); } @@ -1545,3 +1540,30 @@ void BKE_mesh_legacy_convert_flags_to_selection_layers(Mesh *mesh) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Loose Edges + * \{ */ + +void BKE_mesh_legacy_convert_loose_edges_to_flag(Mesh *mesh) +{ + using namespace blender; + using namespace blender::bke; + + const LooseEdgeCache &loose_edges = mesh->loose_edges(); + MutableSpan edges = mesh->edges_for_write(); + threading::parallel_for(edges.index_range(), 4096, [&](const IndexRange range) { + if (loose_edges.count == 0) { + for (const int64_t i : range) { + edges[i].flag &= ~ME_LOOSEEDGE; + } + } + else { + for (const int64_t i : range) { + SET_FLAG_FROM_TEST(edges[i].flag, loose_edges.is_loose_bits[i], ME_LOOSEEDGE); + } + } + }); +} + +/** \} */ diff --git a/source/blender/blenkernel/intern/mesh_runtime.cc b/source/blender/blenkernel/intern/mesh_runtime.cc index 74010a30c45..d62e4442d2a 100644 --- a/source/blender/blenkernel/intern/mesh_runtime.cc +++ b/source/blender/blenkernel/intern/mesh_runtime.cc @@ -15,6 +15,7 @@ #include "BLI_math_geom.h" #include "BLI_task.hh" +#include "BLI_timeit.hh" #include "BKE_bvhutils.h" #include "BKE_editmesh_cache.h" @@ -116,6 +117,38 @@ blender::Span Mesh::looptris() const return {looptris, num_looptris}; } +const blender::bke::LooseEdgeCache &Mesh::loose_edges() const +{ + using namespace blender::bke; + this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) { + SCOPED_TIMER("loose_edges"); + blender::BitVector<> &loose_edges = r_data.is_loose_bits; + loose_edges.resize(0); + loose_edges.resize(this->totedge, true); + + int count = this->totedge; + for (const MLoop &loop : this->loops()) { + if (loose_edges[loop.e]) { + loose_edges[loop.e].reset(); + count--; + } + } + + r_data.count = count; + }); + + return this->runtime->loose_edges_cache.data(); +} + +void Mesh::loose_edges_tag_none() const +{ + using namespace blender::bke; + this->runtime->loose_edges_cache.ensure([&](LooseEdgeCache &r_data) { + r_data.is_loose_bits.resize(0); + r_data.count = 0; + }); +} + /** * Ensure the array is large enough * @@ -254,6 +287,7 @@ void BKE_mesh_runtime_clear_geometry(Mesh *mesh) free_normals(*mesh->runtime); free_subdiv_ccg(*mesh->runtime); mesh->runtime->bounds_cache.tag_dirty(); + mesh->runtime->loose_edges_cache.tag_dirty(); if (mesh->runtime->shrinkwrap_data) { BKE_shrinkwrap_boundary_data_free(mesh->runtime->shrinkwrap_data); } @@ -276,6 +310,11 @@ void BKE_mesh_tag_coords_changed_uniformly(Mesh *mesh) mesh->runtime->bounds_cache.tag_dirty(); } +void BKE_mesh_tag_topology_changed(struct Mesh *mesh) +{ + BKE_mesh_runtime_clear_geometry(mesh); +} + bool BKE_mesh_is_deformed_only(const Mesh *mesh) { return mesh->runtime->deformed_only; diff --git a/source/blender/blenkernel/intern/mesh_validate.cc b/source/blender/blenkernel/intern/mesh_validate.cc index c6bd114e911..634a92e23ca 100644 --- a/source/blender/blenkernel/intern/mesh_validate.cc +++ b/source/blender/blenkernel/intern/mesh_validate.cc @@ -1319,24 +1319,6 @@ void BKE_mesh_strip_loose_edges(Mesh *me) /** \name Mesh Edge Calculation * \{ */ -void BKE_mesh_calc_edges_loose(Mesh *mesh) -{ - const Span loops = mesh->loops(); - MutableSpan edges = mesh->edges_for_write(); - - for (const int i : edges.index_range()) { - edges[i].flag |= ME_LOOSEEDGE; - } - for (const int i : loops.index_range()) { - edges[loops[i].e].flag &= ~ME_LOOSEEDGE; - } - for (const int i : edges.index_range()) { - if (edges[i].flag & ME_LOOSEEDGE) { - edges[i].flag |= ME_EDGEDRAW; - } - } -} - void BKE_mesh_calc_edges_tessface(Mesh *mesh) { const int numFaces = mesh->totface; diff --git a/source/blender/blenkernel/intern/subsurf_ccg.c b/source/blender/blenkernel/intern/subsurf_ccg.c index 80fb637b76e..bfb1f781a70 100644 --- a/source/blender/blenkernel/intern/subsurf_ccg.c +++ b/source/blender/blenkernel/intern/subsurf_ccg.c @@ -999,10 +999,6 @@ static void ccgDM_copyFinalEdgeArray(DerivedMesh *dm, MEdge *medge) int x; int edgeIdx = POINTER_AS_INT(ccgSubSurf_getEdgeEdgeHandle(e)); - if (!ccgSubSurf_getEdgeNumFaces(e)) { - ed_flag |= ME_LOOSEEDGE; - } - if (edgeFlags) { if (edgeIdx != -1) { ed_flag |= ((edgeFlags[index] & (ME_SEAM | ME_SHARP)) | ME_EDGEDRAW); diff --git a/source/blender/blenloader/intern/versioning_280.c b/source/blender/blenloader/intern/versioning_280.c index 1a8fec49516..ffb87949234 100644 --- a/source/blender/blenloader/intern/versioning_280.c +++ b/source/blender/blenloader/intern/versioning_280.c @@ -2969,12 +2969,6 @@ void blo_do_versions_280(FileData *fd, Library *UNUSED(lib), Main *bmain) } } - if (!MAIN_VERSION_ATLEAST(bmain, 280, 28)) { - for (Mesh *mesh = bmain->meshes.first; mesh; mesh = mesh->id.next) { - BKE_mesh_calc_edges_loose(mesh); - } - } - if (!MAIN_VERSION_ATLEAST(bmain, 280, 29)) { for (bScreen *screen = bmain->screens.first; screen; screen = screen->id.next) { LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { diff --git a/source/blender/bmesh/intern/bmesh_construct.c b/source/blender/bmesh/intern/bmesh_construct.c index cff8eb5a2f7..90609121963 100644 --- a/source/blender/bmesh/intern/bmesh_construct.c +++ b/source/blender/bmesh/intern/bmesh_construct.c @@ -731,8 +731,7 @@ short BM_edge_flag_to_mflag(BMEdge *e) const char hflag = e->head.hflag; return (((hflag & BM_ELEM_SEAM) ? ME_SEAM : 0) | ((hflag & BM_ELEM_DRAW) ? ME_EDGEDRAW : 0) | - ((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0) | - (BM_edge_is_wire(e) ? ME_LOOSEEDGE : 0)); + ((hflag & BM_ELEM_SMOOTH) == 0 ? ME_SHARP : 0)); } char BM_face_flag_to_mflag(BMFace *f) { diff --git a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc index 1f5157e6b8b..bfec7cd6f7b 100644 --- a/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc +++ b/source/blender/draw/intern/draw_cache_extract_mesh_render_data.cc @@ -78,22 +78,28 @@ static void mesh_render_data_loose_geom_build(const MeshRenderData *mr, MeshBuff static void mesh_render_data_loose_geom_mesh(const MeshRenderData *mr, MeshBufferCache *cache) { + using namespace blender; BLI_bitmap *lvert_map = BLI_BITMAP_NEW(mr->vert_len, __func__); - cache->loose_geom.edges = static_cast( - MEM_mallocN(mr->edge_len * sizeof(*cache->loose_geom.edges), __func__)); - const MEdge *med = mr->medge; - for (int med_index = 0; med_index < mr->edge_len; med_index++, med++) { - if (med->flag & ME_LOOSEEDGE) { - cache->loose_geom.edges[cache->loose_geom.edge_len++] = med_index; + const bke::LooseEdgeCache &loose_edges = mr->me->loose_edges(); + if (loose_edges.count > 0) { + cache->loose_geom.edges = static_cast( + MEM_malloc_arrayN(loose_edges.count, sizeof(int), __func__)); + + cache->loose_geom.edge_len = 0; + for (const int64_t i : loose_edges.is_loose_bits.index_range()) { + if (loose_edges.is_loose_bits[i]) { + cache->loose_geom.edges[cache->loose_geom.edge_len] = int(i); + cache->loose_geom.edge_len++; + } } - /* Tag verts as not loose. */ - BLI_BITMAP_ENABLE(lvert_map, med->v1); - BLI_BITMAP_ENABLE(lvert_map, med->v2); } - if (cache->loose_geom.edge_len < mr->edge_len) { - cache->loose_geom.edges = static_cast(MEM_reallocN( - cache->loose_geom.edges, cache->loose_geom.edge_len * sizeof(*cache->loose_geom.edges))); + + /* Tag verts as not loose. */ + const Span edges(mr->medge, mr->edge_len); + for (const MEdge &edge : edges) { + BLI_BITMAP_ENABLE(lvert_map, edge.v1); + BLI_BITMAP_ENABLE(lvert_map, edge.v2); } cache->loose_geom.verts = static_cast( diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 52527f6c1b8..bab1f7e4c5e 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -552,6 +552,8 @@ void ED_mesh_geometry_clear(struct Mesh *mesh); void ED_mesh_update(struct Mesh *mesh, struct bContext *C, bool calc_edges, bool calc_edges_loose); +bool ED_mesh_edge_is_loose(const struct Mesh *mesh, int index); + void ED_mesh_uv_ensure(struct Mesh *me, const char *name); int ED_mesh_uv_add( struct Mesh *me, const char *name, bool active_set, bool do_init, struct ReportList *reports); diff --git a/source/blender/editors/mesh/mesh_data.cc b/source/blender/editors/mesh/mesh_data.cc index 9901f4e1836..58b700b3a2e 100644 --- a/source/blender/editors/mesh/mesh_data.cc +++ b/source/blender/editors/mesh/mesh_data.cc @@ -1118,8 +1118,8 @@ void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_lo BKE_mesh_calc_edges(mesh, calc_edges, true); } - if (calc_edges_loose && mesh->totedge) { - BKE_mesh_calc_edges_loose(mesh); + if (calc_edges_loose) { + mesh->runtime->loose_edges_cache.tag_dirty(); } /* Default state is not to have tessface's so make sure this is the case. */ @@ -1132,6 +1132,13 @@ void ED_mesh_update(Mesh *mesh, bContext *C, bool calc_edges, bool calc_edges_lo WM_event_add_notifier(C, NC_GEOM | ND_DATA, mesh); } +bool ED_mesh_edge_is_loose(const Mesh *mesh, const int index) +{ + using namespace blender; + const bke::LooseEdgeCache &loose_edges = mesh->loose_edges(); + return loose_edges.count > 0 && loose_edges.is_loose_bits[index]; +} + static void mesh_add_verts(Mesh *mesh, int len) { using namespace blender; diff --git a/source/blender/editors/object/object_modifier.cc b/source/blender/editors/object/object_modifier.cc index 67399717c72..3a239506cb3 100644 --- a/source/blender/editors/object/object_modifier.cc +++ b/source/blender/editors/object/object_modifier.cc @@ -614,7 +614,7 @@ bool ED_object_modifier_convert_psys_to_mesh(ReportList * /*reports*/, if (k) { medge->v1 = cvert - 1; medge->v2 = cvert; - medge->flag = ME_EDGEDRAW | ME_LOOSEEDGE; + medge->flag = ME_EDGEDRAW; medge++; } else { @@ -633,7 +633,7 @@ bool ED_object_modifier_convert_psys_to_mesh(ReportList * /*reports*/, if (k) { medge->v1 = cvert - 1; medge->v2 = cvert; - medge->flag = ME_EDGEDRAW | ME_LOOSEEDGE; + medge->flag = ME_EDGEDRAW; medge++; } else { diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc index 288fd407641..99cdb9cb7d9 100644 --- a/source/blender/geometry/intern/mesh_merge_by_distance.cc +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -1434,9 +1434,6 @@ static Mesh *create_merged_mesh(const Mesh &mesh, MEdge *me = &dst_edges[dest_index]; me->v1 = vert_final[wegrp->v1]; me->v2 = vert_final[wegrp->v2]; - /* "For now, assume that all merged edges are loose. This flag will be cleared in the - * Polys/Loops step". */ - me->flag |= ME_LOOSEEDGE; edge_final[i] = dest_index; dest_index++; @@ -1485,10 +1482,6 @@ static Mesh *create_merged_mesh(const Mesh &mesh, r_ml->e = e; r_ml++; loop_cur++; - if (iter.type) { - dst_edges[e].flag &= ~ME_LOOSEEDGE; - } - BLI_assert((dst_edges[e].flag & ME_LOOSEEDGE) == 0); } } @@ -1520,10 +1513,6 @@ static Mesh *create_merged_mesh(const Mesh &mesh, r_ml->e = e; r_ml++; loop_cur++; - if (iter.type) { - dst_edges[e].flag &= ~ME_LOOSEEDGE; - } - BLI_assert((dst_edges[e].flag & ME_LOOSEEDGE) == 0); } r_mp->loopstart = loop_start; @@ -1600,11 +1589,19 @@ std::optional mesh_merge_by_distance_connected(const Mesh &mesh, range_vn_i(vert_dest_map.data(), mesh.totvert, 0); /* Collapse Edges that are shorter than the threshold. */ + const bke::LooseEdgeCache *loose_edges = nullptr; + if (only_loose_edges) { + loose_edges = &mesh.loose_edges(); + if (loose_edges->count == 0) { + return {}; + } + } + for (const int i : edges.index_range()) { int v1 = edges[i].v1; int v2 = edges[i].v2; - if (only_loose_edges && (edges[i].flag & ME_LOOSEEDGE) == 0) { + if (loose_edges && !loose_edges->is_loose_bits[i]) { continue; } while (v1 != vert_dest_map[v1]) { diff --git a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.cc b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.cc index 6971ebae449..277101716ed 100644 --- a/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.cc +++ b/source/blender/gpencil_modifiers/intern/lineart/lineart_cpu.cc @@ -1713,69 +1713,9 @@ static void lineart_identify_mlooptri_feature_edges(void *__restrict userdata, struct LooseEdgeData { int loose_count; - int loose_max; int *loose_array; - const MEdge *edges; }; -static void lineart_loose_data_reallocate(LooseEdgeData *loose_data, int count) -{ - int *new_arr = static_cast(MEM_calloc_arrayN(count, sizeof(int), "loose edge array")); - if (loose_data->loose_array) { - memcpy(new_arr, loose_data->loose_array, sizeof(int) * loose_data->loose_max); - MEM_SAFE_FREE(loose_data->loose_array); - } - loose_data->loose_max = count; - loose_data->loose_array = new_arr; -} - -static void lineart_join_loose_edge_arr(LooseEdgeData *loose_data, LooseEdgeData *to_be_joined) -{ - if (!to_be_joined->loose_array) { - return; - } - int new_count = loose_data->loose_count + to_be_joined->loose_count; - if (new_count >= loose_data->loose_max) { - lineart_loose_data_reallocate(loose_data, new_count); - } - memcpy(&loose_data->loose_array[loose_data->loose_count], - to_be_joined->loose_array, - sizeof(int) * to_be_joined->loose_count); - loose_data->loose_count += to_be_joined->loose_count; - MEM_freeN(to_be_joined->loose_array); - to_be_joined->loose_array = nullptr; -} - -static void lineart_add_loose_edge(LooseEdgeData *loose_data, const int i) -{ - if (loose_data->loose_count >= loose_data->loose_max) { - int min_amount = MAX2(100, loose_data->loose_count * 2); - lineart_loose_data_reallocate(loose_data, min_amount); - } - loose_data->loose_array[loose_data->loose_count] = i; - loose_data->loose_count++; -} - -static void lineart_identify_loose_edges(void *__restrict /*userdata*/, - const int i, - const TaskParallelTLS *__restrict tls) -{ - LooseEdgeData *loose_data = (LooseEdgeData *)tls->userdata_chunk; - - if (loose_data->edges[i].flag & ME_LOOSEEDGE) { - lineart_add_loose_edge(loose_data, i); - } -} - -static void loose_data_sum_reduce(const void *__restrict /*userdata*/, - void *__restrict chunk_join, - void *__restrict chunk) -{ - LooseEdgeData *final = (LooseEdgeData *)chunk_join; - LooseEdgeData *loose_chunk = (LooseEdgeData *)chunk; - lineart_join_loose_edge_arr(final, loose_chunk); -} - void lineart_add_edge_to_array(LineartPendingEdges *pe, LineartEdge *e) { if (pe->next >= pe->max || !pe->max) { @@ -1985,6 +1925,7 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, LineartData *la_data, ListBase *shadow_elns) { + using namespace blender; Mesh *me = ob_info->original_me; if (!me->totedge) { return; @@ -2158,15 +2099,18 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, if (la_data->conf.use_loose) { /* Only identifying floating edges at this point because other edges has been taken care of * inside #lineart_identify_mlooptri_feature_edges function. */ - TaskParallelSettings edge_loose_settings; - BLI_parallel_range_settings_defaults(&edge_loose_settings); - edge_loose_settings.min_iter_per_thread = 4000; - edge_loose_settings.func_reduce = loose_data_sum_reduce; - edge_loose_settings.userdata_chunk = &loose_data; - edge_loose_settings.userdata_chunk_size = sizeof(LooseEdgeData); - loose_data.edges = BKE_mesh_edges(me); - BLI_task_parallel_range( - 0, me->totedge, &loose_data, lineart_identify_loose_edges, &edge_loose_settings); + const bke::LooseEdgeCache &loose_edges = me->loose_edges(); + loose_data.loose_array = static_cast( + MEM_malloc_arrayN(loose_edges.count, sizeof(int), __func__)); + if (loose_edges.count > 0) { + int loose_i = 0; + for (const int64_t edge_i : IndexRange(me->totedge)) { + if (loose_edges.is_loose_bits[edge_i]) { + loose_data.loose_array[loose_i] = int(edge_i); + loose_i++; + } + } + } } int allocate_la_e = edge_reduce.feat_edges + loose_data.loose_count; @@ -2271,8 +2215,9 @@ static void lineart_geometry_object_load(LineartObjectInfo *ob_info, } if (loose_data.loose_array) { + const Span edges = me->edges(); for (int i = 0; i < loose_data.loose_count; i++) { - const MEdge *edge = &loose_data.edges[loose_data.loose_array[i]]; + const MEdge *edge = &edges[loose_data.loose_array[i]]; la_edge->v1 = &la_v_arr[edge->v1]; la_edge->v2 = &la_v_arr[edge->v2]; la_edge->flags = LRT_EDGE_FLAG_LOOSE; diff --git a/source/blender/io/collada/GeometryExporter.cpp b/source/blender/io/collada/GeometryExporter.cpp index f6f9026481c..5ede22163fc 100644 --- a/source/blender/io/collada/GeometryExporter.cpp +++ b/source/blender/io/collada/GeometryExporter.cpp @@ -200,21 +200,23 @@ void GeometryExporter::export_key_mesh(Object *ob, Mesh *me, KeyBlock *kb) void GeometryExporter::createLooseEdgeList(Object *ob, Mesh *me, std::string &geom_id) { + using namespace blender; const Span edges = me->edges(); - int totedges = me->totedge; int edges_in_linelist = 0; std::vector edge_list; int index; /* Find all loose edges in Mesh * and save vertex indices in edge_list */ - for (index = 0; index < totedges; index++) { - const MEdge *edge = &edges[index]; - - if (edge->flag & ME_LOOSEEDGE) { - edges_in_linelist += 1; - edge_list.push_back(edge->v1); - edge_list.push_back(edge->v2); + const bke::LooseEdgeCache &loose_edges = me->loose_edges(); + if (loose_edges.count > 0) { + for (const int64_t i : edges.index_range()) { + if (loose_edges.is_loose_bits[i]) { + const MEdge *edge = &edges[i]; + edges_in_linelist += 1; + edge_list.push_back(edge->v1); + edge_list.push_back(edge->v2); + } } } diff --git a/source/blender/io/collada/MeshImporter.cpp b/source/blender/io/collada/MeshImporter.cpp index b2423d3a9e6..3f676fa9df9 100644 --- a/source/blender/io/collada/MeshImporter.cpp +++ b/source/blender/io/collada/MeshImporter.cpp @@ -593,7 +593,6 @@ void MeshImporter::read_lines(COLLADAFW::Mesh *mesh, Mesh *me) uint *indices = mp->getPositionIndices().getData(); for (int j = 0; j < edge_count; j++, med++) { - med->flag |= ME_LOOSEEDGE; med->v1 = indices[2 * j]; med->v2 = indices[2 * j + 1]; } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc index 5289a8c750a..b8d03cfac65 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_file_writer.cc @@ -416,11 +416,16 @@ void OBJWriter::write_edges_indices(FormatHandler &fh, const IndexOffsets &offsets, const OBJMesh &obj_mesh_data) const { - /* NOTE: ensure_mesh_edges should be called before. */ - const Span edges = obj_mesh_data.get_mesh()->edges(); - for (const int i : edges.index_range()) { - const MEdge &edge = edges[i]; - if (edge.flag & ME_LOOSEEDGE) { + const Mesh &mesh = *obj_mesh_data.get_mesh(); + const bke::LooseEdgeCache &loose_edges = mesh.loose_edges(); + if (loose_edges.count == 0) { + return; + } + + const Span edges = mesh.edges(); + for (const int64_t i : edges.index_range()) { + if (loose_edges.is_loose_bits[i]) { + const MEdge &edge = edges[i]; fh.write_obj_edge(edge.v1 + offsets.vertex_offset + 1, edge.v2 + offsets.vertex_offset + 1); } } diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc index a95f917869b..5c3216b085b 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.cc @@ -181,11 +181,6 @@ void OBJMesh::ensure_mesh_normals() const BKE_mesh_calc_normals_split(export_mesh_eval_); } -void OBJMesh::ensure_mesh_edges() const -{ - BKE_mesh_calc_edges_loose(export_mesh_eval_); -} - void OBJMesh::calc_smooth_groups(const bool use_bitflags) { const Span edges = export_mesh_eval_->edges(); diff --git a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh index 89ee6f4dea2..e1c0e67c174 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh +++ b/source/blender/io/wavefront_obj/exporter/obj_export_mesh.hh @@ -132,7 +132,6 @@ class OBJMesh : NonCopyable { const Material *get_object_material(int16_t mat_nr) const; void ensure_mesh_normals() const; - void ensure_mesh_edges() const; /** * Calculate smooth groups of a smooth-shaded object. diff --git a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc index daf2a06e112..f3d379c620c 100644 --- a/source/blender/io/wavefront_obj/exporter/obj_exporter.cc +++ b/source/blender/io/wavefront_obj/exporter/obj_exporter.cc @@ -160,7 +160,6 @@ static void write_mesh_objects(Vector> exportable_as_me if (export_params.export_normals) { obj.ensure_mesh_normals(); } - obj.ensure_mesh_edges(); } /* Parallel over meshes: store normal coords & indices, uv coords and indices. */ diff --git a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc index 56ad7fd4563..b2b49457051 100644 --- a/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc +++ b/source/blender/io/wavefront_obj/importer/obj_import_mesh.cc @@ -259,7 +259,6 @@ void MeshFromGeometry::create_edges(Mesh *mesh) /* Set argument `update` to true so that existing, explicitly imported edges can be merged * with the new ones created from polygons. */ BKE_mesh_calc_edges(mesh, true, false); - BKE_mesh_calc_edges_loose(mesh); } void MeshFromGeometry::create_uv_verts(Mesh *mesh) diff --git a/source/blender/makesdna/DNA_mesh_types.h b/source/blender/makesdna/DNA_mesh_types.h index 3f951583741..3458fe5b334 100644 --- a/source/blender/makesdna/DNA_mesh_types.h +++ b/source/blender/makesdna/DNA_mesh_types.h @@ -22,6 +22,7 @@ namespace bke { struct MeshRuntime; class AttributeAccessor; class MutableAttributeAccessor; +struct LooseEdgeCache; } // namespace bke } // namespace blender using MeshRuntimeHandle = blender::bke::MeshRuntime; @@ -253,6 +254,19 @@ typedef struct Mesh { * Cached triangulation of the mesh. */ blender::Span looptris() const; + + /** + * Cached information about loose edges, calculated lazily when necessary. + */ + const blender::bke::LooseEdgeCache &loose_edges() const; + /** + * Explicitly set the cached number of loose edges to zero. This can improve performance + * later on, because finding loose edges lazily can be skipped entirely. + * + * \note To allow setting this status on meshes without changing them, this This does not tag the + * cache dirty. If the mesh was changed first, the relevant dirty tags should be called first. + */ + void loose_edges_tag_none() const; #endif } Mesh; diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index f1c2dcaae68..96c543d3db5 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -71,9 +71,12 @@ enum { /* SELECT = (1 << 0), */ ME_EDGEDRAW = (1 << 1), ME_SEAM = (1 << 2), - /** Deprecated hide status. Now stored in ".hide_edge" attribute. */ - /* ME_HIDE = (1 << 4), */ +/** Deprecated hide status. Now stored in ".hide_edge" attribute. */ +/* ME_HIDE = (1 << 4), */ +#ifdef DNA_DEPRECATED_ALLOW + /** Deprecated loose edge status. Now stored in #Mesh::loose_edges() runtime cache. */ ME_LOOSEEDGE = (1 << 7), +#endif ME_SHARP = (1 << 9), /* only reason this flag remains a 'short' */ }; diff --git a/source/blender/makesrna/intern/rna_mesh.c b/source/blender/makesrna/intern/rna_mesh.c index 247e1cd1173..2ca465c95b8 100644 --- a/source/blender/makesrna/intern/rna_mesh.c +++ b/source/blender/makesrna/intern/rna_mesh.c @@ -1485,6 +1485,13 @@ static void rna_MeshEdge_select_set(PointerRNA *ptr, bool value) select_edge[index] = value; } +static bool rna_MeshEdge_is_loose_get(PointerRNA *ptr) +{ + const Mesh *mesh = rna_mesh(ptr); + const int index = rna_MeshEdge_index_get(ptr); + return ED_mesh_edge_is_loose(mesh, index); +} + static int rna_MeshLoopTriangle_material_index_get(PointerRNA *ptr) { const Mesh *me = rna_mesh(ptr); @@ -2341,8 +2348,9 @@ static void rna_def_medge(BlenderRNA *brna) RNA_def_property_update(prop, 0, "rna_Mesh_update_data_legacy_deg_tag_all"); prop = RNA_def_property(srna, "is_loose", PROP_BOOLEAN, PROP_NONE); - RNA_def_property_boolean_sdna(prop, NULL, "flag", ME_LOOSEEDGE); - RNA_def_property_ui_text(prop, "Loose", "Loose edge"); + RNA_def_property_clear_flag(prop, PROP_EDITABLE); + RNA_def_property_boolean_funcs(prop, "rna_MeshEdge_is_loose_get", NULL); + RNA_def_property_ui_text(prop, "Loose", "Edge is not connected to any faces"); prop = RNA_def_property(srna, "use_freestyle_mark", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_funcs( diff --git a/source/blender/modifiers/intern/MOD_mask.cc b/source/blender/modifiers/intern/MOD_mask.cc index 659a1625079..ae79ce5ee54 100644 --- a/source/blender/modifiers/intern/MOD_mask.cc +++ b/source/blender/modifiers/intern/MOD_mask.cc @@ -774,8 +774,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext * /*ctx*/, M edges_add_num); } - BKE_mesh_calc_edges_loose(result); - return result; } diff --git a/source/blender/modifiers/intern/MOD_screw.c b/source/blender/modifiers/intern/MOD_screw.c index 4ba274dbd8a..8eb6c978568 100644 --- a/source/blender/modifiers/intern/MOD_screw.c +++ b/source/blender/modifiers/intern/MOD_screw.c @@ -431,19 +431,13 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * mv_new = mvert_new; mv_orig = mvert_orig; - BLI_bitmap *vert_tag = BLI_BITMAP_NEW(totvert, __func__); - /* Copy the first set of edges */ const MEdge *med_orig = medge_orig; med_new = medge_new; for (i = 0; i < totedge; i++, med_orig++, med_new++) { med_new->v1 = med_orig->v1; med_new->v2 = med_orig->v2; - med_new->flag = med_orig->flag & ~ME_LOOSEEDGE; - - /* Tag #MVert as not loose. */ - BLI_BITMAP_ENABLE(vert_tag, med_orig->v1); - BLI_BITMAP_ENABLE(vert_tag, med_orig->v2); + med_new->flag = med_orig->flag; } /* build polygon -> edge map */ @@ -815,9 +809,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * med_new->v1 = varray_stride + j; med_new->v2 = med_new->v1 - totvert; med_new->flag = ME_EDGEDRAW; - if (!BLI_BITMAP_TEST(vert_tag, j)) { - med_new->flag |= ME_LOOSEEDGE; - } med_new++; } } @@ -836,9 +827,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * med_new->v1 = i; med_new->v2 = varray_stride + i; med_new->flag = ME_EDGEDRAW; - if (!BLI_BITMAP_TEST(vert_tag, i)) { - med_new->flag |= ME_LOOSEEDGE; - } med_new++; } } @@ -994,7 +982,7 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * /* new vertical edge */ med_new->v1 = i1; med_new->v2 = i2; - med_new->flag = med_new_firstloop->flag & ~ME_LOOSEEDGE; + med_new->flag = med_new_firstloop->flag; med_new++; } @@ -1025,8 +1013,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext *ctx, Mesh * } #endif - MEM_freeN(vert_tag); - if (edge_poly_map) { MEM_freeN(edge_poly_map); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc index 278d7c4bd24..825913fb985 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_convex_hull.cc @@ -101,7 +101,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span coords) MEdge &edge = edges[0]; edge.v1 = 0; edge.v2 = 1; - edge.flag |= ME_EDGEDRAW | ME_LOOSEEDGE; + edge.flag = ME_EDGEDRAW; edge_index++; } BLI_assert(edge_index == edges_num); diff --git a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc index 8ed97f2019f..a9f1de12672 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_delete_geometry.cc @@ -1053,7 +1053,6 @@ static void do_mesh_separation(GeometrySet &geometry_set, } } - BKE_mesh_calc_edges_loose(mesh_out); geometry_set.replace_mesh(mesh_out); } diff --git a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc index f048ec11f77..7e91c65c78f 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_duplicate_elements.cc @@ -579,6 +579,8 @@ static void duplicate_faces(GeometrySet &geometry_set, } } + new_mesh->loose_edges_tag_none(); + copy_face_attributes_without_id(geometry_set, edge_mapping, vert_mapping, @@ -745,7 +747,6 @@ static void duplicate_edges(GeometrySet &geometry_set, MEdge &new_edge = new_edges[edge_range[i_duplicate]]; new_edge.v1 = vert_range[i_duplicate * 2]; new_edge.v2 = vert_range[i_duplicate * 2] + 1; - new_edge.flag = ME_LOOSEEDGE; } } }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc index 0062abba909..0d3b48fcadd 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_extrude_mesh.cc @@ -158,15 +158,6 @@ static MEdge new_edge(const int v1, const int v2) return edge; } -static MEdge new_loose_edge(const int v1, const int v2) -{ - MEdge edge; - edge.v1 = v1; - edge.v2 = v2; - edge.flag = ME_LOOSEEDGE; - return edge; -} - static MPoly new_poly(const int loopstart, const int totloop) { MPoly poly; @@ -234,7 +225,7 @@ static void extrude_mesh_vertices(Mesh &mesh, MutableSpan new_edges = mesh.edges_for_write().slice(new_edge_range); for (const int i_selection : selection.index_range()) { - new_edges[i_selection] = new_loose_edge(selection[i_selection], new_vert_range[i_selection]); + new_edges[i_selection] = new_edge(selection[i_selection], new_vert_range[i_selection]); } MutableAttributeAccessor attributes = mesh.attributes_for_write(); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc index 14f38efbd42..fca2f0b7313 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_circle.cc @@ -125,14 +125,11 @@ static Mesh *create_circle_mesh(const float radius, } /* Create outer edges. */ - const short edge_flag = (fill_type == GEO_NODE_MESH_CIRCLE_FILL_NONE) ? - ME_LOOSEEDGE : - ME_EDGEDRAW; /* NGON or TRIANGLE_FAN */ for (const int i : IndexRange(verts_num)) { MEdge &edge = edges[i]; edge.v1 = i; edge.v2 = (i + 1) % verts_num; - edge.flag = edge_flag; + edge.flag = ME_EDGEDRAW; } /* Create triangle fan edges. */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc index dca91d2dc61..586470ff3c7 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_cone.cc @@ -697,6 +697,8 @@ Mesh *create_cylinder_or_cone_mesh(const float radius_top, calculate_cone_uvs(mesh, config); calculate_selection_outputs(mesh, config, attribute_outputs); + mesh->loose_edges_tag_none(); + return mesh; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc index e8ee057ee5c..84af0e00312 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_grid.cc @@ -76,7 +76,6 @@ Mesh *create_grid_mesh(const int verts_x, const int y_edges_start = 0; const int x_edges_start = verts_x * edges_y; - const short edge_flag = (edges_x == 0 || edges_y == 0) ? ME_LOOSEEDGE : ME_EDGEDRAW; /* Build the horizontal edges in the X direction. */ threading::parallel_for(IndexRange(verts_x), 512, [&](IndexRange x_range) { @@ -89,7 +88,7 @@ Mesh *create_grid_mesh(const int verts_x, MEdge &edge = edges[y_edge_offset + y]; edge.v1 = vert_index; edge.v2 = vert_index + 1; - edge.flag = edge_flag; + edge.flag = ME_EDGEDRAW; } }); } @@ -105,7 +104,7 @@ Mesh *create_grid_mesh(const int verts_x, MEdge &edge = edges[x_edge_offset + x]; edge.v1 = vert_index; edge.v2 = vert_index + verts_y; - edge.flag = edge_flag; + edge.flag = ME_EDGEDRAW; } }); } @@ -144,6 +143,8 @@ Mesh *create_grid_mesh(const int verts_x, calculate_uvs(mesh, verts, loops, size_x, size_y); } + mesh->loose_edges_tag_none(); + return mesh; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc index 51a4f36507e..e5063d172a0 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_line.cc @@ -196,7 +196,6 @@ Mesh *create_line_mesh(const float3 start, const float3 delta, const int count) for (const int i : range) { edges[i].v1 = i; edges[i].v2 = i + 1; - edges[i].flag |= ME_LOOSEEDGE; } }); }); diff --git a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc index cfa1c477b28..0f693aff876 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_mesh_primitive_uv_sphere.cc @@ -319,6 +319,8 @@ static Mesh *create_uv_sphere_mesh(const float radius, const int segments, const [&]() { calculate_sphere_corners(loops, segments, rings); }, [&]() { calculate_sphere_uvs(mesh, segments, rings); }); + mesh->loose_edges_tag_none(); + return mesh; } diff --git a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc index 4ac3bf712f7..2646e0d826d 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_points_to_vertices.cc @@ -62,6 +62,8 @@ static void geometry_set_points_to_vertices(GeometrySet &geometry_set, } } + mesh->loose_edges_tag_none(); + geometry_set.keep_only_during_modify({GEO_COMPONENT_TYPE_MESH}); } diff --git a/tests/python/CMakeLists.txt b/tests/python/CMakeLists.txt index 14b00ace251..ea568bb7c56 100644 --- a/tests/python/CMakeLists.txt +++ b/tests/python/CMakeLists.txt @@ -334,7 +334,7 @@ add_blender_test( --run={'FINISHED'}&bpy.ops.export_scene.obj\(filepath='${TEST_OUT_DIR}/io_tests/export_obj_cube.obj',use_selection=False\) --md5_source=${TEST_OUT_DIR}/io_tests/export_obj_cube.obj --md5_source=${TEST_OUT_DIR}/io_tests/export_obj_cube.mtl - --md5=95832f81160f07101dc566cb286a9f76 --md5_method=FILE + --md5=e80660437ad9bfe082849641c361a233 --md5_method=FILE ) add_blender_test(