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(