Commit: b592e34559da82f86339e97b25ceac9f844719ab
Author: Jean Da Costa
Date:   Wed Jan 23 18:38:40 2019 +0100
Branches: master
https://developer.blender.org/rBb592e34559da82f86339e97b25ceac9f844719ab

Sculpt: add Topology Rake, to align edges along brush while painting.

This helps to generate cleaner topology and define sharp features for dynamic
topology. Best used on relatively low-poly meshes, it is not needed as much
for high detail areas and has a performance impact.

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

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

M       release/scripts/startup/bl_ui/space_view3d_toolbar.py
M       source/blender/blenkernel/intern/brush.c
M       source/blender/editors/sculpt_paint/sculpt.c
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 95f97ee454f..2c95a9207ec 100644
--- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py
+++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py
@@ -270,6 +270,13 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel):
             if not self.is_popover:
                 brush_basic_sculpt_settings(col, context, brush)
 
+            # topology_rake_factor
+            if capabilities.has_topology_rake and \
+               context.sculpt_object.use_dynamic_topology_sculpting:
+                col.separator()
+                row = col.row()
+                row.prop(brush, "topology_rake_factor", slider=True)
+
             # auto_smooth_factor and use_inverse_smooth_pressure
             if capabilities.has_auto_smooth:
                 col.separator()
diff --git a/source/blender/blenkernel/intern/brush.c 
b/source/blender/blenkernel/intern/brush.c
index f9ba8765d04..bb42f7412e6 100644
--- a/source/blender/blenkernel/intern/brush.c
+++ b/source/blender/blenkernel/intern/brush.c
@@ -79,6 +79,7 @@ static void brush_defaults(Brush *brush)
        brush->size = 35; /* radius of the brush in pixels */
        brush->alpha = 0.5f; /* brush strength/intensity probably variable 
should be renamed? */
        brush->autosmooth_factor = 0.0f;
+       brush->topology_rake_factor = 0.0f;
        brush->crease_pinch_factor = 0.5f;
        brush->sculpt_plane = SCULPT_DISP_DIR_AREA;
        brush->plane_offset = 0.0f; /* how far above or below the plane that is 
found by averaging the faces */
@@ -739,6 +740,8 @@ void BKE_brush_debug_print_state(Brush *br)
 
        BR_TEST(autosmooth_factor, f);
 
+       BR_TEST(topology_rake_factor, f);
+
        BR_TEST(crease_pinch_factor, f);
 
        BR_TEST(plane_trim, f);
diff --git a/source/blender/editors/sculpt_paint/sculpt.c 
b/source/blender/editors/sculpt_paint/sculpt.c
index b2df942b2fa..b25b9f4fff6 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -142,13 +142,22 @@ static bool sculpt_tool_is_proxy_used(const char 
sculpt_tool)
                    SCULPT_TOOL_LAYER);
 }
 
+static bool sculpt_brush_use_topology_rake(
+        const SculptSession *ss, const Brush *brush)
+{
+       return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(brush->sculpt_tool) &&
+              (brush->topology_rake_factor > 0.0f) &&
+              (ss->bm != NULL);
+}
+
 /**
  * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action
  */
-static int sculpt_brush_needs_normal(const Brush *brush, float normal_weight)
+static int sculpt_brush_needs_normal(
+        const SculptSession *ss, const Brush *brush)
 {
        return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) &&
-                (normal_weight > 0.0f)) ||
+                (ss->cache->normal_weight > 0.0f)) ||
 
                ELEM(brush->sculpt_tool,
                     SCULPT_TOOL_BLOB,
@@ -159,7 +168,8 @@ static int sculpt_brush_needs_normal(const Brush *brush, 
float normal_weight)
                     SCULPT_TOOL_ROTATE,
                     SCULPT_TOOL_THUMB) ||
 
-               (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA));
+               (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)) ||
+               sculpt_brush_use_topology_rake(ss, brush);
 }
 /** \} */
 
@@ -1598,6 +1608,94 @@ static void bmesh_neighbor_average(float avg[3], BMVert 
*v)
        copy_v3_v3(avg, v->co);
 }
 
+/* For bmesh: average only the four most aligned (parallel and perpendicular) 
edges
+ * relative to a direction. Naturally converges to a quad-like tesselation. */
+static void bmesh_four_neighbor_average(float avg[3], float direction[3], 
BMVert *v)
+{
+       /* Logic for 3 or more is identical. */
+       const int vfcount = BM_vert_face_count_at_most(v, 3);
+
+       /* Don't modify corner vertices. */
+       if (vfcount < 2) {
+               copy_v3_v3(avg, v->co);
+               return;
+       }
+
+       /* Project the direction to the vertex normal and create an aditional
+        * parallel vector. */
+       float dir_a[3], dir_b[3];
+       cross_v3_v3v3(dir_a, direction, v->no);
+       cross_v3_v3v3(dir_b, dir_a, v->no);
+
+       /* The four vectors which will be used for smoothing.
+        * Ocasionally less than 4 verts match the requirements in that case
+        * use v as fallback. */
+       BMVert *pos_a = v;
+       BMVert *neg_a = v;
+       BMVert *pos_b = v;
+       BMVert *neg_b = v;
+
+       float pos_score_a = 0.0f;
+       float neg_score_a = 0.0f;
+       float pos_score_b = 0.0f;
+       float neg_score_b = 0.0f;
+
+       BMIter liter;
+       BMLoop *l;
+
+       BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) {
+               BMVert *adj_v[2] = { l->prev->v, l->next->v };
+
+               for (int i = 0; i < ARRAY_SIZE(adj_v); i++) {
+                       BMVert *v_other = adj_v[i];
+
+                       if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 
2) <= 2) {
+                               float vec[3];
+                               sub_v3_v3v3(vec, v_other->co, v->co);
+                               normalize_v3(vec);
+
+                               /* The score is a measure of how orthogonal the 
edge is. */
+                               float score = dot_v3v3(vec, dir_a);
+
+                               if (score >= pos_score_a) {
+                                       pos_a = v_other;
+                                       pos_score_a = score;
+                               }
+                               else if (score < neg_score_a) {
+                                       neg_a = v_other;
+                                       neg_score_a = score;
+                               }
+                               /* The same scoring but for the perpendicular 
direction. */
+                               score = dot_v3v3(vec, dir_b);
+
+                               if (score >= pos_score_b) {
+                                       pos_b = v_other;
+                                       pos_score_b = score;
+                               }
+                               else if (score < neg_score_b) {
+                                       neg_b = v_other;
+                                       neg_score_b = score;
+                               }
+                       }
+               }
+       }
+
+       /* Average everything together. */
+       zero_v3(avg);
+       add_v3_v3(avg, pos_a->co);
+       add_v3_v3(avg, neg_a->co);
+       add_v3_v3(avg, pos_b->co);
+       add_v3_v3(avg, neg_b->co);
+       mul_v3_fl(avg, 0.25f);
+
+       /* Preserve volume. */
+       float vec[3];
+       sub_v3_v3(avg, v->co);
+       mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no));
+       sub_v3_v3(avg, vec);
+       add_v3_v3(avg, v->co);
+}
+
 /* Same logic as neighbor_average_mask(), but for bmesh rather than mesh */
 static float bmesh_neighbor_average_mask(BMVert *v, const int 
cd_vert_mask_offset)
 {
@@ -1756,6 +1854,62 @@ static void do_smooth_brush_bmesh_task_cb_ex(
        BKE_pbvh_vertex_iter_end;
 }
 
+static void do_topology_rake_bmesh_task_cb_ex(
+        void *__restrict userdata,
+        const int n,
+        const ParallelRangeTLS *__restrict tls)
+{
+       SculptThreadedTaskData *data = userdata;
+       SculptSession *ss = data->ob->sculpt;
+       Sculpt *sd = data->sd;
+       const Brush *brush = data->brush;
+
+       float direction[3];
+       copy_v3_v3(direction, ss->cache->grab_delta_symmetry);
+
+       float tmp[3];
+       mul_v3_v3fl(
+               tmp, ss->cache->sculpt_normal_symm,
+               dot_v3v3(ss->cache->sculpt_normal_symm, direction));
+       sub_v3_v3(direction, tmp);
+
+       /* Cancel if there's no grab data. */
+       if (is_zero_v3(direction)) {
+               return;
+       }
+
+       float bstrength = data->strength;
+       CLAMP(bstrength, 0.0f, 1.0f);
+
+       SculptBrushTest test;
+       SculptBrushTestFn sculpt_brush_test_sq_fn =
+               sculpt_brush_test_init_with_falloff_shape(ss, &test, 
data->brush->falloff_shape);
+
+       PBVHVertexIter vd;
+       BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, 
PBVH_ITER_UNIQUE)
+       {
+               if (sculpt_brush_test_sq_fn(&test, vd.co)) {
+                       const float fade = bstrength * tex_strength(
+                               ss, brush, vd.co, sqrtf(test.dist),
+                               vd.no, vd.fno, *vd.mask, tls->thread_id) * 
ss->cache->pressure;
+
+                       float avg[3], val[3];
+
+                       bmesh_four_neighbor_average(avg, direction, vd.bm_vert);
+
+                       sub_v3_v3v3(val, avg, vd.co);
+
+                       madd_v3_v3v3fl(val, vd.co, val, fade);
+
+                       sculpt_clip(sd, ss, vd.co, val);
+
+                       if (vd.mvert)
+                               vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+               }
+       }
+       BKE_pbvh_vertex_iter_end;
+}
+
 static void do_smooth_brush_multires_task_cb_ex(
         void *__restrict userdata,
         const int n,
@@ -1982,6 +2136,37 @@ static void smooth(
        }
 }
 
+static void bmesh_topology_rake(
+       Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float 
bstrength)
+{
+       Brush *brush = BKE_paint_brush(&sd->paint);
+       CLAMP(bstrength, 0.0f, 1.0f);
+
+       /* Interactions increase both strength and quality. */
+       const int iterations = 3;
+
+       int iteration;
+       const int count = iterations * bstrength + 1;
+       const float factor = iterations * bstrength / count;
+
+       for (iteration = 0; iteration <= count; ++iteration) {
+
+               SculptThreadedTaskData data = {
+                       .sd = sd,.ob = ob,.brush = brush,.nodes = nodes,
+                       .strength = factor,
+               };
+               ParallelRangeSettings 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,
+                       &data,
+                       do_topology_rake_bmesh_task_cb_ex,
+                       &settings);
+       }
+}
+
 static void do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int 
totnode)
 {
        SculptSession *ss = ob->sculpt;
@@ -3607,7 +3792,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush 
*brush, UnifiedPaintSe
                            do_brush_action_task_cb,
                            &settings);
 
-               if (sculpt_brush_needs_normal(brush, ss->cache->normal_weight))
+               if (sculpt_brush_needs_normal(ss, brush))
                        update_sculpt_normal(sd, ob, nodes, totnode);
 
                if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)
@@ -3682,6 +3867,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, 
Brush *brush, UnifiedPaintSe
                        }
                }
 
+               if (sculpt_brush_use_topology_rake(ss, brush)) {
+                       bmesh_topology_rake(sd, ob, nodes, totnode, 
brush->topology_rake_factor);
+               }
+
                if (ss->cache->supports_gravity)
                        do_gravity(sd, ob, nodes, totnode, sd->gravity_factor);
 
@@ -4394,7 +4583,8 @@ static void 
sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
        if (ELEM(tool,
                 SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE,
                 SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK,
-                SCULPT_TOOL_THUMB))
+                SCULPT_TOOL_THUMB) ||
+           sculpt_brush_use_topology_rake(ss, brush))
        {
                float grab_location[3], imat[4][4], delta[3], loc[3];
 
@@ -4434,6 +4624,10 @@ static void 
sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Bru
                                        invert_m4_m4(imat, ob->obmat);
                                        mul_mat3_m4_v3(imat, cache->grab_delta);
                                        break;
+                               default:
+                                       sub_v3_v3v3(cache->grab_delta, 
grab_location, cache->old_grab_location);
+                                       break;
+
                        }
                }
                else {
diff --git a/source/blender/makesdna/DNA_brush_types.h 
b/source/blender/makesdna/DNA_brush_types.h
index c

@@ Diff output truncated at 10240 characters. @@

_______________________________________________
Bf-blender-cvs mailing list
[email protected]
https://lists.blender.org/mailman/listinfo/bf-blender-cvs

Reply via email to