Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/bmesh/intern/bmesh_interp.c')
-rw-r--r--source/blender/bmesh/intern/bmesh_interp.c407
1 files changed, 332 insertions, 75 deletions
diff --git a/source/blender/bmesh/intern/bmesh_interp.c b/source/blender/bmesh/intern/bmesh_interp.c
index a32f28169f6..f745972293e 100644
--- a/source/blender/bmesh/intern/bmesh_interp.c
+++ b/source/blender/bmesh/intern/bmesh_interp.c
@@ -40,39 +40,43 @@
#include "BKE_customdata.h"
#include "BKE_multires.h"
+#include "BLI_memarena.h"
+#include "BLI_linklist.h"
#include "bmesh.h"
#include "intern/bmesh_private.h"
/* edge and vertex share, currently theres no need to have different logic */
-static void bm_data_interp_from_elem(CustomData *data_layer, BMElem *ele1, BMElem *ele2, BMElem *ele_dst, const float fac)
+static void bm_data_interp_from_elem(
+ CustomData *data_layer, const BMElem *ele_src_1, const BMElem *ele_src_2,
+ BMElem *ele_dst, const float fac)
{
- if (ele1->head.data && ele2->head.data) {
+ if (ele_src_1->head.data && ele_src_2->head.data) {
/* first see if we can avoid interpolation */
if (fac <= 0.0f) {
- if (ele1 == ele_dst) {
+ if (ele_src_1 == ele_dst) {
/* do nothing */
}
else {
CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data);
- CustomData_bmesh_copy_data(data_layer, data_layer, ele1->head.data, &ele_dst->head.data);
+ CustomData_bmesh_copy_data(data_layer, data_layer, ele_src_1->head.data, &ele_dst->head.data);
}
}
else if (fac >= 1.0f) {
- if (ele2 == ele_dst) {
+ if (ele_src_2 == ele_dst) {
/* do nothing */
}
else {
CustomData_bmesh_free_block_data(data_layer, ele_dst->head.data);
- CustomData_bmesh_copy_data(data_layer, data_layer, ele2->head.data, &ele_dst->head.data);
+ CustomData_bmesh_copy_data(data_layer, data_layer, ele_src_2->head.data, &ele_dst->head.data);
}
}
else {
- void *src[2];
+ const void *src[2];
float w[2];
- src[0] = ele1->head.data;
- src[1] = ele2->head.data;
+ src[0] = ele_src_1->head.data;
+ src[1] = ele_src_2->head.data;
w[0] = 1.0f - fac;
w[1] = fac;
CustomData_bmesh_interp(data_layer, src, w, NULL, 2, ele_dst->head.data);
@@ -83,25 +87,25 @@ static void bm_data_interp_from_elem(CustomData *data_layer, BMElem *ele1, BMEle
/**
* \brief Data, Interp From Verts
*
- * Interpolates per-vertex data from two sources to a target.
+ * Interpolates per-vertex data from two sources to \a v_dst
*
* \note This is an exact match to #BM_data_interp_from_edges
*/
-void BM_data_interp_from_verts(BMesh *bm, BMVert *v1, BMVert *v2, BMVert *v, const float fac)
+void BM_data_interp_from_verts(BMesh *bm, const BMVert *v_src_1, const BMVert *v_src_2, BMVert *v_dst, const float fac)
{
- bm_data_interp_from_elem(&bm->vdata, (BMElem *)v1, (BMElem *)v2, (BMElem *)v, fac);
+ bm_data_interp_from_elem(&bm->vdata, (const BMElem *)v_src_1, (const BMElem *)v_src_2, (BMElem *)v_dst, fac);
}
/**
* \brief Data, Interp From Edges
*
- * Interpolates per-edge data from two sources to a target.
+ * Interpolates per-edge data from two sources to \a e_dst.
*
* \note This is an exact match to #BM_data_interp_from_verts
*/
-void BM_data_interp_from_edges(BMesh *bm, BMEdge *e1, BMEdge *e2, BMEdge *e, const float fac)
+void BM_data_interp_from_edges(BMesh *bm, const BMEdge *e_src_1, const BMEdge *e_src_2, BMEdge *e_dst, const float fac)
{
- bm_data_interp_from_elem(&bm->edata, (BMElem *)e1, (BMElem *)e2, (BMElem *)e, fac);
+ bm_data_interp_from_elem(&bm->edata, (const BMElem *)e_src_1, (const BMElem *)e_src_2, (BMElem *)e_dst, fac);
}
/**
@@ -118,26 +122,26 @@ static void UNUSED_FUNCTION(BM_Data_Vert_Average)(BMesh *UNUSED(bm), BMFace *UNU
/**
* \brief Data Face-Vert Edge Interp
*
- * Walks around the faces of an edge and interpolates the per-face-edge
- * data between two sources to a target.
+ * Walks around the faces of \a e and interpolates
+ * the loop data between two sources.
*/
-void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BMVert *v, BMEdge *e1, const float fac)
+void BM_data_interp_face_vert_edge(
+ BMesh *bm, const BMVert *v_src_1, const BMVert *UNUSED(v_src_2), BMVert *v, BMEdge *e, const float fac)
{
- void *src[2];
float w[2];
BMLoop *l_v1 = NULL, *l_v = NULL, *l_v2 = NULL;
BMLoop *l_iter = NULL;
- if (!e1->l) {
+ if (!e->l) {
return;
}
w[1] = 1.0f - fac;
w[0] = fac;
- l_iter = e1->l;
+ l_iter = e->l;
do {
- if (l_iter->v == v1) {
+ if (l_iter->v == v_src_1) {
l_v1 = l_iter;
l_v = l_v1->next;
l_v2 = l_v->next;
@@ -148,14 +152,17 @@ void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BM
l_v2 = l_iter->prev;
}
- if (!l_v1 || !l_v2)
+ if (!l_v1 || !l_v2) {
return;
-
- src[0] = l_v1->head.data;
- src[1] = l_v2->head.data;
+ }
+ else {
+ const void *src[2];
+ src[0] = l_v1->head.data;
+ src[1] = l_v2->head.data;
- CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, l_v->head.data);
- } while ((l_iter = l_iter->radial_next) != e1->l);
+ CustomData_bmesh_interp(&bm->ldata, src, w, NULL, 2, l_v->head.data);
+ }
+ } while ((l_iter = l_iter->radial_next) != e->l);
}
/**
@@ -166,56 +173,57 @@ void BM_data_interp_face_vert_edge(BMesh *bm, BMVert *v1, BMVert *UNUSED(v2), BM
*
* \note Only handles loop customdata. multires is handled.
*/
-void BM_face_interp_from_face_ex(BMesh *bm, BMFace *target, BMFace *source, const bool do_vertex,
- void **blocks_l, void **blocks_v, float (*cos_2d)[2], float axis_mat[3][3])
+void BM_face_interp_from_face_ex(
+ BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex,
+ const void **blocks_l, const void **blocks_v, float (*cos_2d)[2], float axis_mat[3][3])
{
BMLoop *l_iter;
BMLoop *l_first;
- float *w = BLI_array_alloca(w, source->len);
+ float *w = BLI_array_alloca(w, f_src->len);
float co[2];
int i;
- if (source != target)
- BM_elem_attrs_copy(bm, bm, source, target);
+ if (f_src != f_dst)
+ BM_elem_attrs_copy(bm, bm, f_src, f_dst);
/* interpolate */
i = 0;
- l_iter = l_first = BM_FACE_FIRST_LOOP(target);
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f_dst);
do {
mul_v2_m3v3(co, axis_mat, l_iter->v->co);
- interp_weights_poly_v2(w, cos_2d, source->len, co);
- CustomData_bmesh_interp(&bm->ldata, blocks_l, w, NULL, source->len, l_iter->head.data);
+ interp_weights_poly_v2(w, cos_2d, f_src->len, co);
+ CustomData_bmesh_interp(&bm->ldata, blocks_l, w, NULL, f_src->len, l_iter->head.data);
if (do_vertex) {
- CustomData_bmesh_interp(&bm->vdata, blocks_v, w, NULL, source->len, l_iter->v->head.data);
+ CustomData_bmesh_interp(&bm->vdata, blocks_v, w, NULL, f_src->len, l_iter->v->head.data);
}
} while (i++, (l_iter = l_iter->next) != l_first);
}
-void BM_face_interp_from_face(BMesh *bm, BMFace *target, BMFace *source, const bool do_vertex)
+void BM_face_interp_from_face(BMesh *bm, BMFace *f_dst, const BMFace *f_src, const bool do_vertex)
{
BMLoop *l_iter;
BMLoop *l_first;
- void **blocks_l = BLI_array_alloca(blocks_l, source->len);
- void **blocks_v = do_vertex ? BLI_array_alloca(blocks_v, source->len) : NULL;
- float (*cos_2d)[2] = BLI_array_alloca(cos_2d, source->len);
+ const void **blocks_l = BLI_array_alloca(blocks_l, f_src->len);
+ const void **blocks_v = do_vertex ? BLI_array_alloca(blocks_v, f_src->len) : NULL;
+ float (*cos_2d)[2] = BLI_array_alloca(cos_2d, f_src->len);
float axis_mat[3][3]; /* use normal to transform into 2d xy coords */
int i;
/* convert the 3d coords into 2d for projection */
- BLI_assert(BM_face_is_normal_valid(source));
- axis_dominant_v3_to_m3(axis_mat, source->no);
+ BLI_assert(BM_face_is_normal_valid(f_src));
+ axis_dominant_v3_to_m3(axis_mat, f_src->no);
i = 0;
- l_iter = l_first = BM_FACE_FIRST_LOOP(source);
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f_src);
do {
mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
blocks_l[i] = l_iter->head.data;
if (do_vertex) blocks_v[i] = l_iter->v->head.data;
} while (i++, (l_iter = l_iter->next) != l_first);
- BM_face_interp_from_face_ex(bm, target, source, do_vertex,
+ BM_face_interp_from_face_ex(bm, f_dst, f_src, do_vertex,
blocks_l, blocks_v, cos_2d, axis_mat);
}
@@ -425,7 +433,7 @@ static void bm_loop_flip_disp(const float source_axis_x[3], const float source_a
disp[1] = (mat[0][0] * b[1] - b[0] * mat[1][0]) / d;
}
-static void bm_loop_interp_mdisps(BMesh *bm, BMLoop *l_dst, BMFace *f_src)
+static void bm_loop_interp_mdisps(BMesh *bm, BMLoop *l_dst, const BMFace *f_src)
{
const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS);
MDisps *md_dst;
@@ -609,36 +617,37 @@ void BM_face_multires_bounds_smooth(BMesh *bm, BMFace *f)
}
/**
- * project the multires grid in target onto source's set of multires grids
+ * project the multires grid in target onto f_src's set of multires grids
*/
-void BM_loop_interp_multires(BMesh *bm, BMLoop *target, BMFace *source)
+void BM_loop_interp_multires(BMesh *bm, BMLoop *l_dst, const BMFace *f_src)
{
- bm_loop_interp_mdisps(bm, target, source);
+ bm_loop_interp_mdisps(bm, l_dst, f_src);
}
/**
- * projects a single loop, target, onto source for customdata interpolation. multires is handled.
+ * projects a single loop, target, onto f_src for customdata interpolation. multires is handled.
* if do_vertex is true, target's vert data will also get interpolated.
*/
-void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source,
- const bool do_vertex, const bool do_multires)
+void BM_loop_interp_from_face(
+ BMesh *bm, BMLoop *l_dst, const BMFace *f_src,
+ const bool do_vertex, const bool do_multires)
{
BMLoop *l_iter;
BMLoop *l_first;
- void **vblocks = do_vertex ? BLI_array_alloca(vblocks, source->len) : NULL;
- void **blocks = BLI_array_alloca(blocks, source->len);
- float (*cos_2d)[2] = BLI_array_alloca(cos_2d, source->len);
- float *w = BLI_array_alloca(w, source->len);
+ const void **vblocks = do_vertex ? BLI_array_alloca(vblocks, f_src->len) : NULL;
+ const void **blocks = BLI_array_alloca(blocks, f_src->len);
+ float (*cos_2d)[2] = BLI_array_alloca(cos_2d, f_src->len);
+ float *w = BLI_array_alloca(w, f_src->len);
float axis_mat[3][3]; /* use normal to transform into 2d xy coords */
float co[2];
int i;
/* convert the 3d coords into 2d for projection */
- BLI_assert(BM_face_is_normal_valid(source));
- axis_dominant_v3_to_m3(axis_mat, source->no);
+ BLI_assert(BM_face_is_normal_valid(f_src));
+ axis_dominant_v3_to_m3(axis_mat, f_src->no);
i = 0;
- l_iter = l_first = BM_FACE_FIRST_LOOP(source);
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f_src);
do {
mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
blocks[i] = l_iter->head.data;
@@ -648,48 +657,48 @@ void BM_loop_interp_from_face(BMesh *bm, BMLoop *target, BMFace *source,
}
} while (i++, (l_iter = l_iter->next) != l_first);
- mul_v2_m3v3(co, axis_mat, target->v->co);
+ mul_v2_m3v3(co, axis_mat, l_dst->v->co);
/* interpolate */
- interp_weights_poly_v2(w, cos_2d, source->len, co);
- CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, source->len, target->head.data);
+ interp_weights_poly_v2(w, cos_2d, f_src->len, co);
+ CustomData_bmesh_interp(&bm->ldata, blocks, w, NULL, f_src->len, l_dst->head.data);
if (do_vertex) {
- CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, source->len, target->v->head.data);
+ CustomData_bmesh_interp(&bm->vdata, vblocks, w, NULL, f_src->len, l_dst->v->head.data);
}
if (do_multires) {
- bm_loop_interp_mdisps(bm, target, source);
+ bm_loop_interp_mdisps(bm, l_dst, f_src);
}
}
-void BM_vert_interp_from_face(BMesh *bm, BMVert *v, BMFace *source)
+void BM_vert_interp_from_face(BMesh *bm, BMVert *v_dst, const BMFace *f_src)
{
BMLoop *l_iter;
BMLoop *l_first;
- void **blocks = BLI_array_alloca(blocks, source->len);
- float (*cos_2d)[2] = BLI_array_alloca(cos_2d, source->len);
- float *w = BLI_array_alloca(w, source->len);
+ const void **blocks = BLI_array_alloca(blocks, f_src->len);
+ float (*cos_2d)[2] = BLI_array_alloca(cos_2d, f_src->len);
+ float *w = BLI_array_alloca(w, f_src->len);
float axis_mat[3][3]; /* use normal to transform into 2d xy coords */
float co[2];
int i;
/* convert the 3d coords into 2d for projection */
- BLI_assert(BM_face_is_normal_valid(source));
- axis_dominant_v3_to_m3(axis_mat, source->no);
+ BLI_assert(BM_face_is_normal_valid(f_src));
+ axis_dominant_v3_to_m3(axis_mat, f_src->no);
i = 0;
- l_iter = l_first = BM_FACE_FIRST_LOOP(source);
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f_src);
do {
mul_v2_m3v3(cos_2d[i], axis_mat, l_iter->v->co);
blocks[i] = l_iter->v->head.data;
} while (i++, (l_iter = l_iter->next) != l_first);
- mul_v2_m3v3(co, axis_mat, v->co);
+ mul_v2_m3v3(co, axis_mat, v_dst->co);
/* interpolate */
- interp_weights_poly_v2(w, cos_2d, source->len, co);
- CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, source->len, v->head.data);
+ interp_weights_poly_v2(w, cos_2d, f_src->len, co);
+ CustomData_bmesh_interp(&bm->vdata, blocks, w, NULL, f_src->len, v_dst->head.data);
}
static void update_data_blocks(BMesh *bm, CustomData *olddata, CustomData *data)
@@ -812,6 +821,7 @@ void BM_data_layer_free(BMesh *bm, CustomData *data, int type)
has_layer = CustomData_free_layer_active(data, type, 0);
/* assert because its expensive to realloc - better not do if layer isnt present */
BLI_assert(has_layer != false);
+ UNUSED_VARS_NDEBUG(has_layer);
update_data_blocks(bm, &olddata, data);
if (olddata.layers) MEM_freeN(olddata.layers);
@@ -831,7 +841,8 @@ void BM_data_layer_free_n(BMesh *bm, CustomData *data, int type, int n)
has_layer = CustomData_free_layer(data, type, 0, CustomData_get_layer_index_n(data, type, n));
/* assert because its expensive to realloc - better not do if layer isnt present */
BLI_assert(has_layer != false);
-
+ UNUSED_VARS_NDEBUG(has_layer);
+
update_data_blocks(bm, &olddata, data);
if (olddata.layers) MEM_freeN(olddata.layers);
}
@@ -893,3 +904,249 @@ void BM_elem_float_data_set(CustomData *cd, void *element, int type, const float
float *f = CustomData_bmesh_get(cd, ((BMHeader *)element)->data, type);
if (f) *f = val;
}
+
+/** \name Loop interpolation functions: BM_vert_loop_groups_data_layer_***
+ *
+ * Handling loop custom-data such as UV's, while keeping contiguous fans is rather tedious.
+ * Especially when a verts loops can have multiple CustomData layers,
+ * and each layer can have multiple (different) contiguous fans.
+ * Said differently, a single vertices loops may span multiple UV islands.
+ *
+ * These functions snapshot vertices loops, storing each contiguous fan in its own group.
+ * The caller can manipulate the loops, then re-combine the CustomData values.
+ *
+ * While these functions don't explicitly handle multiple layers at once,
+ * the caller can simply store its own list.
+ *
+ * \note Currently they are averaged back together (weighted by loop angle)
+ * but we could copy add other methods to re-combine CustomData-Loop-Fans.
+ *
+ * \{ */
+
+struct LoopWalkCtx {
+ /* same for all groups */
+ int type;
+ int cd_layer_offset;
+ const float *loop_weights;
+ MemArena *arena;
+
+ /* --- Per loop fan vars --- */
+
+ /* reference for this contiguous fan */
+ const void *data_ref;
+ int data_len;
+
+ /* accumulate 'LoopGroupCD.weight' to make unit length */
+ float weight_accum;
+
+ /* both arrays the size of the 'BM_vert_face_count(v)'
+ * each contiguous fan gets a slide of these arrays */
+ void **data_array;
+ int *data_index_array;
+ float *weight_array;
+};
+
+/* Store vars to pass into 'CustomData_bmesh_interp' */
+struct LoopGroupCD {
+ /* direct customdata pointer array */
+ void **data;
+ /* weights (aligned with 'data') */
+ float *data_weights;
+ /* index-in-face */
+ int *data_index;
+ /* number of loops in the fan */
+ int data_len;
+};
+
+static void bm_loop_walk_add(struct LoopWalkCtx *lwc, BMLoop *l)
+{
+ const int i = BM_elem_index_get(l);
+ const float w = lwc->loop_weights[i];
+ BM_elem_flag_enable(l, BM_ELEM_INTERNAL_TAG);
+ lwc->data_array[lwc->data_len] = BM_ELEM_CD_GET_VOID_P(l, lwc->cd_layer_offset);
+ lwc->data_index_array[lwc->data_len] = i;
+ lwc->weight_array[lwc->data_len] = w;
+ lwc->weight_accum += w;
+
+ lwc->data_len += 1;
+}
+
+/**
+ * called recursively, keep stack-usage minimal.
+ *
+ * \note called for fan matching so we're pretty much safe not to break the stack
+ */
+static void bm_loop_walk_data(struct LoopWalkCtx *lwc, BMLoop *l_walk)
+{
+ int i;
+
+ BLI_assert(CustomData_data_equals(lwc->type, lwc->data_ref, BM_ELEM_CD_GET_VOID_P(l_walk, lwc->cd_layer_offset)));
+ BLI_assert(BM_elem_flag_test(l_walk, BM_ELEM_INTERNAL_TAG) == false);
+
+ bm_loop_walk_add(lwc, l_walk);
+
+ /* recurse around this loop-fan (in both directions) */
+ for (i = 0; i < 2; i++) {
+ BMLoop *l_other = ((i == 0) ? l_walk : l_walk->prev)->radial_next;
+ if (l_other->radial_next != l_other) {
+ if (l_other->v != l_walk->v) {
+ l_other = l_other->next;
+ }
+ BLI_assert(l_other->v == l_walk->v);
+ if (!BM_elem_flag_test(l_other, BM_ELEM_INTERNAL_TAG)) {
+ if (CustomData_data_equals(lwc->type, lwc->data_ref, BM_ELEM_CD_GET_VOID_P(l_other, lwc->cd_layer_offset))) {
+ bm_loop_walk_data(lwc, l_other);
+ }
+ }
+ }
+ }
+}
+
+LinkNode *BM_vert_loop_groups_data_layer_create(
+ BMesh *bm, BMVert *v, const int layer_n, const float *loop_weights, MemArena *arena)
+{
+ struct LoopWalkCtx lwc;
+ LinkNode *groups = NULL;
+ BMLoop *l;
+ BMIter liter;
+ int loop_num;
+
+
+ lwc.type = bm->ldata.layers[layer_n].type;
+ lwc.cd_layer_offset = bm->ldata.layers[layer_n].offset;
+ lwc.loop_weights = loop_weights;
+ lwc.arena = arena;
+
+ loop_num = 0;
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ BM_elem_flag_disable(l, BM_ELEM_INTERNAL_TAG);
+ BM_elem_index_set(l, loop_num); /* set_dirty! */
+ loop_num++;
+ }
+ bm->elem_index_dirty |= BM_LOOP;
+
+ lwc.data_len = 0;
+ lwc.data_array = BLI_memarena_alloc(lwc.arena, sizeof(void *) * loop_num);
+ lwc.data_index_array = BLI_memarena_alloc(lwc.arena, sizeof(int) * loop_num);
+ lwc.weight_array = BLI_memarena_alloc(lwc.arena, sizeof(float) * loop_num);
+
+ BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) {
+ if (!BM_elem_flag_test(l, BM_ELEM_INTERNAL_TAG)) {
+ struct LoopGroupCD *lf = BLI_memarena_alloc(lwc.arena, sizeof(*lf));
+ int len_prev = lwc.data_len;
+
+ lwc.data_ref = BM_ELEM_CD_GET_VOID_P(l, lwc.cd_layer_offset);
+
+ /* assign len-last */
+ lf->data = &lwc.data_array[lwc.data_len];
+ lf->data_index = &lwc.data_index_array[lwc.data_len];
+ lf->data_weights = &lwc.weight_array[lwc.data_len];
+ lwc.weight_accum = 0.0f;
+
+ /* new group */
+ bm_loop_walk_data(&lwc, l);
+ lf->data_len = lwc.data_len - len_prev;
+
+ if (LIKELY(lwc.weight_accum != 0.0f)) {
+ mul_vn_fl(lf->data_weights, lf->data_len, 1.0f / lwc.weight_accum);
+ }
+ else {
+ fill_vn_fl(lf->data_weights, lf->data_len, 1.0f / (float)lf->data_len);
+ }
+
+ BLI_linklist_prepend_arena(&groups, lf, lwc.arena);
+ }
+ }
+
+ BLI_assert(lwc.data_len == loop_num);
+
+ return groups;
+}
+
+static void bm_vert_loop_groups_data_layer_merge__single(
+ BMesh *bm, void *lf_p, int layer_n,
+ void *data_tmp)
+{
+ struct LoopGroupCD *lf = lf_p;
+ const int type = bm->ldata.layers[layer_n].type;
+ int i;
+ const float *data_weights;
+
+ data_weights = lf->data_weights;
+
+ CustomData_bmesh_interp_n(
+ &bm->ldata, (const void **)lf->data,
+ data_weights, NULL, lf->data_len, data_tmp, layer_n);
+
+ for (i = 0; i < lf->data_len; i++) {
+ CustomData_copy_elements(type, data_tmp, lf->data[i], 1);
+ }
+}
+
+static void bm_vert_loop_groups_data_layer_merge_weights__single(
+ BMesh *bm, void *lf_p, const int layer_n, void *data_tmp,
+ const float *loop_weights)
+{
+ struct LoopGroupCD *lf = lf_p;
+ const int type = bm->ldata.layers[layer_n].type;
+ int i;
+ const float *data_weights;
+
+ /* re-weight */
+ float *temp_weights = BLI_array_alloca(temp_weights, lf->data_len);
+ float weight_accum = 0.0f;
+
+ for (i = 0; i < lf->data_len; i++) {
+ float w = loop_weights[lf->data_index[i]] * lf->data_weights[i];
+ temp_weights[i] = w;
+ weight_accum += w;
+ }
+
+ if (LIKELY(weight_accum != 0.0f)) {
+ mul_vn_fl(temp_weights, lf->data_len, 1.0f / weight_accum);
+ data_weights = temp_weights;
+ }
+ else {
+ data_weights = lf->data_weights;
+ }
+
+ CustomData_bmesh_interp_n(
+ &bm->ldata, (const void **)lf->data,
+ data_weights, NULL, lf->data_len, data_tmp, layer_n);
+
+ for (i = 0; i < lf->data_len; i++) {
+ CustomData_copy_elements(type, data_tmp, lf->data[i], 1);
+ }
+}
+
+/**
+ * Take existing custom data and merge each fan's data.
+ */
+void BM_vert_loop_groups_data_layer_merge(BMesh *bm, LinkNode *groups, const int layer_n)
+{
+ const int type = bm->ldata.layers[layer_n].type;
+ const int size = CustomData_sizeof(type);
+ void *data_tmp = alloca(size);
+
+ do {
+ bm_vert_loop_groups_data_layer_merge__single(bm, groups->link, layer_n, data_tmp);
+ } while ((groups = groups->next));
+}
+
+/**
+ * A version of #BM_vert_loop_groups_data_layer_merge
+ * that takes an array of loop-weights (aligned with #BM_LOOPS_OF_VERT iterator)
+ */
+void BM_vert_loop_groups_data_layer_merge_weights(
+ BMesh *bm, LinkNode *groups, const int layer_n, const float *loop_weights)
+{
+ const int type = bm->ldata.layers[layer_n].type;
+ const int size = CustomData_sizeof(type);
+ void *data_tmp = alloca(size);
+
+ do {
+ bm_vert_loop_groups_data_layer_merge_weights__single(bm, groups->link, layer_n, data_tmp, loop_weights);
+ } while ((groups = groups->next));
+}
+
+/** \} */