This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/bmesh/intern/bmesh_mesh_partial_update.c

405 lines
13 KiB
C

/* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bmesh
*
* Generate data needed for partially updating mesh information.
* Currently this is used for normals and tessellation.
*
* Transform is the obvious use case where there is no need to update normals or tessellation
* for geometry which has not been modified.
*
* In the future this could be integrated into GPU updates too.
*
* Kinds of Partial Geometry
* =========================
*
* All Tagged
* ----------
* Operate on everything that's tagged as well as connected geometry.
* see: #BM_mesh_partial_create_from_verts
*
* Grouped
* -------
* Operate on everything that is connected to both tagged and un-tagged.
* see: #BM_mesh_partial_create_from_verts_group_single
*
* Reduces computations when transforming isolated regions.
*
* Optionally support multiple groups since axis-mirror (for example)
* will transform vertices in different directions, as well as keeping centered vertices.
* see: #BM_mesh_partial_create_from_verts_group_multi
*
* \note Others can be added as needed.
*/
#include "DNA_object_types.h"
#include "MEM_guardedalloc.h"
#include "BLI_alloca.h"
#include "BLI_bitmap.h"
#include "BLI_math_vector.h"
#include "bmesh.h"
/**
* Grow by 1.5x (rounding up).
*
* \note Use conservative reallocation since the initial sizes reserved
* may be close to (or exactly) the number of elements needed.
*/
#define GROW(len_alloc) ((len_alloc) + ((len_alloc) - ((len_alloc) / 2)))
#define GROW_ARRAY(mem, len_alloc) \
{ \
mem = MEM_reallocN(mem, sizeof(*mem) * ((len_alloc) = GROW(len_alloc))); \
} \
((void)0)
#define GROW_ARRAY_AS_NEEDED(mem, len_alloc, index) \
if (UNLIKELY(len_alloc == index)) { \
GROW_ARRAY(mem, len_alloc); \
}
BLI_INLINE bool partial_elem_vert_ensure(BMPartialUpdate *bmpinfo,
BLI_bitmap *verts_tag,
BMVert *v)
{
const int i = BM_elem_index_get(v);
if (!BLI_BITMAP_TEST(verts_tag, i)) {
BLI_BITMAP_ENABLE(verts_tag, i);
GROW_ARRAY_AS_NEEDED(bmpinfo->verts, bmpinfo->verts_len_alloc, bmpinfo->verts_len);
bmpinfo->verts[bmpinfo->verts_len++] = v;
return true;
}
return false;
}
BLI_INLINE bool partial_elem_face_ensure(BMPartialUpdate *bmpinfo,
BLI_bitmap *faces_tag,
BMFace *f)
{
const int i = BM_elem_index_get(f);
if (!BLI_BITMAP_TEST(faces_tag, i)) {
BLI_BITMAP_ENABLE(faces_tag, i);
GROW_ARRAY_AS_NEEDED(bmpinfo->faces, bmpinfo->faces_len_alloc, bmpinfo->faces_len);
bmpinfo->faces[bmpinfo->faces_len++] = f;
return true;
}
return false;
}
BMPartialUpdate *BM_mesh_partial_create_from_verts(BMesh *bm,
const BMPartialUpdate_Params *params,
const BLI_bitmap *verts_mask,
const int verts_mask_count)
{
/* The caller is doing something wrong if this isn't the case. */
BLI_assert(verts_mask_count <= bm->totvert);
BMPartialUpdate *bmpinfo = MEM_callocN(sizeof(*bmpinfo), __func__);
/* Reserve more edges than vertices since it's common for a grid topology
* to use around twice as many edges as vertices. */
const int default_verts_len_alloc = verts_mask_count;
const int default_faces_len_alloc = min_ii(bm->totface, verts_mask_count);
/* Allocate tags instead of using #BM_ELEM_TAG because the caller may already be using tags.
* Further, walking over all geometry to clear the tags isn't so efficient. */
BLI_bitmap *verts_tag = NULL;
BLI_bitmap *faces_tag = NULL;
/* Set vert inline. */
BM_mesh_elem_index_ensure(bm, BM_FACE);
if (params->do_normals || params->do_tessellate) {
/* - Extend to all vertices connected faces:
* In the case of tessellation this is enough.
*
* In the case of vertex normal calculation,
* All the relevant connectivity data can be accessed from the faces
* (there is no advantage in storing connected edges or vertices in this pass).
*
* NOTE: In the future it may be useful to differentiate between vertices
* that are directly marked (by the filter function when looping over all vertices).
* And vertices marked from indirect connections.
* This would require an extra tag array, so avoid this unless it's needed.
*/
/* Faces. */
if (bmpinfo->faces == NULL) {
bmpinfo->faces_len_alloc = default_faces_len_alloc;
bmpinfo->faces = MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__);
faces_tag = BLI_BITMAP_NEW((size_t)bm->totface, __func__);
}
BMVert *v;
BMIter iter;
int i;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
BM_elem_index_set(v, i); /* set_inline */
if (!BLI_BITMAP_TEST(verts_mask, i)) {
continue;
}
BMEdge *e_iter = v->e;
if (e_iter != NULL) {
/* Loop over edges. */
BMEdge *e_first = v->e;
do {
BMLoop *l_iter = e_iter->l;
if (e_iter->l != NULL) {
BMLoop *l_first = e_iter->l;
/* Loop over radial loops. */
do {
if (l_iter->v == v) {
partial_elem_face_ensure(bmpinfo, faces_tag, l_iter->f);
}
} while ((l_iter = l_iter->radial_next) != l_first);
}
} while ((e_iter = BM_DISK_EDGE_NEXT(e_iter, v)) != e_first);
}
}
}
if (params->do_normals) {
/* - Extend to all faces vertices:
* Any changes to the faces normal needs to update all surrounding vertices.
*
* - Extend to all these vertices connected edges:
* These and needed to access those vertices edge vectors in normal calculation logic.
*/
/* Vertices. */
if (bmpinfo->verts == NULL) {
bmpinfo->verts_len_alloc = default_verts_len_alloc;
bmpinfo->verts = MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__);
verts_tag = BLI_BITMAP_NEW((size_t)bm->totvert, __func__);
}
for (int i = 0; i < bmpinfo->faces_len; i++) {
BMFace *f = bmpinfo->faces[i];
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
} while ((l_iter = l_iter->next) != l_first);
}
}
if (verts_tag) {
MEM_freeN(verts_tag);
}
if (faces_tag) {
MEM_freeN(faces_tag);
}
bmpinfo->params = *params;
return bmpinfo;
}
BMPartialUpdate *BM_mesh_partial_create_from_verts_group_single(
BMesh *bm,
const BMPartialUpdate_Params *params,
const BLI_bitmap *verts_mask,
const int verts_mask_count)
{
BMPartialUpdate *bmpinfo = MEM_callocN(sizeof(*bmpinfo), __func__);
BLI_bitmap *verts_tag = NULL;
BLI_bitmap *faces_tag = NULL;
/* It's not worth guessing a large number as isolated regions will allocate zero faces. */
const int default_faces_len_alloc = 1;
int face_tag_loop_len = 0;
if (params->do_normals || params->do_tessellate) {
/* Faces. */
if (bmpinfo->faces == NULL) {
bmpinfo->faces_len_alloc = default_faces_len_alloc;
bmpinfo->faces = MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__);
faces_tag = BLI_BITMAP_NEW((size_t)bm->totface, __func__);
}
BMFace *f;
BMIter iter;
int i;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
enum { SIDE_A = (1 << 0), SIDE_B = (1 << 1) } side_flag = 0;
BM_elem_index_set(f, i); /* set_inline */
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
const int j = BM_elem_index_get(l_iter->v);
side_flag |= BLI_BITMAP_TEST(verts_mask, j) ? SIDE_A : SIDE_B;
if (UNLIKELY(side_flag == (SIDE_A | SIDE_B))) {
partial_elem_face_ensure(bmpinfo, faces_tag, f);
face_tag_loop_len += f->len;
break;
}
} while ((l_iter = l_iter->next) != l_first);
}
}
if (params->do_normals) {
/* Extend to all faces vertices:
* Any changes to the faces normal needs to update all surrounding vertices. */
/* Over allocate using the total number of face loops. */
const int default_verts_len_alloc = min_ii(bm->totvert, max_ii(1, face_tag_loop_len));
/* Vertices. */
if (bmpinfo->verts == NULL) {
bmpinfo->verts_len_alloc = default_verts_len_alloc;
bmpinfo->verts = MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__);
verts_tag = BLI_BITMAP_NEW((size_t)bm->totvert, __func__);
}
for (int i = 0; i < bmpinfo->faces_len; i++) {
BMFace *f = bmpinfo->faces[i];
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
} while ((l_iter = l_iter->next) != l_first);
}
/* Loose vertex support, these need special handling as loose normals depend on location. */
if (bmpinfo->verts_len < verts_mask_count) {
BMVert *v;
BMIter iter;
int i;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
if (BLI_BITMAP_TEST(verts_mask, i) && (BM_vert_find_first_loop(v) == NULL)) {
partial_elem_vert_ensure(bmpinfo, verts_tag, v);
}
}
}
}
if (verts_tag) {
MEM_freeN(verts_tag);
}
if (faces_tag) {
MEM_freeN(faces_tag);
}
bmpinfo->params = *params;
return bmpinfo;
}
BMPartialUpdate *BM_mesh_partial_create_from_verts_group_multi(
BMesh *bm,
const BMPartialUpdate_Params *params,
const int *verts_group,
const int verts_group_count)
{
/* Provide a quick way of visualizing which faces are being manipulated. */
// #define DEBUG_MATERIAL
BMPartialUpdate *bmpinfo = MEM_callocN(sizeof(*bmpinfo), __func__);
BLI_bitmap *verts_tag = NULL;
BLI_bitmap *faces_tag = NULL;
/* It's not worth guessing a large number as isolated regions will allocate zero faces. */
const int default_faces_len_alloc = 1;
int face_tag_loop_len = 0;
if (params->do_normals || params->do_tessellate) {
/* Faces. */
if (bmpinfo->faces == NULL) {
bmpinfo->faces_len_alloc = default_faces_len_alloc;
bmpinfo->faces = MEM_mallocN((sizeof(BMFace *) * bmpinfo->faces_len_alloc), __func__);
faces_tag = BLI_BITMAP_NEW((size_t)bm->totface, __func__);
}
BMFace *f;
BMIter iter;
int i;
BM_ITER_MESH_INDEX (f, &iter, bm, BM_FACES_OF_MESH, i) {
BM_elem_index_set(f, i); /* set_inline */
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
const int group_test = verts_group[BM_elem_index_get(l_iter->prev->v)];
#ifdef DEBUG_MATERIAL
f->mat_nr = 0;
#endif
do {
const int group_iter = verts_group[BM_elem_index_get(l_iter->v)];
if (UNLIKELY((group_iter != group_test) || (group_iter == -1))) {
partial_elem_face_ensure(bmpinfo, faces_tag, f);
face_tag_loop_len += f->len;
#ifdef DEBUG_MATERIAL
f->mat_nr = 1;
#endif
break;
}
} while ((l_iter = l_iter->next) != l_first);
}
}
if (params->do_normals) {
/* Extend to all faces vertices:
* Any changes to the faces normal needs to update all surrounding vertices. */
/* Over allocate using the total number of face loops. */
const int default_verts_len_alloc = min_ii(bm->totvert, max_ii(1, face_tag_loop_len));
/* Vertices. */
if (bmpinfo->verts == NULL) {
bmpinfo->verts_len_alloc = default_verts_len_alloc;
bmpinfo->verts = MEM_mallocN((sizeof(BMVert *) * bmpinfo->verts_len_alloc), __func__);
verts_tag = BLI_BITMAP_NEW((size_t)bm->totvert, __func__);
}
for (int i = 0; i < bmpinfo->faces_len; i++) {
BMFace *f = bmpinfo->faces[i];
BMLoop *l_iter, *l_first;
l_iter = l_first = BM_FACE_FIRST_LOOP(f);
do {
partial_elem_vert_ensure(bmpinfo, verts_tag, l_iter->v);
} while ((l_iter = l_iter->next) != l_first);
}
/* Loose vertex support, these need special handling as loose normals depend on location. */
if (bmpinfo->verts_len < verts_group_count) {
BMVert *v;
BMIter iter;
int i;
BM_ITER_MESH_INDEX (v, &iter, bm, BM_VERTS_OF_MESH, i) {
if ((verts_group[i] != 0) && (BM_vert_find_first_loop(v) == NULL)) {
partial_elem_vert_ensure(bmpinfo, verts_tag, v);
}
}
}
}
if (verts_tag) {
MEM_freeN(verts_tag);
}
if (faces_tag) {
MEM_freeN(faces_tag);
}
bmpinfo->params = *params;
return bmpinfo;
}
void BM_mesh_partial_destroy(BMPartialUpdate *bmpinfo)
{
if (bmpinfo->verts) {
MEM_freeN(bmpinfo->verts);
}
if (bmpinfo->faces) {
MEM_freeN(bmpinfo->faces);
}
MEM_freeN(bmpinfo);
}