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
This commit is contained in:
parent
c0f33814c1
commit
1ea169d90e
Notes:
blender-bot
2024-04-17 14:24:09 +02:00
Referenced by commite0fbeb6e7b
, Fix T103225: Line Art modifier skips loose edges Referenced by issue #95966, Struct of Arrays Refactor for Mesh Edges Referenced by issue #120721, Collada: Import with loose edges not immediately show them Referenced by pull request #120737, Fix #120721: Collada: Import with loose edges not immediately show them Referenced by commit0bda626ba3
, Fix #120721: Collada: Import with loose edges not immediately show them
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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
|
||||
|
||||
/**
|
||||
|
|
|
@ -11,6 +11,8 @@
|
|||
|
||||
# include <mutex>
|
||||
|
||||
# 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<LooseEdgeCache> 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.
|
||||
|
|
|
@ -1159,22 +1159,12 @@ static BitVector<> loose_verts_map_get(const Span<MEdge> edges,
|
|||
return loose_verts_mask;
|
||||
}
|
||||
|
||||
static BitVector<> loose_edges_map_get(const Span<MEdge> 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<MPoly> 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(
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -338,9 +338,6 @@ void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh,
|
|||
const Span<MPoly> polys = mesh.polys();
|
||||
const Span<MLoop> loops = mesh.loops();
|
||||
|
||||
/* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */
|
||||
Array<bool> 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(
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MEdge> 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);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
|
|
@ -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<MLoopTri> 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;
|
||||
|
|
|
@ -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<MLoop> loops = mesh->loops();
|
||||
MutableSpan<MEdge> 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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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<int *>(
|
||||
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<int *>(
|
||||
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<int *>(MEM_reallocN(
|
||||
cache->loose_geom.edges, cache->loose_geom.edge_len * sizeof(*cache->loose_geom.edges)));
|
||||
|
||||
/* Tag verts as not loose. */
|
||||
const Span<MEdge> 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<int *>(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 *> 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]) {
|
||||
|
|
|
@ -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<int *>(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<int *>(
|
||||
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<MEdge> 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;
|
||||
|
|
|
@ -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<MEdge> edges = me->edges();
|
||||
int totedges = me->totedge;
|
||||
int edges_in_linelist = 0;
|
||||
std::vector<uint> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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];
|
||||
}
|
||||
|
|
|
@ -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<MEdge> 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<MEdge> 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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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<MEdge> edges = export_mesh_eval_->edges();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -160,7 +160,6 @@ static void write_mesh_objects(Vector<std::unique_ptr<OBJMesh>> 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. */
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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<MLoopTri> 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;
|
||||
|
||||
|
|
|
@ -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' */
|
||||
};
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -774,8 +774,6 @@ static Mesh *modifyMesh(ModifierData *md, const ModifierEvalContext * /*ctx*/, M
|
|||
edges_add_num);
|
||||
}
|
||||
|
||||
BKE_mesh_calc_edges_loose(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -101,7 +101,7 @@ static Mesh *hull_from_bullet(const Mesh *mesh, Span<float3> 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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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<MEdge> 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();
|
||||
|
|
|
@ -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. */
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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});
|
||||
}
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue