Sculpt: Ensure faces are uniquely assigned to PBVHNodes

PBVH_FACES and PBVH_GRIDS do not store faces directly in nodes;
instead they store 'primitives', which are tesselation triangles
for PBVH_FACES and grids (which are per-loop) for PBVH_GRIDS.

Primitives from the same face could sometimes end up in different
PBVH nodes.  This is now prevented in two ways:

* All primitives of the same face are given the same boundary
  during PBVH build.  This prevents them from being swapped
  away from each other during partitioning.
* build_sub adjusts the final partition midpoint to fall
  between primitives of different faces.
This commit is contained in:
Joseph Eagar
2022-10-14 22:08:36 -07:00
parent bc06fcca47
commit 010c10febe
3 changed files with 193 additions and 4 deletions

View File

@@ -267,7 +267,8 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
void **gridfaces,
struct DMFlagMat *flagmats,
unsigned int **grid_hidden,
struct Mesh *me);
struct Mesh *me,
struct SubdivCCG *subdiv_ccg);
/**
* Build a PBVH from a BMesh.
*/

View File

@@ -2237,7 +2237,8 @@ static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect
(void **)subdiv_ccg->grid_faces,
subdiv_ccg->grid_flag_mats,
subdiv_ccg->grid_hidden,
base_mesh);
base_mesh,
subdiv_ccg);
pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets);
return pbvh;

View File

@@ -39,8 +39,10 @@
#define LEAF_LIMIT 10000
//#define PERFCNTRS
/* Uncomment to test that faces are only assigned to one PBVHNode */
//#define VALIDATE_UNIQUE_NODE_FACES
//#define PERFCNTRS
#define STACK_FIXED_DEPTH 100
typedef struct PBVHStack {
@@ -422,6 +424,56 @@ static bool leaf_needs_material_split(PBVH *pbvh, int offset, int count)
return false;
}
static int adjust_partition_faces(PBVH *pbvh, int offset, int mid, int count)
{
int poly = pbvh->looptri[pbvh->prim_indices[mid]].poly;
/* Scan backwards. */
while (mid > offset + 2) { /* First node should have at least 1 primitive */
if (pbvh->looptri[pbvh->prim_indices[mid - 1]].poly != poly) {
return mid;
}
mid--;
}
/* If that didn't work try scanning forward. */
while (mid < pbvh->totprim + count) {
if (pbvh->looptri[pbvh->prim_indices[mid]].poly != poly) {
break;
}
mid++;
}
return mid;
}
static int adjust_partition_grids(PBVH *pbvh, int offset, int mid, int count)
{
int poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid]);
/* Scan backwards. */
while (mid > offset + 2) { /* First node should have at least 1 primitive */
if (BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid - 1]) != poly) {
return mid;
}
mid--;
}
/* If that didn't work try scanning forward. */
while (mid < pbvh->totprim + count) {
if (BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, pbvh->prim_indices[mid]) != poly) {
break;
}
mid++;
}
return mid;
}
/* Recursively build a node in the tree
*
* vb is the voxel box around all of the primitives contained in
@@ -478,6 +530,13 @@ static void build_sub(PBVH *pbvh, int node_index, BB *cb, BBC *prim_bbc, int off
end = partition_indices_material(pbvh, offset, offset + count - 1);
}
if (pbvh->header.type == PBVH_FACES) {
end = adjust_partition_faces(pbvh, offset, end, count);
}
else {
end = adjust_partition_grids(pbvh, offset, end, count);
}
/* Build children */
build_sub(pbvh, pbvh->nodes[node_index].children_offset, NULL, prim_bbc, offset, end - offset);
build_sub(pbvh,
@@ -587,6 +646,73 @@ static void pbvh_draw_args_init(PBVH *pbvh, PBVH_GPU_Args *args, PBVHNode *node)
}
}
#ifdef VALIDATE_UNIQUE_NODE_FACES
static void pbvh_validate_node_prims(PBVH *pbvh)
{
int totface = 0;
if (pbvh->header.type == PBVH_BMESH) {
return;
}
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (!(node->flag & PBVH_Leaf)) {
continue;
}
for (int j = 0; j < node->totprim; j++) {
int poly;
if (pbvh->header.type == PBVH_FACES) {
poly = pbvh->looptri[node->prim_indices[j]].poly;
}
else {
poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, node->prim_indices[j]);
}
totface = max_ii(totface, poly + 1);
}
}
int *facemap = (int *)MEM_malloc_arrayN(totface, sizeof(*facemap), __func__);
for (int i = 0; i < totface; i++) {
facemap[i] = -1;
}
for (int i = 0; i < pbvh->totnode; i++) {
PBVHNode *node = pbvh->nodes + i;
if (!(node->flag & PBVH_Leaf)) {
continue;
}
for (int j = 0; j < node->totprim; j++) {
int poly;
if (pbvh->header.type == PBVH_FACES) {
poly = pbvh->looptri[node->prim_indices[j]].poly;
}
else {
poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, node->prim_indices[j]);
}
if (facemap[poly] != -1 && facemap[poly] != i) {
printf("%s: error: face spanned multiple nodes (old: %d new: %d)\n",
__func__,
facemap[poly],
i);
}
facemap[poly] = i;
}
}
MEM_SAFE_FREE(facemap);
}
#endif
void BKE_pbvh_build_mesh(PBVH *pbvh,
Mesh *mesh,
const MPoly *mpoly,
@@ -645,6 +771,32 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
BB_expand(&cb, bbc->bcentroid);
}
/* Ensure all primitives belonging to the same base face
* have the same bounds. This is needed to prevent them
* from being swapped away from each other inside the partition
* array.
*/
for (int i = 0; i < looptri_num; i++) {
const MLoopTri *lt = &looptri[i];
int poly = lt->poly;
BBC *bbc = prim_bbc + i;
int j = i + 1;
while (j < looptri_num && looptri[j].poly == poly) {
BBC *bbc2 = prim_bbc + j;
BB_expand((BB *)bbc, bbc2->bmin);
BB_expand((BB *)bbc, bbc2->bmax);
j++;
}
j = i + 1;
while (j < looptri_num && looptri[j].poly == poly) {
prim_bbc[j] = prim_bbc[i];
j++;
}
}
if (looptri_num) {
pbvh_build(pbvh, &cb, prim_bbc, looptri_num);
}
@@ -655,6 +807,10 @@ void BKE_pbvh_build_mesh(PBVH *pbvh,
memset(pbvh->vert_bitmap, 0, sizeof(bool) * totvert);
BKE_pbvh_update_active_vcol(pbvh, mesh);
#ifdef VALIDATE_UNIQUE_NODE_FACES
pbvh_validate_node_prims(pbvh);
#endif
}
void BKE_pbvh_build_grids(PBVH *pbvh,
@@ -664,7 +820,8 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
void **gridfaces,
DMFlagMat *flagmats,
BLI_bitmap **grid_hidden,
Mesh *me)
Mesh *me,
SubdivCCG *subdiv_ccg)
{
const int gridsize = key->grid_size;
@@ -675,6 +832,7 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
pbvh->totgrid = totgrid;
pbvh->gridkey = *key;
pbvh->grid_hidden = grid_hidden;
pbvh->subdiv_ccg = subdiv_ccg;
pbvh->leaf_limit = max_ii(LEAF_LIMIT / (gridsize * gridsize), 1);
/* We need the base mesh attribute layout for PBVH draw. */
@@ -706,11 +864,40 @@ void BKE_pbvh_build_grids(PBVH *pbvh,
BB_expand(&cb, bbc->bcentroid);
}
/* Ensure all primitives belonging to the same base face
* have the same bounds. This is needed to prevent them
* from being swapped away from each other inside the partition
* array.
*/
for (int i = 0; i < totgrid; i++) {
int poly = BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, i);
BBC *bbc = prim_bbc + i;
int j = i + 1;
while (j < totgrid && BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, j) == poly) {
BBC *bbc2 = prim_bbc + j;
BB_expand((BB *)bbc, bbc2->bmin);
BB_expand((BB *)bbc, bbc2->bmax);
j++;
}
j = i + 1;
while (j < totgrid && BKE_subdiv_ccg_grid_to_face_index(pbvh->subdiv_ccg, j) == poly) {
prim_bbc[j] = prim_bbc[i];
j++;
}
}
if (totgrid) {
pbvh_build(pbvh, &cb, prim_bbc, totgrid);
}
MEM_freeN(prim_bbc);
#ifdef VALIDATE_UNIQUE_NODE_FACES
pbvh_validate_node_prims(pbvh);
#endif
}
PBVH *BKE_pbvh_new(PBVHType type)