[Bf-blender-cvs] [2b2739724e6] master: Sculpt: Topology automasking

Pablo Dobarro noreply at git.blender.org
Mon Sep 9 17:00:47 CEST 2019


Commit: 2b2739724e6502d2798d22b516a4ffb61596c556
Author: Pablo Dobarro
Date:   Mon Sep 9 16:58:09 2019 +0200
Branches: master
https://developer.blender.org/rB2b2739724e6502d2798d22b516a4ffb61596c556

Sculpt: Topology automasking

The sculpt automasking feature assigns a factor to each vertex before starting the stroke. This can be used for isolating disconnected meshes, masking cavities, mesh boundary edges or creating topological falloffs.

This commit implements automasking in all brushes and topology automasking without topology falloff.

Reviewed By: brecht

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

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

M	release/scripts/startup/bl_ui/space_view3d_toolbar.py
M	source/blender/editors/sculpt_paint/sculpt.c
M	source/blender/editors/sculpt_paint/sculpt_intern.h
M	source/blender/makesdna/DNA_brush_types.h
M	source/blender/makesrna/intern/rna_brush.c

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

diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
index 5bc4883d1ea..a247725468c 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -376,6 +376,10 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
                 row = col.row()
                 row.prop(brush, "elastic_deform_compressibility", slider=True)
 
+            col.separator()
+            row = col.row()
+            row.prop(brush, "use_automasking_topology")
+
             # topology_rake_factor
             if (
                     capabilities.has_topology_rake and
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index b05d9db87f7..0a4c77b5656 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -28,6 +28,8 @@
 #include "BLI_blenlib.h"
 #include "BLI_dial_2d.h"
 #include "BLI_hash.h"
+#include "BLI_gsqueue.h"
+#include "BLI_stack.h"
 #include "BLI_task.h"
 #include "BLI_utildefines.h"
 #include "BLI_ghash.h"
@@ -116,7 +118,7 @@ static void sculpt_vertex_random_access_init(SculptSession *ss)
   }
 }
 
-static int UNUSED_FUNCTION(sculpt_active_vertex_get)(SculptSession *ss)
+static int sculpt_active_vertex_get(SculptSession *ss)
 {
   switch (BKE_pbvh_type(ss->pbvh)) {
     case PBVH_FACES:
@@ -341,6 +343,99 @@ static void sculpt_vertex_mask_clamp(SculptSession *ss, int index, float min, fl
   }
 }
 
+static void do_nearest_vertex_get_task_cb(void *__restrict userdata,
+                                          const int n,
+                                          const TaskParallelTLS *__restrict UNUSED(tls))
+{
+  SculptThreadedTaskData *data = userdata;
+  SculptSession *ss = data->ob->sculpt;
+  PBVHVertexIter vd;
+  int node_nearest_vertex_index = -1;
+  float node_nearest_vertex_distance_squared = FLT_MAX;
+
+  BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
+  {
+    float distance_squared = len_squared_v3v3(vd.co, data->nearest_vertex_search_co);
+    if (distance_squared < node_nearest_vertex_distance_squared &&
+        distance_squared < data->max_distance_squared) {
+      node_nearest_vertex_index = vd.index;
+      node_nearest_vertex_distance_squared = distance_squared;
+    }
+  }
+  BKE_pbvh_vertex_iter_end;
+
+  BLI_mutex_lock(&data->mutex);
+  if (data->nearest_vertex_index == -1) {
+    data->nearest_vertex_index = node_nearest_vertex_index;
+  }
+  else if (node_nearest_vertex_distance_squared <
+           len_squared_v3v3(data->nearest_vertex_search_co,
+                            sculpt_vertex_co_get(ss, data->nearest_vertex_index))) {
+    data->nearest_vertex_index = node_nearest_vertex_index;
+  }
+  BLI_mutex_unlock(&data->mutex);
+}
+
+int sculpt_nearest_vertex_get(
+    Sculpt *sd, Object *ob, float co[3], float max_distance, bool use_original)
+{
+  SculptSession *ss = ob->sculpt;
+  PBVHNode **nodes = NULL;
+  int totnode;
+  SculptSearchSphereData data = {
+      .ss = ss,
+      .sd = sd,
+      .radius_squared = max_distance * max_distance,
+      .original = use_original,
+      .center = co,
+  };
+  BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
+  if (totnode == 0) {
+    return -1;
+  }
+
+  SculptThreadedTaskData task_data = {
+      .sd = sd,
+      .ob = ob,
+      .nodes = nodes,
+      .max_distance_squared = max_distance * max_distance,
+      .nearest_vertex_index = -1,
+  };
+  copy_v3_v3(task_data.nearest_vertex_search_co, co);
+
+  BLI_mutex_init(&task_data.mutex);
+  TaskParallelSettings settings;
+  BLI_parallel_range_settings_defaults(&settings);
+  settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT);
+  BLI_task_parallel_range(0, totnode, &task_data, do_nearest_vertex_get_task_cb, &settings);
+  BLI_mutex_end(&task_data.mutex);
+
+  return task_data.nearest_vertex_index;
+}
+
+static bool is_symmetry_iteration_valid(char i, char symm)
+{
+  return i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)));
+}
+
+/* Checks if a vertex is inside the brush radius from any of its mirrored axis */
+static bool sculpt_is_vertex_inside_brush_radius_symm(float vertex[3],
+                                                      float br_co[3],
+                                                      float radius,
+                                                      char symm)
+{
+  for (char i = 0; i <= symm; ++i) {
+    if (is_symmetry_iteration_valid(i, symm)) {
+      float location[3];
+      flip_v3_v3(location, br_co, (char)i);
+      if (len_squared_v3v3(location, vertex) < radius * radius) {
+        return true;
+      }
+    }
+  }
+  return false;
+}
+
 /** \name Tool Capabilities
  *
  * Avoid duplicate checks, internal logic only,
@@ -976,6 +1071,145 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test,
 
 #endif
 
+/* Automasking */
+
+static bool sculpt_automasking_enabled(SculptSession *ss, const Brush *br)
+{
+  // REMOVE WITH PBVH_GRIDS
+  if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+    return false;
+  }
+
+  if (sculpt_stroke_is_dynamic_topology(ss, br)) {
+    return false;
+  }
+  if (br->automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
+    return true;
+  }
+  return false;
+}
+
+static float sculpt_automasking_factor_get(SculptSession *ss, const Brush *br, int vert)
+{
+  if (ss->cache->automask) {
+    return ss->cache->automask[vert];
+  }
+  else {
+    return 1.0f;
+  }
+}
+
+static void sculpt_automasking_end(Object *ob)
+{
+  SculptSession *ss = ob->sculpt;
+  if (ss->cache && ss->cache->automask) {
+    MEM_freeN(ss->cache->automask);
+  }
+}
+
+static bool sculpt_automasking_is_constrained_by_radius(Brush *br)
+{
+  if (ELEM(br->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) {
+    return true;
+  }
+  return false;
+}
+
+typedef struct VertexTopologyIterator {
+  int v;
+  int it;
+} VertexTopologyIterator;
+
+static float *sculpt_topology_automasking_init(Sculpt *sd, Object *ob, float *automask_factor)
+{
+
+  SculptSession *ss = ob->sculpt;
+  Brush *brush = BKE_paint_brush(&sd->paint);
+
+  if (!sculpt_automasking_enabled(ss, brush)) {
+    return NULL;
+  }
+
+  if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES && !ss->pmap) {
+    BLI_assert(!"Topology masking: pmap missing");
+    return NULL;
+  }
+
+  bool *visited_vertices = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(bool),
+                                       "visited vertices");
+
+  BLI_Stack *not_visited_vertices = BLI_stack_new(sizeof(VertexTopologyIterator),
+                                                  "not vertices stack");
+
+  VertexTopologyIterator mevit;
+
+  /* Add active vertex and symmetric vertices to the stack. */
+  float location[3];
+  const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
+  for (char i = 0; i <= symm; ++i) {
+    if (is_symmetry_iteration_valid(i, symm)) {
+      flip_v3_v3(location, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)), i);
+      if (i == 0) {
+        mevit.v = sculpt_active_vertex_get(ss);
+      }
+      else {
+        mevit.v = sculpt_nearest_vertex_get(
+            sd, ob, location, ss->cache->radius * ss->cache->radius, false);
+      }
+      if (mevit.v != -1) {
+        mevit.it = 1;
+        BLI_stack_push(not_visited_vertices, &mevit);
+      }
+    }
+  }
+
+  copy_v3_v3(location, sculpt_vertex_co_get(ss, sculpt_active_vertex_get(ss)));
+  bool use_radius = sculpt_automasking_is_constrained_by_radius(brush);
+
+  /* Flood fill automask to connected vertices. Limited to vertices inside the brush radius if the
+   * tool requires it */
+  while (!BLI_stack_is_empty(not_visited_vertices)) {
+    VertexTopologyIterator c_mevit;
+    BLI_stack_pop(not_visited_vertices, &c_mevit);
+    SculptVertexNeighborIter ni;
+    sculpt_vertex_neighbors_iter_begin(ss, c_mevit.v, ni)
+    {
+      if (!visited_vertices[(int)ni.index]) {
+        VertexTopologyIterator new_entry;
+        new_entry.v = ni.index;
+        automask_factor[new_entry.v] = 1.0f;
+        visited_vertices[(int)ni.index] = true;
+        if (!use_radius ||
+            sculpt_is_vertex_inside_brush_radius_symm(
+                sculpt_vertex_co_get(ss, new_entry.v), location, ss->cache->radius, symm)) {
+          BLI_stack_push(not_visited_vertices, &new_entry);
+        }
+      }
+    }
+    sculpt_vertex_neighbors_iter_end(ni)
+  }
+
+  BLI_stack_free(not_visited_vertices);
+
+  MEM_freeN(visited_vertices);
+
+  return automask_factor;
+}
+
+static void sculpt_automasking_init(Sculpt *sd, Object *ob)
+{
+  SculptSession *ss = ob->sculpt;
+  Brush *brush = BKE_paint_brush(&sd->paint);
+
+  ss->cache->automask = MEM_callocN(sizeof(float) * sculpt_vertex_count_get(ss),
+                                    "automask_factor");
+
+  if (brush->automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY) {
+    sculpt_vertex_random_access_init(ss);
+    sculpt_topology_automasking_init(sd, ob, ss->cache->automask);
+  }
+}
+
 /* ===== Sculpting =====
  */
 static void flip_v3(float v[3], const char symm)
@@ -1479,6 +1713,7 @@ float tex_strength(SculptSession *ss,
                    const short vno[3],
                    const float fno[3],
                    const float mask,
+                   const int vertex_index,
                    const int thread_id)
 {
   StrokeCache *cache = ss->cache;
@@ -1549,6 +1784,9 @@ float tex_strength(SculptSession *ss,
   /* Paint mask */
   avg *= 1.0f - mask;
 
+  /* Automasking */
+  avg *= sculpt_automasking_factor_get(ss, br, vertex_index);
+
   return avg;
 }
 
@@ -1557,7 +1795,12 @@ bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
 {
   SculptSearchSphereData *data = data_v;
   float *center, nearest[3];
-  center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location;
+  if (data->center) {
+    center = data->center;
+  }
+  else {
+    center = data->ss->cache ? data->ss->cache->location : data->ss->cursor_location;
+  }
   float t[3], bb_min[3], bb_max[3];
   int i;
 
@@ -1642,6 +1885,7 @@ static PBVHNode **sculpt_pbvh_gath

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-blender-cvs mailing list