[Bf-blender-cvs] [e06a346458f] master: Fix T78747: Fix mesh boundary detection and automasking

Pablo Dobarro noreply at git.blender.org
Wed Jul 15 16:37:10 CEST 2020


Commit: e06a346458fa898e137cc984f23975f5572d94fb
Author: Pablo Dobarro
Date:   Wed Jul 15 16:24:03 2020 +0200
Branches: master
https://developer.blender.org/rBe06a346458fa898e137cc984f23975f5572d94fb

Fix T78747: Fix mesh boundary detection and automasking

This issue was produced by a hack in the sculpt mode code from 2.80
 when the sculpt API for connectivity info was not available.
The smooth brush was the only brush that needed connectivity info,
so there were 3 different smooth functions with the connectivity
queries implemented for dyntopo, meshes and grids. The mesh version
of smoothing was checking the number of connected faces to a vertex
to mask the mesh boundaries, which was not covering all cases and
was hardcoded in the smooth function itself.

This patch removes all those legacy functions and unifies all
smooth functions into a single one using the new API and the
automasking system. In order to achieve this, there were needed
some extra changes:

- The smooth brush now does not automasks the boundaries by default,
so its default preset needs to be updated to enable automasking

- The mesh boundary info is extracted once and cached in a
bitmap, similar to the disconnected elements IDs. This makes
boundary detection work as expected in all cases, solving a lot
of known issues with the smooth brush. In multires, this info is
extracted and cached only at the base mesh level, so it is much
more memory efficient than the previous automasking system.

- In order to keep the brushes responsive as they were before,
the automasking system can now skip creating the cache when it
is not needed for the requested options. This means that for
high poly meshes and simple automasking options the brushes
won't lag on start.

Reviewed By: sergey

Maniphest Tasks: T78747

Differential Revision: https://developer.blender.org/D8260

===================================================================

M	source/blender/blenkernel/BKE_paint.h
M	source/blender/blenkernel/intern/brush.c
M	source/blender/blenkernel/intern/paint.c
M	source/blender/editors/sculpt_paint/sculpt.c
M	source/blender/editors/sculpt_paint/sculpt_automasking.c
M	source/blender/editors/sculpt_paint/sculpt_filter_mesh.c
M	source/blender/editors/sculpt_paint/sculpt_intern.h
M	source/blender/editors/sculpt_paint/sculpt_smooth.c

===================================================================

diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h
index e08d3fe26fb..e3a6fb4ba2e 100644
--- a/source/blender/blenkernel/BKE_paint.h
+++ b/source/blender/blenkernel/BKE_paint.h
@@ -24,6 +24,7 @@
  * \ingroup bke
  */
 
+#include "BLI_bitmap.h"
 #include "BLI_utildefines.h"
 #include "DNA_object_enums.h"
 
@@ -291,6 +292,9 @@ typedef struct SculptPersistentBase {
 typedef struct SculptVertexInfo {
   /* Idexed by vertex, stores and ID of its topologycally connected component. */
   int *connected_component;
+
+  /* Indexed by base mesh vertex index, stores if that vertex is a boundary. */
+  BLI_bitmap *boundary;
 } SculptVertexInfo;
 
 typedef struct SculptFakeNeighbors {
diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c
index 522bf42105c..a85617680d2 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -1533,6 +1533,7 @@ void BKE_brush_sculpt_reset(Brush *br)
       break;
     case SCULPT_TOOL_SMOOTH:
       br->flag &= ~BRUSH_SPACE_ATTEN;
+      br->automasking_flags |= BRUSH_AUTOMASKING_BOUNDARY_EDGES;
       br->spacing = 5;
       br->alpha = 0.7f;
       br->surface_smooth_shape_preservation = 0.5f;
diff --git a/source/blender/blenkernel/intern/paint.c b/source/blender/blenkernel/intern/paint.c
index dca2022382a..50feb8e99c8 100644
--- a/source/blender/blenkernel/intern/paint.c
+++ b/source/blender/blenkernel/intern/paint.c
@@ -1369,6 +1369,8 @@ void BKE_sculptsession_free(Object *ob)
     MEM_SAFE_FREE(ss->preview_vert_index_list);
 
     MEM_SAFE_FREE(ss->vertex_info.connected_component);
+    MEM_SAFE_FREE(ss->vertex_info.boundary);
+
     MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index);
 
     if (ss->pose_ik_chain_preview) {
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index ffdf2c1e7c0..02b736c00c2 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -389,7 +389,7 @@ bool SCULPT_vertex_any_face_set_visible_get(SculptSession *ss, int index)
   return true;
 }
 
-bool SCULPT_vertex_all_face_sets_visible_get(SculptSession *ss, int index)
+bool SCULPT_vertex_all_face_sets_visible_get(const SculptSession *ss, int index)
 {
   switch (BKE_pbvh_type(ss->pbvh)) {
     case PBVH_FACES: {
@@ -788,28 +788,13 @@ void SCULPT_vertex_neighbors_get(SculptSession *ss,
   }
 }
 
-static bool sculpt_check_boundary_vertex_in_base_mesh(SculptSession *ss, const int index)
+static bool sculpt_check_boundary_vertex_in_base_mesh(const 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;
+  BLI_assert(ss->vertex_info.boundary);
+  return BLI_BITMAP_TEST(ss->vertex_info.boundary, index);
 }
 
-bool SCULPT_vertex_is_boundary(SculptSession *ss, const int index)
+bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index)
 {
   switch (BKE_pbvh_type(ss->pbvh)) {
     case PBVH_FACES: {
@@ -8488,6 +8473,37 @@ static void sculpt_connected_components_ensure(Object *ob)
   }
 }
 
+void SCULPT_boundary_info_ensure(Object *object)
+{
+  SculptSession *ss = object->sculpt;
+  if (ss->vertex_info.boundary) {
+    return;
+  }
+
+  Mesh *base_mesh = BKE_mesh_from_object(object);
+  ss->vertex_info.boundary = BLI_BITMAP_NEW(base_mesh->totvert, "Boundary info");
+  int *adjacent_faces_edge_count = MEM_calloc_arrayN(
+      base_mesh->totedge, sizeof(int), "Adjacent face edge count");
+
+  for (int p = 0; p < base_mesh->totpoly; p++) {
+    MPoly *poly = &base_mesh->mpoly[p];
+    for (int l = 0; l < poly->totloop; l++) {
+      MLoop *loop = &base_mesh->mloop[l + poly->loopstart];
+      adjacent_faces_edge_count[loop->e]++;
+    }
+  }
+
+  for (int e = 0; e < base_mesh->totedge; e++) {
+    if (adjacent_faces_edge_count[e] < 2) {
+      MEdge *edge = &base_mesh->medge[e];
+      BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v1, true);
+      BLI_BITMAP_SET(ss->vertex_info.boundary, edge->v2, true);
+    }
+  }
+
+  MEM_freeN(adjacent_faces_edge_count);
+}
+
 void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist)
 {
   SculptSession *ss = ob->sculpt;
diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c
index 39a480756d8..4b016b3df4d 100644
--- a/source/blender/editors/sculpt_paint/sculpt_automasking.c
+++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c
@@ -89,19 +89,65 @@ bool SCULPT_is_automasking_enabled(const Sculpt *sd, const SculptSession *ss, co
   return false;
 }
 
+int SCULPT_automasking_mode_effective_bits(const Sculpt *sculpt, const Brush *brush)
+{
+  return sculpt->automasking_flags | brush->automasking_flags;
+}
+
+static bool SCULPT_automasking_needs_cache(const Sculpt *sd, const Brush *brush)
+{
+
+  const int automasking_flags = SCULPT_automasking_mode_effective_bits(sd, brush);
+  if (automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
+    return true;
+  }
+  if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
+    return brush->automasking_boundary_edges_propagation_steps != 1;
+  }
+  if (automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
+    return brush->automasking_boundary_edges_propagation_steps != 1;
+  }
+  return false;
+}
+
 float SCULPT_automasking_factor_get(SculptSession *ss, int vert)
 {
-  if (ss->cache && ss->cache->automask) {
-    return ss->cache->automask[vert];
+  if (!ss->cache) {
+    return 1.0f;
+  }
+  /* If the cache is initialized with valid info, use the cache. This is used when the
+   * automasking information can't be computed in real time per vertex and needs to be
+   * initialized for the whole mesh when the stroke starts. */
+  if (ss->cache->automask_factor) {
+    return ss->cache->automask_factor[vert];
+  }
+
+  if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_FACE_SETS) {
+    if (!SCULPT_vertex_has_face_set(ss, vert, ss->cache->automask_settings.initial_face_set)) {
+      return 0.0f;
+    }
   }
+
+  if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) {
+    if (SCULPT_vertex_is_boundary(ss, vert)) {
+      return 0.0f;
+    }
+  }
+
+  if (ss->cache->automask_settings.flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS) {
+    if (!SCULPT_vertex_has_unique_face_set(ss, vert)) {
+      return 0.0f;
+    }
+  }
+
   return 1.0f;
 }
 
 void SCULPT_automasking_end(Object *ob)
 {
   SculptSession *ss = ob->sculpt;
-  if (ss->cache && ss->cache->automask) {
-    MEM_freeN(ss->cache->automask);
+  if (ss->cache && ss->cache->automask_factor) {
+    MEM_freeN(ss->cache->automask_factor);
   }
 }
 
@@ -153,7 +199,7 @@ static float *SCULPT_topology_automasking_init(Sculpt *sd, Object *ob, float *au
 
   const int totvert = SCULPT_vertex_count_get(ss);
   for (int i = 0; i < totvert; i++) {
-    ss->cache->automask[i] = 0.0f;
+    ss->cache->automask_factor[i] = 0.0f;
   }
 
   /* Flood fill automask to connected vertices. Limited to vertices inside
@@ -259,6 +305,14 @@ float *SCULPT_boundary_automasking_init(Object *ob,
   return automask_factor;
 }
 
+static void SCULPT_stroke_automasking_settings_update(SculptSession *ss, Sculpt *sd, Brush *brush)
+{
+  BLI_assert(ss->cache);
+
+  ss->cache->automask_settings.flags = SCULPT_automasking_mode_effective_bits(sd, brush);
+  ss->cache->automask_settings.initial_face_set = SCULPT_active_face_set_get(ss);
+}
+
 void SCULPT_automasking_init(Sculpt *sd, Object *ob)
 {
   SculptSession *ss = ob->sculpt;
@@ -269,20 +323,26 @@ void SCULPT_automasking_init(Sculpt *sd, Object *ob)
     return;
   }
 
-  ss->cache->automask = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss),
-                                    "automask_factor");
+  SCULPT_stroke_automasking_settings_update(ss, sd, brush);
+  SCULPT_boundary_info_ensure(ob);
+
+  if (!SCULPT_automasking_needs_cache(sd, brush)) {
+    return;
+  }
+
+  ss->cache->automask_factor = MEM_malloc_arrayN(totvert, sizeof(float), "automask_factor");
 
   for (int i = 0; i < totvert; i++) {
-    ss->cache->automask[i] = 1.0f;
+    ss->cache->automask_factor[i] = 1.0f;
   }
 
   if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_TOPOLOGY)) {
     SCULPT_vertex_random_access_init(ss);
-    SCULPT_topology_automasking_init(sd, ob, ss->cache->automask);
+    SCULPT_topology_automasking_init(sd, ob, ss->cache->automask_factor);
   }
   if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_FACE_SETS)) {
     SCULPT_vertex_random_access_init(ss);
-    sculpt_face_sets_automasking_init(sd, ob, ss->cache->automask);
+    sculpt_face_sets_automasking_init(sd, ob, ss->cache->automask_factor);
   }
 
   if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
@@ -290,13 +350,13 @@ void SCULPT_automasking_init(Sculpt *sd, Object *ob)
     SCULPT_boundary_automasking_init(ob,
                                      AUTOMASK_INIT_BOUNDARY_EDGES,
                                      brush->automasking_boundary_edges_propagation_steps,
-                                     ss->cache->automask);
+                                     ss->cache->automask_factor);
   }
   if (SCULPT_is_automasking_mode_enabled(sd, brush, BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
     SCULPT_vertex_random_access_init(ss);
     SCULPT_boundary_automasking_init(ob,
                                      AUTOMASK_INIT_BOUNDARY_FACE_SETS,
                                      brush->automasking_boundary_edges_propagation_steps,
-                                     ss->cache->automask);
+                                     ss->cache->automask_factor);
   }
 }
diff --git a/source/blender/ed

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list