Mesh: Replace MLoop struct with generic attributes #104424
|
@ -6212,6 +6212,7 @@ class VIEW3D_PT_shading_compositor(Panel):
|
|||
bl_region_type = 'HEADER'
|
||||
bl_label = "Compositor"
|
||||
bl_parent_id = 'VIEW3D_PT_shading'
|
||||
bl_order = 10
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
|
|
@ -772,47 +772,6 @@ void BKE_mesh_polys_flip(const struct MPoly *mpoly,
|
|||
struct CustomData *ldata,
|
||||
int totpoly);
|
||||
|
||||
/* Merge verts. */
|
||||
/* Enum for merge_mode of #BKE_mesh_merge_verts.
|
||||
* Refer to mesh_merge.c for details. */
|
||||
enum {
|
||||
MESH_MERGE_VERTS_DUMP_IF_MAPPED,
|
||||
MESH_MERGE_VERTS_DUMP_IF_EQUAL,
|
||||
};
|
||||
/**
|
||||
* Merge Verts
|
||||
*
|
||||
* This frees the given mesh and returns a new mesh.
|
||||
*
|
||||
* \param vtargetmap: The table that maps vertices to target vertices. a value of -1
|
||||
* indicates a vertex is a target, and is to be kept.
|
||||
* This array is aligned with 'mesh->totvert'
|
||||
* \warning \a vtargetmap must **not** contain any chained mapping (v1 -> v2 -> v3 etc.),
|
||||
* this is not supported and will likely generate corrupted geometry.
|
||||
*
|
||||
* \param tot_vtargetmap: The number of non '-1' values in vtargetmap. (not the size)
|
||||
*
|
||||
* \param merge_mode: enum with two modes.
|
||||
* - #MESH_MERGE_VERTS_DUMP_IF_MAPPED
|
||||
* When called by the Mirror Modifier,
|
||||
* In this mode it skips any faces that have all vertices merged (to avoid creating pairs
|
||||
* of faces sharing the same set of vertices)
|
||||
* - #MESH_MERGE_VERTS_DUMP_IF_EQUAL
|
||||
* When called by the Array Modifier,
|
||||
* In this mode, faces where all vertices are merged are double-checked,
|
||||
* to see whether all target vertices actually make up a poly already.
|
||||
* Indeed it could be that all of a poly's vertices are merged,
|
||||
* but merged to vertices that do not make up a single poly,
|
||||
* in which case the original poly should not be dumped.
|
||||
* Actually this later behavior could apply to the Mirror Modifier as well,
|
||||
* but the additional checks are costly and not necessary in the case of mirror,
|
||||
* because each vertex is only merged to its own mirror.
|
||||
*/
|
||||
struct Mesh *BKE_mesh_merge_verts(struct Mesh *mesh,
|
||||
const int *vtargetmap,
|
||||
int tot_vtargetmap,
|
||||
int merge_mode);
|
||||
|
||||
/**
|
||||
* Account for custom-data such as UVs becoming detached because of imprecision
|
||||
* in custom-data interpolation.
|
||||
|
|
|
@ -35,7 +35,9 @@ struct Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(struct MirrorModi
|
|||
struct Object *ob,
|
||||
const struct Mesh *mesh,
|
||||
int axis,
|
||||
bool use_correct_order_on_merge);
|
||||
bool use_correct_order_on_merge,
|
||||
int **r_vert_merge_map,
|
||||
int *r_vert_merge_map_len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -201,7 +201,6 @@ set(SRC
|
|||
intern/mesh_iterators.cc
|
||||
intern/mesh_legacy_convert.cc
|
||||
intern/mesh_mapping.cc
|
||||
intern/mesh_merge.c
|
||||
intern/mesh_merge_customdata.cc
|
||||
intern/mesh_mirror.cc
|
||||
intern/mesh_normals.cc
|
||||
|
|
|
@ -1,666 +0,0 @@
|
|||
/* SPDX-License-Identifier: GPL-2.0-or-later
|
||||
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*/
|
||||
#include <string.h> /* for memcpy */
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
#include "DNA_meshdata_types.h"
|
||||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_edgehash.h"
|
||||
#include "BLI_ghash.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_utildefines.h"
|
||||
#include "BLI_utildefines_stack.h"
|
||||
|
||||
#include "BKE_customdata.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_mesh_mapping.h"
|
||||
|
||||
/**
|
||||
* Poly compare with vtargetmap
|
||||
* Function used by #BKE_mesh_merge_verts.
|
||||
* The function compares poly_source after applying vtargetmap, with poly_target.
|
||||
* The two polys are identical if they share the same vertices in the same order,
|
||||
* or in reverse order, but starting position loopstart may be different.
|
||||
* The function is called with direct_reverse=1 for same order (i.e. same normal),
|
||||
* and may be called again with direct_reverse=-1 for reverse order.
|
||||
* \return 1 if polys are identical, 0 if polys are different.
|
||||
*/
|
||||
static int cddm_poly_compare(const int *corner_verts,
|
||||
const MPoly *mpoly_source,
|
||||
const MPoly *mpoly_target,
|
||||
const int *vtargetmap,
|
||||
const int direct_reverse)
|
||||
{
|
||||
int vert_source, first_vert_source, vert_target;
|
||||
int i_loop_source;
|
||||
int i_loop_target, i_loop_target_start, i_loop_target_offset, i_loop_target_adjusted;
|
||||
bool compare_completed = false;
|
||||
bool same_loops = false;
|
||||
|
||||
const int *corner_vert_source, *corner_vert_target;
|
||||
|
||||
BLI_assert(ELEM(direct_reverse, 1, -1));
|
||||
|
||||
i_loop_source = 0;
|
||||
corner_vert_source = corner_verts + mpoly_source->loopstart;
|
||||
vert_source = *corner_vert_source;
|
||||
|
||||
if (vtargetmap[vert_source] != -1) {
|
||||
vert_source = vtargetmap[vert_source];
|
||||
}
|
||||
else {
|
||||
/* All source loop vertices should be mapped */
|
||||
BLI_assert(false);
|
||||
}
|
||||
|
||||
/* Find same vertex within mpoly_target's loops */
|
||||
corner_vert_target = corner_verts + mpoly_target->loopstart;
|
||||
for (i_loop_target = 0; i_loop_target < mpoly_target->totloop;
|
||||
i_loop_target++, corner_vert_target++) {
|
||||
if (*corner_vert_target == vert_source) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If same vertex not found, then polys cannot be equal */
|
||||
if (i_loop_target >= mpoly_target->totloop) {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Now corner_vert_source and m_loop_target have one identical vertex */
|
||||
/* corner_vert_source is at position 0, while m_loop_target has advanced to find identical vertex
|
||||
*/
|
||||
/* Go around the loop and check that all vertices match in same order */
|
||||
/* Skipping source loops when consecutive source vertices are mapped to same target vertex */
|
||||
|
||||
i_loop_target_start = i_loop_target;
|
||||
i_loop_target_offset = 0;
|
||||
first_vert_source = vert_source;
|
||||
|
||||
compare_completed = false;
|
||||
same_loops = false;
|
||||
|
||||
while (!compare_completed) {
|
||||
|
||||
vert_target = *corner_vert_target;
|
||||
|
||||
/* First advance i_loop_source, until it points to different vertex, after mapping applied */
|
||||
do {
|
||||
i_loop_source++;
|
||||
|
||||
if (i_loop_source == mpoly_source->totloop) {
|
||||
/* End of loops for source, must match end of loop for target. */
|
||||
if (i_loop_target_offset == mpoly_target->totloop - 1) {
|
||||
compare_completed = true;
|
||||
same_loops = true;
|
||||
break; /* Polys are identical */
|
||||
}
|
||||
|
||||
compare_completed = true;
|
||||
same_loops = false;
|
||||
break; /* Polys are different */
|
||||
}
|
||||
|
||||
corner_vert_source++;
|
||||
vert_source = *corner_vert_source;
|
||||
|
||||
if (vtargetmap[vert_source] != -1) {
|
||||
vert_source = vtargetmap[vert_source];
|
||||
}
|
||||
else {
|
||||
/* All source loop vertices should be mapped */
|
||||
BLI_assert(false);
|
||||
}
|
||||
|
||||
} while (vert_source == vert_target);
|
||||
|
||||
if (compare_completed) {
|
||||
break;
|
||||
}
|
||||
|
||||
/* Now advance i_loop_target as well */
|
||||
i_loop_target_offset++;
|
||||
|
||||
if (i_loop_target_offset == mpoly_target->totloop) {
|
||||
/* End of loops for target only, that means no match */
|
||||
/* except if all remaining source vertices are mapped to first target */
|
||||
for (; i_loop_source < mpoly_source->totloop; i_loop_source++, corner_vert_source++) {
|
||||
vert_source = vtargetmap[*corner_vert_source];
|
||||
if (vert_source != first_vert_source) {
|
||||
compare_completed = true;
|
||||
same_loops = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!compare_completed) {
|
||||
same_loops = true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
/* Adjust i_loop_target for cycling around and for direct/reverse order
|
||||
* defined by delta = +1 or -1 */
|
||||
i_loop_target_adjusted = (i_loop_target_start + direct_reverse * i_loop_target_offset) %
|
||||
mpoly_target->totloop;
|
||||
if (i_loop_target_adjusted < 0) {
|
||||
i_loop_target_adjusted += mpoly_target->totloop;
|
||||
}
|
||||
corner_vert_target = corner_verts + mpoly_target->loopstart + i_loop_target_adjusted;
|
||||
vert_target = *corner_vert_target;
|
||||
|
||||
if (vert_target != vert_source) {
|
||||
same_loops = false; /* Polys are different */
|
||||
break;
|
||||
}
|
||||
}
|
||||
return same_loops;
|
||||
}
|
||||
|
||||
/* Utility stuff for using GHash with polys, used by vertex merging. */
|
||||
|
||||
typedef struct PolyKey {
|
||||
int poly_index; /* index of the MPoly within the derived mesh */
|
||||
int totloops; /* number of loops in the poly */
|
||||
uint hash_sum; /* Sum of all vertices indices */
|
||||
uint hash_xor; /* Xor of all vertices indices */
|
||||
} PolyKey;
|
||||
|
||||
static uint poly_gset_hash_fn(const void *key)
|
||||
{
|
||||
const PolyKey *pk = key;
|
||||
return pk->hash_sum;
|
||||
}
|
||||
|
||||
static bool poly_gset_compare_fn(const void *k1, const void *k2)
|
||||
{
|
||||
const PolyKey *pk1 = k1;
|
||||
const PolyKey *pk2 = k2;
|
||||
if ((pk1->hash_sum == pk2->hash_sum) && (pk1->hash_xor == pk2->hash_xor) &&
|
||||
(pk1->totloops == pk2->totloops)) {
|
||||
/* Equality - note that this does not mean equality of polys */
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Mesh *BKE_mesh_merge_verts(Mesh *mesh,
|
||||
const int *vtargetmap,
|
||||
const int tot_vtargetmap,
|
||||
const int merge_mode)
|
||||
{
|
||||
/* This was commented out back in 2013, see commit f45d8827bafe6b9eaf9de42f4054e9d84a21955d. */
|
||||
// #define USE_LOOPS
|
||||
|
||||
Mesh *result = NULL;
|
||||
|
||||
const int totvert = mesh->totvert;
|
||||
const int totedge = mesh->totedge;
|
||||
const int totloop = mesh->totloop;
|
||||
const int totpoly = mesh->totpoly;
|
||||
const MEdge *src_edges = BKE_mesh_edges(mesh);
|
||||
const MPoly *src_polys = BKE_mesh_polys(mesh);
|
||||
const int *src_corner_verts = BKE_mesh_corner_verts(mesh);
|
||||
const int *src_corner_edges = BKE_mesh_corner_edges(mesh);
|
||||
|
||||
const int totvert_final = totvert - tot_vtargetmap;
|
||||
|
||||
int *oldv = MEM_malloc_arrayN(totvert_final, sizeof(*oldv), __func__);
|
||||
int *newv = MEM_malloc_arrayN(totvert, sizeof(*newv), __func__);
|
||||
STACK_DECLARE(oldv);
|
||||
|
||||
/* NOTE: create (totedge + totloop) elements because partially invalid polys due to merge may
|
||||
* require generating new edges, and while in 99% cases we'll still end with less final edges
|
||||
* than totedge, cases can be forged that would end requiring more. */
|
||||
const MEdge *med;
|
||||
MEdge *medge = MEM_malloc_arrayN((totedge + totloop), sizeof(*medge), __func__);
|
||||
int *olde = MEM_malloc_arrayN((totedge + totloop), sizeof(*olde), __func__);
|
||||
int *newe = MEM_malloc_arrayN((totedge + totloop), sizeof(*newe), __func__);
|
||||
STACK_DECLARE(medge);
|
||||
STACK_DECLARE(olde);
|
||||
|
||||
int *corner_verts = MEM_malloc_arrayN(totloop, sizeof(int), __func__);
|
||||
int *corner_edges = MEM_malloc_arrayN(totloop, sizeof(int), __func__);
|
||||
int *oldl = MEM_malloc_arrayN(totloop, sizeof(*oldl), __func__);
|
||||
#ifdef USE_LOOPS
|
||||
int *newl = MEM_malloc_arrayN(totloop, sizeof(*newl), __func__);
|
||||
#endif
|
||||
STACK_DECLARE(corner_verts);
|
||||
STACK_DECLARE(corner_edges);
|
||||
STACK_DECLARE(oldl);
|
||||
|
||||
const MPoly *mp;
|
||||
MPoly *mpoly = MEM_malloc_arrayN(totpoly, sizeof(*medge), __func__);
|
||||
int *oldp = MEM_malloc_arrayN(totpoly, sizeof(*oldp), __func__);
|
||||
STACK_DECLARE(mpoly);
|
||||
STACK_DECLARE(oldp);
|
||||
|
||||
EdgeHash *ehash = BLI_edgehash_new_ex(__func__, totedge);
|
||||
|
||||
int i, j, c;
|
||||
|
||||
PolyKey *poly_keys;
|
||||
GSet *poly_gset = NULL;
|
||||
MeshElemMap *poly_map = NULL;
|
||||
int *poly_map_mem = NULL;
|
||||
|
||||
STACK_INIT(oldv, totvert_final);
|
||||
STACK_INIT(olde, totedge);
|
||||
STACK_INIT(oldl, totloop);
|
||||
STACK_INIT(oldp, totpoly);
|
||||
|
||||
STACK_INIT(medge, totedge);
|
||||
STACK_INIT(corner_verts, totloop);
|
||||
STACK_INIT(corner_edges, totloop);
|
||||
STACK_INIT(mpoly, totpoly);
|
||||
|
||||
/* fill newv with destination vertex indices */
|
||||
c = 0;
|
||||
for (i = 0; i < totvert; i++) {
|
||||
if (vtargetmap[i] == -1) {
|
||||
STACK_PUSH(oldv, i);
|
||||
newv[i] = c++;
|
||||
}
|
||||
else {
|
||||
/* dummy value */
|
||||
newv[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/* now link target vertices to destination indices */
|
||||
for (i = 0; i < totvert; i++) {
|
||||
if (vtargetmap[i] != -1) {
|
||||
newv[i] = newv[vtargetmap[i]];
|
||||
}
|
||||
}
|
||||
|
||||
/* Don't remap vertices in cddm->mloop, because we need to know the original
|
||||
* indices in order to skip faces with all vertices merged.
|
||||
* The "update loop indices..." section further down remaps vertices in mloop.
|
||||
*/
|
||||
|
||||
/* now go through and fix edges and faces */
|
||||
med = src_edges;
|
||||
c = 0;
|
||||
for (i = 0; i < totedge; i++, med++) {
|
||||
const uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1;
|
||||
const uint v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2;
|
||||
if (LIKELY(v1 != v2)) {
|
||||
void **val_p;
|
||||
|
||||
if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
|
||||
newe[i] = POINTER_AS_INT(*val_p);
|
||||
}
|
||||
else {
|
||||
STACK_PUSH(olde, i);
|
||||
STACK_PUSH(medge, *med);
|
||||
newe[i] = c;
|
||||
*val_p = POINTER_FROM_INT(c);
|
||||
c++;
|
||||
}
|
||||
}
|
||||
else {
|
||||
newe[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_EQUAL) {
|
||||
/* In this mode, we need to determine, whenever a poly' vertices are all mapped */
|
||||
/* if the targets already make up a poly, in which case the new poly is dropped */
|
||||
/* This poly equality check is rather complex.
|
||||
* We use a BLI_ghash to speed it up with a first level check */
|
||||
PolyKey *mpgh;
|
||||
poly_keys = MEM_malloc_arrayN(totpoly, sizeof(PolyKey), __func__);
|
||||
poly_gset = BLI_gset_new_ex(poly_gset_hash_fn, poly_gset_compare_fn, __func__, totpoly);
|
||||
/* Duplicates allowed because our compare function is not pure equality */
|
||||
BLI_gset_flag_set(poly_gset, GHASH_FLAG_ALLOW_DUPES);
|
||||
|
||||
mp = src_polys;
|
||||
mpgh = poly_keys;
|
||||
for (i = 0; i < totpoly; i++, mp++, mpgh++) {
|
||||
mpgh->poly_index = i;
|
||||
mpgh->totloops = mp->totloop;
|
||||
mpgh->hash_sum = mpgh->hash_xor = 0;
|
||||
for (j = 0; j < mp->totloop; j++) {
|
||||
const int vert_i = src_corner_verts[mp->loopstart + j];
|
||||
mpgh->hash_sum += vert_i;
|
||||
mpgh->hash_xor ^= vert_i;
|
||||
}
|
||||
BLI_gset_insert(poly_gset, mpgh);
|
||||
}
|
||||
|
||||
/* Can we optimize by reusing an old `pmap`? How do we know an old `pmap` is stale? */
|
||||
/* When called by `MOD_array.c` the `cddm` has just been created, so it has no valid `pmap`. */
|
||||
BKE_mesh_vert_poly_map_create(
|
||||
&poly_map, &poly_map_mem, src_polys, src_corner_verts, totvert, totpoly, totloop);
|
||||
} /* done preparing for fast poly compare */
|
||||
|
||||
BLI_bitmap *vert_tag = BLI_BITMAP_NEW(mesh->totvert, __func__);
|
||||
|
||||
mp = src_polys;
|
||||
for (i = 0; i < totpoly; i++, mp++) {
|
||||
MPoly *mp_new;
|
||||
|
||||
/* check faces with all vertices merged */
|
||||
bool all_verts_merged = true;
|
||||
|
||||
for (j = 0; j < mp->totloop; j++) {
|
||||
const int vert_i = src_corner_verts[mp->loopstart + j];
|
||||
if (vtargetmap[vert_i] == -1) {
|
||||
all_verts_merged = false;
|
||||
/* This will be used to check for poly using several time the same vert. */
|
||||
BLI_BITMAP_DISABLE(vert_tag, vert_i);
|
||||
}
|
||||
else {
|
||||
/* This will be used to check for poly using several time the same vert. */
|
||||
BLI_BITMAP_DISABLE(vert_tag, vtargetmap[vert_i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (UNLIKELY(all_verts_merged)) {
|
||||
if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_MAPPED) {
|
||||
/* In this mode, all vertices merged is enough to dump face */
|
||||
continue;
|
||||
}
|
||||
if (merge_mode == MESH_MERGE_VERTS_DUMP_IF_EQUAL) {
|
||||
/* Additional condition for face dump: target vertices must make up an identical face.
|
||||
* The test has 2 steps:
|
||||
* 1) first step is fast `ghash` lookup, but not fail-proof.
|
||||
* 2) second step is thorough but more costly poly compare. */
|
||||
int i_poly, v_target;
|
||||
bool found = false;
|
||||
PolyKey pkey;
|
||||
|
||||
/* Use poly_gset for fast (although not 100% certain) identification of same poly */
|
||||
/* First, make up a poly_summary structure */
|
||||
pkey.hash_sum = pkey.hash_xor = 0;
|
||||
pkey.totloops = 0;
|
||||
for (j = 0; j < mp->totloop; j++) {
|
||||
const int vert_i = src_corner_verts[mp->loopstart + j];
|
||||
v_target = vtargetmap[vert_i]; /* Cannot be -1, they are all mapped */
|
||||
pkey.hash_sum += v_target;
|
||||
pkey.hash_xor ^= v_target;
|
||||
pkey.totloops++;
|
||||
}
|
||||
if (BLI_gset_haskey(poly_gset, &pkey)) {
|
||||
|
||||
/* There might be a poly that matches this one.
|
||||
* We could just leave it there and say there is, and do a "continue".
|
||||
* ... but we are checking whether there is an exact poly match.
|
||||
* It's not so costly in terms of CPU since it's very rare, just a lot of complex code.
|
||||
*/
|
||||
|
||||
/* Consider current loop again */
|
||||
/* Consider the target of the loop's first vert */
|
||||
v_target = vtargetmap[src_corner_verts[mp->loopstart]];
|
||||
/* Now see if v_target belongs to a poly that shares all vertices with source poly,
|
||||
* in same order, or reverse order */
|
||||
|
||||
for (i_poly = 0; i_poly < poly_map[v_target].count; i_poly++) {
|
||||
const MPoly *target_poly = src_polys + *(poly_map[v_target].indices + i_poly);
|
||||
|
||||
if (cddm_poly_compare(corner_verts, mp, target_poly, vtargetmap, +1) ||
|
||||
cddm_poly_compare(corner_verts, mp, target_poly, vtargetmap, -1)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (found) {
|
||||
/* Current poly's vertices are mapped to a poly that is strictly identical */
|
||||
/* Current poly is dumped */
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Here either the poly's vertices were not all merged
|
||||
* or they were all merged, but targets do not make up an identical poly,
|
||||
* the poly is retained.
|
||||
*/
|
||||
c = 0;
|
||||
int *last_valid_corner_vert = NULL;
|
||||
int *last_valid_corner_edge = NULL;
|
||||
int *first_valid_corner_vert = NULL;
|
||||
int *first_valid_corner_edge = NULL;
|
||||
bool need_edge_from_last_valid_ml = false;
|
||||
bool need_edge_to_first_valid_ml = false;
|
||||
int created_edges = 0;
|
||||
for (j = 0; j < mp->totloop; j++) {
|
||||
const int orig_vert_i = src_corner_verts[mp->loopstart + j];
|
||||
const int orig_edge_i = src_corner_edges[mp->loopstart + j];
|
||||
const uint mlv = (vtargetmap[orig_vert_i] != -1) ? vtargetmap[orig_vert_i] : orig_vert_i;
|
||||
#ifndef NDEBUG
|
||||
{
|
||||
const int next_corner_vert = src_corner_verts[mp->loopstart + ((j + 1) % mp->totloop)];
|
||||
uint next_mlv = (vtargetmap[next_corner_vert] != -1) ? vtargetmap[next_corner_vert] :
|
||||
next_corner_vert;
|
||||
med = src_edges + orig_edge_i;
|
||||
uint v1 = (vtargetmap[med->v1] != -1) ? vtargetmap[med->v1] : med->v1;
|
||||
uint v2 = (vtargetmap[med->v2] != -1) ? vtargetmap[med->v2] : med->v2;
|
||||
BLI_assert((mlv == v1 && next_mlv == v2) || (mlv == v2 && next_mlv == v1));
|
||||
}
|
||||
#endif
|
||||
/* A loop is only valid if its matching edge is,
|
||||
* and it's not reusing a vertex already used by this poly. */
|
||||
if (LIKELY((newe[orig_edge_i] != -1) && !BLI_BITMAP_TEST(vert_tag, mlv))) {
|
||||
BLI_BITMAP_ENABLE(vert_tag, mlv);
|
||||
|
||||
if (UNLIKELY(last_valid_corner_vert != NULL && need_edge_from_last_valid_ml)) {
|
||||
/* We need to create a new edge between last valid loop and this one! */
|
||||
void **val_p;
|
||||
|
||||
uint v1 = (vtargetmap[*last_valid_corner_vert] != -1) ?
|
||||
vtargetmap[*last_valid_corner_vert] :
|
||||
*last_valid_corner_vert;
|
||||
uint v2 = mlv;
|
||||
BLI_assert(v1 != v2);
|
||||
if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
|
||||
*last_valid_corner_edge = POINTER_AS_INT(*val_p);
|
||||
}
|
||||
else {
|
||||
const int new_eidx = STACK_SIZE(medge);
|
||||
STACK_PUSH(olde, olde[*last_valid_corner_edge]);
|
||||
STACK_PUSH(medge, src_edges[*last_valid_corner_edge]);
|
||||
medge[new_eidx].v1 = *last_valid_corner_vert;
|
||||
medge[new_eidx].v2 = orig_vert_i;
|
||||
/* DO NOT change newe mapping,
|
||||
* could break actual values due to some deleted original edges. */
|
||||
*val_p = POINTER_FROM_INT(new_eidx);
|
||||
created_edges++;
|
||||
|
||||
*last_valid_corner_edge = new_eidx;
|
||||
}
|
||||
need_edge_from_last_valid_ml = false;
|
||||
}
|
||||
|
||||
#ifdef USE_LOOPS
|
||||
newl[j + mp->loopstart] = STACK_SIZE(corner_verts);
|
||||
#endif
|
||||
STACK_PUSH(oldl, j + mp->loopstart);
|
||||
last_valid_corner_vert = STACK_PUSH_RET_PTR(corner_verts);
|
||||
last_valid_corner_edge = STACK_PUSH_RET_PTR(corner_edges);
|
||||
*last_valid_corner_vert = orig_vert_i;
|
||||
*last_valid_corner_edge = orig_edge_i;
|
||||
if (first_valid_corner_vert == NULL) {
|
||||
first_valid_corner_vert = last_valid_corner_vert;
|
||||
}
|
||||
if (first_valid_corner_edge == NULL) {
|
||||
first_valid_corner_edge = last_valid_corner_edge;
|
||||
}
|
||||
c++;
|
||||
|
||||
/* We absolutely HAVE to handle edge index remapping here, otherwise potential newly
|
||||
* created edges in that part of code make remapping later totally unreliable. */
|
||||
BLI_assert(newe[orig_edge_i] != -1);
|
||||
*last_valid_corner_edge = newe[orig_edge_i];
|
||||
}
|
||||
else {
|
||||
if (last_valid_corner_vert != NULL) {
|
||||
need_edge_from_last_valid_ml = true;
|
||||
}
|
||||
else {
|
||||
need_edge_to_first_valid_ml = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (UNLIKELY(last_valid_corner_vert != NULL &&
|
||||
!ELEM(first_valid_corner_vert, NULL, last_valid_corner_vert) &&
|
||||
(need_edge_to_first_valid_ml || need_edge_from_last_valid_ml))) {
|
||||
/* We need to create a new edge between last valid loop and first valid one! */
|
||||
void **val_p;
|
||||
|
||||
uint v1 = (vtargetmap[*last_valid_corner_vert] != -1) ? vtargetmap[*last_valid_corner_vert] :
|
||||
*last_valid_corner_vert;
|
||||
uint v2 = (vtargetmap[*first_valid_corner_vert] != -1) ?
|
||||
vtargetmap[*first_valid_corner_vert] :
|
||||
*first_valid_corner_vert;
|
||||
BLI_assert(v1 != v2);
|
||||
if (BLI_edgehash_ensure_p(ehash, v1, v2, &val_p)) {
|
||||
*last_valid_corner_edge = POINTER_AS_INT(*val_p);
|
||||
}
|
||||
else {
|
||||
const int new_eidx = STACK_SIZE(medge);
|
||||
STACK_PUSH(olde, olde[*last_valid_corner_edge]);
|
||||
STACK_PUSH(medge, src_edges[*last_valid_corner_edge]);
|
||||
medge[new_eidx].v1 = *last_valid_corner_vert;
|
||||
medge[new_eidx].v2 = *first_valid_corner_vert;
|
||||
/* DO NOT change newe mapping,
|
||||
* could break actual values due to some deleted original edges. */
|
||||
*val_p = POINTER_FROM_INT(new_eidx);
|
||||
created_edges++;
|
||||
|
||||
*last_valid_corner_edge = new_eidx;
|
||||
}
|
||||
need_edge_to_first_valid_ml = need_edge_from_last_valid_ml = false;
|
||||
}
|
||||
|
||||
if (UNLIKELY(c == 0)) {
|
||||
BLI_assert(created_edges == 0);
|
||||
continue;
|
||||
}
|
||||
if (UNLIKELY(c < 3)) {
|
||||
STACK_DISCARD(oldl, c);
|
||||
STACK_DISCARD(corner_verts, c);
|
||||
STACK_DISCARD(corner_edges, c);
|
||||
if (created_edges > 0) {
|
||||
for (j = STACK_SIZE(medge) - created_edges; j < STACK_SIZE(medge); j++) {
|
||||
BLI_edgehash_remove(ehash, medge[j].v1, medge[j].v2, NULL);
|
||||
}
|
||||
STACK_DISCARD(olde, created_edges);
|
||||
STACK_DISCARD(medge, created_edges);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
mp_new = STACK_PUSH_RET_PTR(mpoly);
|
||||
*mp_new = *mp;
|
||||
mp_new->totloop = c;
|
||||
BLI_assert(mp_new->totloop >= 3);
|
||||
mp_new->loopstart = STACK_SIZE(corner_verts) - c;
|
||||
|
||||
STACK_PUSH(oldp, i);
|
||||
} /* End of the loop that tests polys. */
|
||||
|
||||
if (poly_gset) {
|
||||
// printf("hash quality %.6f\n", BLI_gset_calc_quality(poly_gset));
|
||||
|
||||
BLI_gset_free(poly_gset, NULL);
|
||||
MEM_freeN(poly_keys);
|
||||
}
|
||||
|
||||
/* Create new cddm. */
|
||||
result = BKE_mesh_new_nomain_from_template(
|
||||
mesh, totvert_final, STACK_SIZE(medge), 0, STACK_SIZE(corner_verts), STACK_SIZE(mpoly));
|
||||
|
||||
/* Update edge indices and copy customdata. */
|
||||
MEdge *new_med = medge;
|
||||
for (i = 0; i < result->totedge; i++, new_med++) {
|
||||
BLI_assert(newv[new_med->v1] != -1);
|
||||
new_med->v1 = newv[new_med->v1];
|
||||
BLI_assert(newv[new_med->v2] != -1);
|
||||
new_med->v2 = newv[new_med->v2];
|
||||
|
||||
/* Can happen in case vtargetmap contains some double chains, we do not support that. */
|
||||
BLI_assert(new_med->v1 != new_med->v2);
|
||||
|
||||
CustomData_copy_data(&mesh->edata, &result->edata, olde[i], i, 1);
|
||||
}
|
||||
|
||||
/* Update loop indices and copy customdata. */
|
||||
for (i = 0; i < result->totloop; i++) {
|
||||
/* Edge remapping has already be done in main loop handling part above. */
|
||||
BLI_assert(newv[corner_verts[i]] != -1);
|
||||
corner_verts[i] = newv[corner_verts[i]];
|
||||
|
||||
CustomData_copy_data(&mesh->ldata, &result->ldata, oldl[i], i, 1);
|
||||
}
|
||||
|
||||
/* Copy vertex customdata. */
|
||||
for (i = 0; i < result->totvert; i++) {
|
||||
CustomData_copy_data(&mesh->vdata, &result->vdata, oldv[i], i, 1);
|
||||
}
|
||||
|
||||
/* Copy poly customdata. */
|
||||
mp = mpoly;
|
||||
for (i = 0; i < result->totpoly; i++, mp++) {
|
||||
CustomData_copy_data(&mesh->pdata, &result->pdata, oldp[i], i, 1);
|
||||
}
|
||||
|
||||
/* Copy over data. #CustomData_add_layer can do this, need to look it up. */
|
||||
if (STACK_SIZE(medge)) {
|
||||
memcpy(BKE_mesh_edges_for_write(result), medge, sizeof(MEdge) * STACK_SIZE(medge));
|
||||
}
|
||||
if (STACK_SIZE(corner_verts)) {
|
||||
memcpy(BKE_mesh_corner_verts_for_write(result),
|
||||
corner_verts,
|
||||
sizeof(int) * STACK_SIZE(corner_verts));
|
||||
}
|
||||
if (STACK_SIZE(corner_edges)) {
|
||||
memcpy(BKE_mesh_corner_edges_for_write(result),
|
||||
corner_edges,
|
||||
sizeof(int) * STACK_SIZE(corner_edges));
|
||||
}
|
||||
if (STACK_SIZE(mpoly)) {
|
||||
memcpy(BKE_mesh_polys_for_write(result), mpoly, sizeof(MPoly) * STACK_SIZE(mpoly));
|
||||
}
|
||||
|
||||
MEM_freeN(medge);
|
||||
MEM_freeN(corner_verts);
|
||||
MEM_freeN(corner_edges);
|
||||
MEM_freeN(mpoly);
|
||||
|
||||
MEM_freeN(newv);
|
||||
MEM_freeN(newe);
|
||||
#ifdef USE_LOOPS
|
||||
MEM_freeN(newl);
|
||||
#endif
|
||||
|
||||
MEM_freeN(oldv);
|
||||
MEM_freeN(olde);
|
||||
MEM_freeN(oldl);
|
||||
MEM_freeN(oldp);
|
||||
|
||||
MEM_freeN(vert_tag);
|
||||
|
||||
BLI_edgehash_free(ehash, NULL);
|
||||
|
||||
if (poly_map != NULL) {
|
||||
MEM_freeN(poly_map);
|
||||
}
|
||||
if (poly_map_mem != NULL) {
|
||||
MEM_freeN(poly_map_mem);
|
||||
}
|
||||
|
||||
BKE_id_free(NULL, mesh);
|
||||
|
||||
return result;
|
||||
}
|
|
@ -115,11 +115,12 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
|||
Object *ob,
|
||||
const Mesh *mesh,
|
||||
const int axis,
|
||||
const bool use_correct_order_on_merge)
|
||||
const bool use_correct_order_on_merge,
|
||||
int **r_vert_merge_map,
|
||||
int *r_vert_merge_map_len)
|
||||
{
|
||||
const float tolerance_sq = mmd->tolerance * mmd->tolerance;
|
||||
const bool do_vtargetmap = (mmd->flag & MOD_MIR_NO_MERGE) == 0;
|
||||
int tot_vtargetmap = 0; /* total merge vertices */
|
||||
const bool do_vtargetmap = (mmd->flag & MOD_MIR_NO_MERGE) == 0 && r_vert_merge_map != nullptr;
|
||||
|
||||
const bool do_bisect = ((axis == 0 && mmd->flag & MOD_MIR_BISECT_AXIS_X) ||
|
||||
(axis == 1 && mmd->flag & MOD_MIR_BISECT_AXIS_Y) ||
|
||||
|
@ -132,7 +133,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
|||
float plane_co[3], plane_no[3];
|
||||
int i;
|
||||
int a, totshape;
|
||||
int *vtargetmap = nullptr, *vtmap_a = nullptr, *vtmap_b = nullptr;
|
||||
int *vtmap_a = nullptr, *vtmap_b = nullptr;
|
||||
|
||||
/* mtx is the mirror transformation */
|
||||
unit_m4(mtx);
|
||||
|
@ -210,11 +211,13 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
|||
|
||||
if (do_vtargetmap) {
|
||||
/* second half is filled with -1 */
|
||||
vtargetmap = static_cast<int *>(
|
||||
*r_vert_merge_map = static_cast<int *>(
|
||||
MEM_malloc_arrayN(maxVerts, sizeof(int[2]), "MOD_mirror tarmap"));
|
||||
|
||||
vtmap_a = vtargetmap;
|
||||
vtmap_b = vtargetmap + maxVerts;
|
||||
vtmap_a = *r_vert_merge_map;
|
||||
vtmap_b = *r_vert_merge_map + maxVerts;
|
||||
|
||||
*r_vert_merge_map_len = 0;
|
||||
}
|
||||
|
||||
/* mirror vertex coordinates */
|
||||
|
@ -242,7 +245,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
|||
if (UNLIKELY(len_squared_v3v3(positions[vert_index_prev], positions[vert_index]) <
|
||||
tolerance_sq)) {
|
||||
*vtmap_b = i;
|
||||
tot_vtargetmap++;
|
||||
(*r_vert_merge_map_len)++;
|
||||
|
||||
/* average location */
|
||||
mid_v3_v3v3(positions[vert_index], positions[vert_index_prev], positions[vert_index]);
|
||||
|
@ -259,7 +262,7 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
|||
if (UNLIKELY(len_squared_v3v3(positions[vert_index_prev], positions[vert_index]) <
|
||||
tolerance_sq)) {
|
||||
*vtmap_a = maxVerts + i;
|
||||
tot_vtargetmap++;
|
||||
(*r_vert_merge_map_len)++;
|
||||
|
||||
/* average location */
|
||||
mid_v3_v3v3(positions[vert_index], positions[vert_index_prev], positions[vert_index]);
|
||||
|
@ -444,10 +447,12 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
|||
if (flip_map) {
|
||||
for (i = 0; i < maxVerts; dvert++, i++) {
|
||||
/* merged vertices get both groups, others get flipped */
|
||||
if (use_correct_order_on_merge && do_vtargetmap && (vtargetmap[i + maxVerts] != -1)) {
|
||||
if (use_correct_order_on_merge && do_vtargetmap &&
|
||||
((*r_vert_merge_map)[i + maxVerts] != -1)) {
|
||||
BKE_defvert_flip_merged(dvert - maxVerts, flip_map, flip_map_len);
|
||||
}
|
||||
else if (!use_correct_order_on_merge && do_vtargetmap && (vtargetmap[i] != -1)) {
|
||||
else if (!use_correct_order_on_merge && do_vtargetmap &&
|
||||
((*r_vert_merge_map)[i] != -1)) {
|
||||
BKE_defvert_flip_merged(dvert, flip_map, flip_map_len);
|
||||
}
|
||||
else {
|
||||
|
@ -460,16 +465,6 @@ Mesh *BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(MirrorModifierData *mmd,
|
|||
}
|
||||
}
|
||||
|
||||
if (do_vtargetmap) {
|
||||
/* slow - so only call if one or more merge verts are found,
|
||||
* users may leave this on and not realize there is nothing to merge - campbell */
|
||||
if (tot_vtargetmap) {
|
||||
result = BKE_mesh_merge_verts(
|
||||
result, vtargetmap, tot_vtargetmap, MESH_MERGE_VERTS_DUMP_IF_MAPPED);
|
||||
}
|
||||
MEM_freeN(vtargetmap);
|
||||
}
|
||||
|
||||
if (mesh_bisect != nullptr) {
|
||||
BKE_id_free(nullptr, mesh_bisect);
|
||||
}
|
||||
|
|
|
@ -3661,19 +3661,19 @@ static void get_normalized_fcurve_bounds(FCurve *fcu,
|
|||
range);
|
||||
const short mapping_flag = ANIM_get_normalization_flags(ac);
|
||||
|
||||
const float min_height = 0.01f;
|
||||
const float height = BLI_rctf_size_y(r_bounds);
|
||||
if (height < min_height) {
|
||||
r_bounds->ymin -= (min_height - height) / 2;
|
||||
r_bounds->ymax += (min_height - height) / 2;
|
||||
}
|
||||
|
||||
float offset;
|
||||
const float unit_fac = ANIM_unit_mapping_get_factor(
|
||||
ac->scene, ale->id, fcu, mapping_flag, &offset);
|
||||
|
||||
r_bounds->ymin = (r_bounds->ymin + offset) * unit_fac;
|
||||
r_bounds->ymax = (r_bounds->ymax + offset) * unit_fac;
|
||||
|
||||
const float min_height = 0.01f;
|
||||
const float height = BLI_rctf_size_y(r_bounds);
|
||||
if (height < min_height) {
|
||||
r_bounds->ymin -= (min_height - height) / 2;
|
||||
r_bounds->ymax += (min_height - height) / 2;
|
||||
}
|
||||
}
|
||||
|
||||
static void get_gpencil_bounds(bGPDlayer *gpl, const float range[2], rctf *r_bounds)
|
||||
|
@ -3749,14 +3749,14 @@ static void add_region_padding(bContext *C, bAnimContext *ac, rctf *bounds)
|
|||
BLI_rctf_pad_y(bounds, ac->region->winy, pad_bottom, pad_top);
|
||||
}
|
||||
|
||||
/* Find the window region in the bAnimContext area and move it to bounds. */
|
||||
static void move_graph_view(bContext *C, bAnimContext *ac, rctf *bounds, const int smooth_viewtx)
|
||||
static ARegion *get_window_region(bAnimContext *ac)
|
||||
{
|
||||
LISTBASE_FOREACH (ARegion *, region, &ac->area->regionbase) {
|
||||
if (region->regiontype == RGN_TYPE_WINDOW) {
|
||||
UI_view2d_smooth_view(C, region, bounds, smooth_viewtx);
|
||||
return region;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
|
||||
|
@ -3768,6 +3768,12 @@ static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ARegion *window_region = get_window_region(&ac);
|
||||
|
||||
if (!window_region) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
const int filter = (ANIMFILTER_SEL | ANIMFILTER_NODUPLIS | ANIMFILTER_DATA_VISIBLE |
|
||||
ANIMFILTER_LIST_VISIBLE | ANIMFILTER_LIST_CHANNELS);
|
||||
|
@ -3805,8 +3811,13 @@ static int graphkeys_view_selected_channels_exec(bContext *C, wmOperator *op)
|
|||
|
||||
add_region_padding(C, &ac, &bounds);
|
||||
|
||||
if (ac.spacetype == SPACE_ACTION) {
|
||||
bounds.ymin = window_region->v2d.cur.ymin;
|
||||
bounds.ymax = window_region->v2d.cur.ymax;
|
||||
}
|
||||
|
||||
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
|
||||
move_graph_view(C, &ac, &bounds, smooth_viewtx);
|
||||
UI_view2d_smooth_view(C, window_region, &bounds, smooth_viewtx);
|
||||
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
|
||||
|
@ -3852,6 +3863,12 @@ static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const
|
|||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ARegion *window_region = get_window_region(&ac);
|
||||
|
||||
if (!window_region) {
|
||||
return OPERATOR_CANCELLED;
|
||||
}
|
||||
|
||||
ListBase anim_data = {NULL, NULL};
|
||||
const int filter = (ANIMFILTER_DATA_VISIBLE | ANIMFILTER_LIST_VISIBLE | ANIMFILTER_NODUPLIS |
|
||||
ANIMFILTER_LIST_CHANNELS);
|
||||
|
@ -3880,8 +3897,13 @@ static int graphkeys_channel_view_pick_invoke(bContext *C, wmOperator *op, const
|
|||
|
||||
add_region_padding(C, &ac, &bounds);
|
||||
|
||||
if (ac.spacetype == SPACE_ACTION) {
|
||||
bounds.ymin = window_region->v2d.cur.ymin;
|
||||
bounds.ymax = window_region->v2d.cur.ymax;
|
||||
}
|
||||
|
||||
const int smooth_viewtx = WM_operator_smooth_viewtx_get(op);
|
||||
move_graph_view(C, &ac, &bounds, smooth_viewtx);
|
||||
UI_view2d_smooth_view(C, window_region, &bounds, smooth_viewtx);
|
||||
|
||||
ANIM_animdata_freelist(&anim_data);
|
||||
|
||||
|
|
|
@ -822,7 +822,7 @@ static Mesh *remesh_symmetry_mirror(Object *ob, Mesh *mesh, eSymmetryAxes symmet
|
|||
mmd.flag &= MOD_MIR_AXIS_X << i;
|
||||
mesh_mirror_temp = mesh_mirror;
|
||||
mesh_mirror = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(
|
||||
&mmd, ob, mesh_mirror, axis, true);
|
||||
&mmd, ob, mesh_mirror, axis, true, nullptr, nullptr);
|
||||
if (mesh_mirror_temp != mesh_mirror) {
|
||||
BKE_id_free(nullptr, mesh_mirror_temp);
|
||||
}
|
||||
|
|
|
@ -440,7 +440,6 @@ static bool weight_paint_set(Object *ob, float paintweight)
|
|||
Mesh *me = static_cast<Mesh *>(ob->data);
|
||||
MDeformWeight *dw, *dw_prev;
|
||||
int vgroup_active, vgroup_mirror = -1;
|
||||
uint index;
|
||||
const bool topology = (me->editflag & ME_EDIT_MIRROR_TOPO) != 0;
|
||||
|
||||
/* mutually exclusive, could be made into a */
|
||||
|
@ -473,7 +472,7 @@ static bool weight_paint_set(Object *ob, float paintweight)
|
|||
const MPoly &poly = polys[i];
|
||||
uint fidx = poly.totloop - 1;
|
||||
|
||||
if ((paint_selmode == SCE_SELECT_FACE) && !(select_poly && select_poly[index])) {
|
||||
if ((paint_selmode == SCE_SELECT_FACE) && !(select_poly && select_poly[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -517,7 +516,7 @@ static bool weight_paint_set(Object *ob, float paintweight)
|
|||
|
||||
{
|
||||
MDeformVert *dv = dvert;
|
||||
for (index = me->totvert; index != 0; index--, dv++) {
|
||||
for (int index = me->totvert; index != 0; index--, dv++) {
|
||||
dv->flag = 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,4 +38,19 @@ std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
|
|||
float merge_distance,
|
||||
bool only_loose_edges);
|
||||
|
||||
/**
|
||||
* Merge Verts indicated in the targets map.
|
||||
*
|
||||
* This frees the given mesh and returns a new mesh.
|
||||
*
|
||||
* \param vert_dest_map: The table that maps vertices to target vertices. a value of -1
|
||||
* indicates a vertex is a target, and is to be kept.
|
||||
* This array is aligned with 'mesh->totvert'
|
||||
* \warning \a vert_merge_map must **not** contain any chained mapping (v1 -> v2 -> v3 etc.),
|
||||
* this is not supported and will likely generate corrupted geometry.
|
||||
*
|
||||
* \param vert_dest_map_len: The number of non '-1' values in vtargetmap. (not the size)
|
||||
*/
|
||||
Mesh *mesh_merge_verts(const Mesh &mesh, MutableSpan<int> vert_dest_map, int vert_dest_map_len);
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -318,7 +318,7 @@ static void weld_assert_poly_len(const WeldPoly *wp, const Span<WeldLoop> wloop)
|
|||
*
|
||||
* \return array with the context weld vertices.
|
||||
*/
|
||||
static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map,
|
||||
static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(MutableSpan<int> vert_dest_map,
|
||||
const int vert_kill_len)
|
||||
{
|
||||
Vector<WeldVert> wvert;
|
||||
|
@ -326,10 +326,20 @@ static Vector<WeldVert> weld_vert_ctx_alloc_and_setup(Span<int> vert_dest_map,
|
|||
|
||||
for (const int i : vert_dest_map.index_range()) {
|
||||
if (vert_dest_map[i] != OUT_OF_CONTEXT) {
|
||||
const int vert_dest = vert_dest_map[i];
|
||||
WeldVert wv{};
|
||||
wv.vert_dest = vert_dest_map[i];
|
||||
wv.vert_dest = vert_dest;
|
||||
wv.vert_orig = i;
|
||||
wvert.append(wv);
|
||||
|
||||
if (vert_dest_map[vert_dest] != vert_dest) {
|
||||
/* The target vertex is also part of the context and needs to be referenced.
|
||||
* #vert_dest_map could already indicate this from the beginning, but for better
|
||||
* compatibility, it is done here as well. */
|
||||
vert_dest_map[vert_dest] = vert_dest;
|
||||
wv.vert_orig = vert_dest;
|
||||
wvert.append(wv);
|
||||
}
|
||||
}
|
||||
}
|
||||
return wvert;
|
||||
|
@ -1841,6 +1851,11 @@ std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh,
|
|||
return create_merged_mesh(mesh, vert_dest_map, vert_kill_len);
|
||||
}
|
||||
|
||||
Mesh *mesh_merge_verts(const Mesh &mesh, MutableSpan<int> vert_dest_map, int vert_dest_map_len)
|
||||
{
|
||||
return create_merged_mesh(mesh, vert_dest_map, vert_dest_map_len);
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender::geometry
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -46,6 +47,10 @@
|
|||
#include "DEG_depsgraph.h"
|
||||
#include "DEG_depsgraph_query.h"
|
||||
|
||||
#include "GEO_mesh_merge_by_distance.hh"
|
||||
|
||||
using namespace blender;
|
||||
|
||||
static void initData(ModifierData *md)
|
||||
{
|
||||
ArrayModifierData *amd = (ArrayModifierData *)md;
|
||||
|
@ -755,7 +760,7 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
|
|||
if (new_i != -1) {
|
||||
/* We have to follow chains of doubles
|
||||
* (merge start/end especially is likely to create some),
|
||||
* those are not supported at all by BKE_mesh_merge_verts! */
|
||||
* those are not supported at all by `geometry::mesh_merge_verts`! */
|
||||
while (!ELEM(full_doubles_map[new_i], -1, new_i)) {
|
||||
new_i = full_doubles_map[new_i];
|
||||
}
|
||||
|
@ -769,8 +774,10 @@ static Mesh *arrayModifier_doArray(ArrayModifierData *amd,
|
|||
}
|
||||
}
|
||||
if (tot_doubles > 0) {
|
||||
result = BKE_mesh_merge_verts(
|
||||
result, full_doubles_map, tot_doubles, MESH_MERGE_VERTS_DUMP_IF_EQUAL);
|
||||
Mesh *tmp = result;
|
||||
result = geometry::mesh_merge_verts(
|
||||
*tmp, MutableSpan<int>{full_doubles_map, result->totvert}, tot_doubles);
|
||||
BKE_id_free(NULL, tmp);
|
||||
}
|
||||
MEM_freeN(full_doubles_map);
|
||||
}
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
* \ingroup modifiers
|
||||
*/
|
||||
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
#include "DNA_defaults.h"
|
||||
|
@ -33,6 +36,10 @@
|
|||
#include "MOD_modifiertypes.h"
|
||||
#include "MOD_ui_common.h"
|
||||
|
||||
#include "GEO_mesh_merge_by_distance.hh"
|
||||
|
||||
using namespace blender;
|
||||
|
||||
static void initData(ModifierData *md)
|
||||
{
|
||||
MirrorModifierData *mmd = (MirrorModifierData *)md;
|
||||
|
@ -58,6 +65,36 @@ static void updateDepsgraph(ModifierData *md, const ModifierUpdateDepsgraphConte
|
|||
}
|
||||
}
|
||||
|
||||
static Mesh *mirror_apply_on_axis(MirrorModifierData *mmd,
|
||||
Object *ob,
|
||||
Mesh *mesh,
|
||||
const int axis,
|
||||
const bool use_correct_order_on_merge)
|
||||
{
|
||||
int *vert_merge_map = nullptr;
|
||||
int vert_merge_map_len;
|
||||
Mesh *result = mesh;
|
||||
result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(
|
||||
mmd, ob, result, axis, use_correct_order_on_merge, &vert_merge_map, &vert_merge_map_len);
|
||||
|
||||
if (vert_merge_map) {
|
||||
/* Slow - so only call if one or more merge verts are found,
|
||||
* users may leave this on and not realize there is nothing to merge - campbell */
|
||||
|
||||
/* TODO(mano-wii): Polygons with all vertices merged are the ones that form duplicates.
|
||||
* Therefore the duplicate polygon test can be skipped. */
|
||||
if (vert_merge_map_len) {
|
||||
Mesh *tmp = result;
|
||||
result = geometry::mesh_merge_verts(
|
||||
*tmp, MutableSpan<int>{vert_merge_map, result->totvert}, vert_merge_map_len);
|
||||
BKE_id_free(NULL, tmp);
|
||||
}
|
||||
MEM_freeN(vert_merge_map);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static Mesh *mirrorModifier__doMirror(MirrorModifierData *mmd, Object *ob, Mesh *mesh)
|
||||
{
|
||||
Mesh *result = mesh;
|
||||
|
@ -65,13 +102,11 @@ static Mesh *mirrorModifier__doMirror(MirrorModifierData *mmd, Object *ob, Mesh
|
|||
|
||||
/* check which axes have been toggled and mirror accordingly */
|
||||
if (mmd->flag & MOD_MIR_AXIS_X) {
|
||||
result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(
|
||||
mmd, ob, result, 0, use_correct_order_on_merge);
|
||||
result = mirror_apply_on_axis(mmd, ob, result, 0, use_correct_order_on_merge);
|
||||
}
|
||||
if (mmd->flag & MOD_MIR_AXIS_Y) {
|
||||
Mesh *tmp = result;
|
||||
result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(
|
||||
mmd, ob, result, 1, use_correct_order_on_merge);
|
||||
result = mirror_apply_on_axis(mmd, ob, result, 1, use_correct_order_on_merge);
|
||||
if (tmp != mesh) {
|
||||
/* free intermediate results */
|
||||
BKE_id_free(nullptr, tmp);
|
||||
|
@ -79,8 +114,7 @@ static Mesh *mirrorModifier__doMirror(MirrorModifierData *mmd, Object *ob, Mesh
|
|||
}
|
||||
if (mmd->flag & MOD_MIR_AXIS_Z) {
|
||||
Mesh *tmp = result;
|
||||
result = BKE_mesh_mirror_apply_mirror_on_axis_for_modifier(
|
||||
mmd, ob, result, 2, use_correct_order_on_merge);
|
||||
result = mirror_apply_on_axis(mmd, ob, result, 2, use_correct_order_on_merge);
|
||||
if (tmp != mesh) {
|
||||
/* free intermediate results */
|
||||
BKE_id_free(nullptr, tmp);
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
|
||||
#include "BLI_bitmap.h"
|
||||
#include "BLI_math.h"
|
||||
#include "BLI_span.hh"
|
||||
|
||||
#include "BLT_translation.h"
|
||||
|
||||
|
@ -22,6 +23,7 @@
|
|||
#include "DNA_screen_types.h"
|
||||
|
||||
#include "BKE_context.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_query.h"
|
||||
#include "BKE_mesh.h"
|
||||
#include "BKE_screen.h"
|
||||
|
@ -42,6 +44,10 @@
|
|||
|
||||
#include "BLI_strict_flags.h"
|
||||
|
||||
#include "GEO_mesh_merge_by_distance.hh"
|
||||
|
||||
using namespace blender;
|
||||
|
||||
static void initData(ModifierData *md)
|
||||
{
|
||||
ScrewModifierData *ltmd = (ScrewModifierData *)md;
|
||||
|
@ -165,10 +171,16 @@ static Mesh *mesh_remove_doubles_on_axis(Mesh *result,
|
|||
}
|
||||
}
|
||||
}
|
||||
result = BKE_mesh_merge_verts(result,
|
||||
full_doubles_map,
|
||||
int(tot_doubles * (step_tot - 1)),
|
||||
MESH_MERGE_VERTS_DUMP_IF_MAPPED);
|
||||
|
||||
Mesh *tmp = result;
|
||||
|
||||
/* TODO(mano-wii): Polygons with all vertices merged are the ones that form duplicates.
|
||||
* Therefore the duplicate polygon test can be skipped. */
|
||||
result = geometry::mesh_merge_verts(*tmp,
|
||||
MutableSpan<int>{full_doubles_map, result->totvert},
|
||||
int(tot_doubles * (step_tot - 1)));
|
||||
|
||||
BKE_id_free(NULL, tmp);
|
||||
MEM_freeN(full_doubles_map);
|
||||
}
|
||||
|
||||
|
|
|
@ -463,15 +463,13 @@ static void wn_face_area(WeightedNormalModifierData *wnmd, WeightedNormalData *w
|
|||
const blender::Span<MPoly> polys = wn_data->polys;
|
||||
const blender::Span<int> corner_verts = wn_data->corner_verts;
|
||||
|
||||
int mp_index;
|
||||
|
||||
ModePair *face_area = static_cast<ModePair *>(
|
||||
MEM_malloc_arrayN(size_t(polys.size()), sizeof(*face_area), __func__));
|
||||
|
||||
ModePair *f_area = face_area;
|
||||
for (const int i : polys.index_range()) {
|
||||
f_area->val = BKE_mesh_calc_poly_area(&polys[i], &corner_verts[polys[i].loopstart], positions);
|
||||
f_area->index = mp_index;
|
||||
f_area->index = i;
|
||||
}
|
||||
|
||||
qsort(face_area, polys.size(), sizeof(*face_area), modepair_cmp_by_val_inverse);
|
||||
|
|
Loading…
Reference in New Issue