Fix T78664: Implement Mesh and Face Set boundary automasking in Multires

This implements the SCULPT_vertex_is_boundary and SCULPT_vertex_has_unique_face_set functions for PBVH_GRIDS, which makes features such as automasking now work in multires. It also fixes some other face sets related features in multires, like face set boundary smoothing.

This uses the BKE_subdiv_ccg_coarse_mesh_adjacency_info_get function to get the vertex indicies in the base mesh from multires. This way the API functions can get topology or face set information directly from it. In the future, these vertex indices can be used to get any other information from the base mesh from multires, like seams, sharp edges, disconnected elements IDs...

Reviewed By: sergey

Maniphest Tasks: T78664

Differential Revision: https://developer.blender.org/D8227
This commit is contained in:
Pablo Dobarro 2020-07-08 17:22:11 +02:00
parent bd84b2cbcc
commit e5ebaa9fd6
Notes: blender-bot 2023-02-14 05:59:31 +01:00
Referenced by issue #86995, Regression. Sculpting, face set painting with faceset boundary automasking works wrong
Referenced by issue #78664, Mesh Boundary and Face Sets Boundary automasking not working in sculpt mode with Multires
4 changed files with 183 additions and 41 deletions

View File

@ -313,6 +313,22 @@ void BKE_subdiv_ccg_neighbor_coords_get(const SubdivCCG *subdiv_ccg,
int BKE_subdiv_ccg_grid_to_face_index(const SubdivCCG *subdiv_ccg, const int grid_index);
typedef enum SubdivCCGAdjacencyType {
SUBDIV_CCG_ADJACENT_NONE,
SUBDIV_CCG_ADJACENT_VERTEX,
SUBDIV_CCG_ADJACENT_EDGE,
} SubdivCCGAdjacencyType;
/* Returns if a grid coordinates is adjacent to a coarse mesh edge, vertex or nothing. If it is
* adjacent to an edge, r_v1 and r_v2 will be set to the two vertices of that edge. If it is
* adjacent to a vertex, r_v1 and r_v2 will be the index of that vertex. */
SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
const MLoop *mloop,
const MPoly *mpoly,
int *r_v1,
int *r_v2);
/* Get array which is indexed by face index and contains index of a first grid of the face.
*
* The "ensure" version allocates the mapping if it's not know yet and stores it in the subdiv_ccg

View File

@ -1834,4 +1834,59 @@ const int *BKE_subdiv_ccg_start_face_grid_index_get(const SubdivCCG *subdiv_ccg)
return subdiv_ccg->cache_.start_face_grid_index;
}
static void adjacet_vertices_index_from_adjacent_edge(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
const MLoop *mloop,
const MPoly *mpoly,
int *r_v1,
int *r_v2)
{
const int grid_size_1 = subdiv_ccg->grid_size - 1;
const int poly_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, coord->grid_index);
const MPoly *p = &mpoly[poly_index];
*r_v1 = mloop[coord->grid_index].v;
if (coord->x == grid_size_1) {
const MLoop *next = ME_POLY_LOOP_NEXT(mloop, p, coord->grid_index);
*r_v2 = next->v;
}
if (coord->y == grid_size_1) {
const MLoop *prev = ME_POLY_LOOP_PREV(mloop, p, coord->grid_index);
*r_v2 = prev->v;
}
}
SubdivCCGAdjacencyType BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(const SubdivCCG *subdiv_ccg,
const SubdivCCGCoord *coord,
const MLoop *mloop,
const MPoly *mpoly,
int *r_v1,
int *r_v2)
{
const int grid_size_1 = subdiv_ccg->grid_size - 1;
if (is_corner_grid_coord(subdiv_ccg, coord)) {
if (coord->x == 0 && coord->y == 0) {
/* Grid corner in the center of a poly. */
return SUBDIV_CCG_ADJACENT_NONE;
}
if (coord->x == grid_size_1 && coord->y == grid_size_1) {
/* Grid corner adjacent to a coarse mesh vertex. */
*r_v1 = *r_v2 = mloop[coord->grid_index].v;
return SUBDIV_CCG_ADJACENT_VERTEX;
}
/* Grid corner adjacent to the middle of a coarse mesh edge. */
adjacet_vertices_index_from_adjacent_edge(subdiv_ccg, coord, mloop, mpoly, r_v1, r_v2);
return SUBDIV_CCG_ADJACENT_EDGE;
}
if (is_boundary_grid_coord(subdiv_ccg, coord)) {
if (!is_inner_edge_grid_coordinate(subdiv_ccg, coord)) {
/* Grid boundary adjacent to a coarse mesh edge. */
adjacet_vertices_index_from_adjacent_edge(subdiv_ccg, coord, mloop, mpoly, r_v1, r_v2);
return SUBDIV_CCG_ADJACENT_EDGE;
}
}
return SUBDIV_CCG_ADJACENT_NONE;
}
/** \} */

View File

@ -547,28 +547,79 @@ void SCULPT_visibility_sync_all_vertex_to_face_sets(SculptSession *ss)
}
}
bool sculpt_check_unique_face_set_in_base_mesh(SculptSession *ss, int index)
{
MeshElemMap *vert_map = &ss->pmap[index];
int face_set = -1;
for (int i = 0; i < ss->pmap[index].count; i++) {
if (face_set == -1) {
face_set = abs(ss->face_sets[vert_map->indices[i]]);
}
else {
if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) {
return false;
}
}
}
return true;
}
/* Checks if the face sets of the adjacent faces to the edge between v1 and v2 in the base mesh are
* equal. */
bool sculpt_check_unique_face_set_for_edge_in_base_mesh(SculptSession *ss, int v1, int v2)
{
MeshElemMap *vert_map = &ss->pmap[v1];
int p1 = -1, p2 = -1;
for (int i = 0; i < ss->pmap[v1].count; i++) {
MPoly *p = &ss->mpoly[vert_map->indices[i]];
for (int l = 0; l < p->totloop; l++) {
MLoop *loop = &ss->mloop[p->loopstart + l];
if (loop->v == v2) {
if (p1 == -1) {
p1 = vert_map->indices[i];
break;
}
else if (p2 == -1) {
p2 = vert_map->indices[i];
break;
}
}
}
}
if (p1 != -1 && p2 != -1) {
return abs(ss->face_sets[p1]) == (ss->face_sets[p2]);
}
return true;
}
bool SCULPT_vertex_has_unique_face_set(SculptSession *ss, int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
MeshElemMap *vert_map = &ss->pmap[index];
int face_set = -1;
for (int i = 0; i < ss->pmap[index].count; i++) {
if (face_set == -1) {
face_set = abs(ss->face_sets[vert_map->indices[i]]);
}
else {
if (abs(ss->face_sets[vert_map->indices[i]]) != face_set) {
return false;
}
}
}
return true;
return sculpt_check_unique_face_set_in_base_mesh(ss, index);
}
case PBVH_BMESH:
return false;
case PBVH_GRIDS:
return true;
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = index / key->grid_area;
const int vertex_index = index - grid_index * key->grid_area;
const SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
.y = vertex_index / key->grid_size};
int v1, v2;
const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2);
switch (adjacency) {
case SUBDIV_CCG_ADJACENT_VERTEX:
return sculpt_check_unique_face_set_in_base_mesh(ss, v1);
case SUBDIV_CCG_ADJACENT_EDGE:
return sculpt_check_unique_face_set_for_edge_in_base_mesh(ss, v1, v2);
case SUBDIV_CCG_ADJACENT_NONE:
return true;
}
}
}
return false;
}
@ -735,44 +786,64 @@ void SCULPT_vertex_neighbors_get(SculptSession *ss,
}
}
bool sculpt_check_boundary_vertex_in_base_mesh(SculptSession *ss, const int index)
{
const MeshElemMap *vert_map = &ss->pmap[index];
if (vert_map->count <= 1) {
return true;
}
for (int i = 0; i < vert_map->count; i++) {
const MPoly *p = &ss->mpoly[vert_map->indices[i]];
unsigned f_adj_v[2];
if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
int j;
for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
if (!(vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2)) {
return true;
}
}
}
}
return false;
}
bool SCULPT_vertex_is_boundary(SculptSession *ss, const int index)
{
switch (BKE_pbvh_type(ss->pbvh)) {
case PBVH_FACES: {
const MeshElemMap *vert_map = &ss->pmap[index];
if (vert_map->count <= 1) {
return false;
}
if (!SCULPT_vertex_all_face_sets_visible_get(ss, index)) {
return false;
return true;
}
for (int i = 0; i < vert_map->count; i++) {
const MPoly *p = &ss->mpoly[vert_map->indices[i]];
unsigned f_adj_v[2];
if (poly_get_adj_loops_from_vert(p, ss->mloop, index, f_adj_v) != -1) {
int j;
for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) {
if (!(vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2)) {
return false;
}
}
}
}
return true;
return sculpt_check_boundary_vertex_in_base_mesh(ss, index);
}
case PBVH_BMESH: {
BMVert *v = BM_vert_at_index(ss->bm, index);
return BM_vert_is_boundary(v);
}
case PBVH_GRIDS:
return true;
case PBVH_GRIDS: {
const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh);
const int grid_index = index / key->grid_area;
const int vertex_index = index - grid_index * key->grid_area;
const SubdivCCGCoord coord = {.grid_index = grid_index,
.x = vertex_index % key->grid_size,
.y = vertex_index / key->grid_size};
int v1, v2;
const SubdivCCGAdjacencyType adjacency = BKE_subdiv_ccg_coarse_mesh_adjacency_info_get(
ss->subdiv_ccg, &coord, ss->mloop, ss->mpoly, &v1, &v2);
switch (adjacency) {
case SUBDIV_CCG_ADJACENT_VERTEX:
return sculpt_check_boundary_vertex_in_base_mesh(ss, v1);
case SUBDIV_CCG_ADJACENT_EDGE:
return sculpt_check_boundary_vertex_in_base_mesh(ss, v1) &&
sculpt_check_boundary_vertex_in_base_mesh(ss, v2);
case SUBDIV_CCG_ADJACENT_NONE:
return false;
}
}
}
return true;
return false;
}
/* Utilities */

View File

@ -209,7 +209,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
{
SculptSession *ss = ob->sculpt;
if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
if (!ss->pmap) {
BLI_assert(!"Boundary Edges masking: pmap missing");
return NULL;
}
@ -221,7 +221,7 @@ float *SCULPT_boundary_automasking_init(Object *ob,
edge_distance[i] = EDGE_DISTANCE_INF;
switch (mode) {
case AUTOMASK_INIT_BOUNDARY_EDGES:
if (!SCULPT_vertex_is_boundary(ss, i)) {
if (SCULPT_vertex_is_boundary(ss, i)) {
edge_distance[i] = 0;
}
break;