Sculpt: Pose Brush Face Sets FK mode
authorPablo Dobarro <pablodp606@gmail.com>
Tue, 26 May 2020 21:54:53 +0000 (23:54 +0200)
committerPablo Dobarro <pablodp606@gmail.com>
Mon, 1 Jun 2020 20:30:01 +0000 (22:30 +0200)
This Pose Brush origin mode simulates an FK deformation in the entire
model when clicking on the face sets, as they were controls of a fully
rigged character. Combined with the previous Face Sets modes that allow
creating IK chains, the pose brush should now be able to simulate most
of the common rigs deformations.

Reviewed By: sergey

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

release/scripts/startup/bl_ui/properties_paint_common.py
source/blender/editors/sculpt_paint/sculpt_pose.c
source/blender/makesdna/DNA_brush_types.h
source/blender/makesrna/intern/rna_brush.c

index 5fadb31c83f876321e9543bc397e874f3fa33432..1612cce3c516a5a5348d37d93beea4b4f951204d 100644 (file)
@@ -623,7 +623,7 @@ def brush_settings(layout, context, brush, popover=False):
             layout.prop(brush, "pose_origin_type")
             layout.prop(brush, "pose_offset")
             layout.prop(brush, "pose_smooth_iterations")
-            if brush.pose_deform_type == 'ROTATE_TWIST':
+            if brush.pose_deform_type == 'ROTATE_TWIST' and brush.pose_origin_type in ('TOPOLOGY','FACE_SETS'):
               layout.prop(brush, "pose_ik_segments")
             layout.prop(brush, "use_pose_ik_anchored")
             layout.separator()
index 35f4870fa128f5cf9fdd18688c66a2256e78a36f..7c9caeb434064deabd34dc7bf9ca9f68571e386d 100644 (file)
@@ -401,6 +401,13 @@ typedef struct PoseFloodFillData {
    * that have the current face set. */
   float fallback_origin[3];
   int fallback_count;
+
+  /* Face Set FK mode. */
+  int *floodfill_it;
+  float *fk_weights;
+  int initial_face_set;
+  int masked_face_set_it;
+  int masked_face_set;
 } PoseFloodFillData;
 
 static bool pose_topology_floodfill_cb(
@@ -806,6 +813,92 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
   return ik_chain;
 }
 
+static bool pose_face_sets_fk_find_masked_floodfill_cb(
+    SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+{
+  PoseFloodFillData *data = userdata;
+
+  if (!is_duplicate) {
+    data->floodfill_it[to_v] = data->floodfill_it[from_v] + 1;
+  }
+  else {
+    data->floodfill_it[to_v] = data->floodfill_it[from_v];
+  }
+
+  const int to_face_set = SCULPT_vertex_face_set_get(ss, to_v);
+  if (SCULPT_vertex_has_unique_face_set(ss, to_v) &&
+      !SCULPT_vertex_has_unique_face_set(ss, from_v) &&
+      SCULPT_vertex_has_face_set(ss, from_v, to_face_set)) {
+    if (data->floodfill_it[to_v] > data->masked_face_set_it) {
+      data->masked_face_set = to_face_set;
+      data->masked_face_set_it = data->floodfill_it[to_v];
+    }
+  }
+
+  return SCULPT_vertex_has_face_set(ss, to_v, data->initial_face_set);
+}
+
+static bool pose_face_sets_fk_set_weights_floodfill_cb(
+    SculptSession *ss, int UNUSED(from_v), int to_v, bool UNUSED(is_duplicate), void *userdata)
+{
+  PoseFloodFillData *data = userdata;
+  data->fk_weights[to_v] = 1.0f;
+  return !SCULPT_vertex_has_face_set(ss, to_v, data->masked_face_set);
+}
+
+static SculptPoseIKChain *pose_ik_chain_init_face_sets_fk(
+    Sculpt *sd, Object *ob, SculptSession *ss, const float radius, const float *initial_location)
+{
+  const int totvert = SCULPT_vertex_count_get(ss);
+
+  SculptPoseIKChain *ik_chain = pose_ik_chain_new(1, totvert);
+
+  const int active_vertex = SCULPT_active_vertex_get(ss);
+  const int active_face_set = SCULPT_active_face_set_get(ss);
+
+  SculptFloodFill flood;
+  SCULPT_floodfill_init(ss, &flood);
+  SCULPT_floodfill_add_initial(&flood, active_vertex);
+  PoseFloodFillData fdata;
+  fdata.floodfill_it = MEM_calloc_arrayN(totvert, sizeof(int), "floodfill iteration");
+  fdata.floodfill_it[active_vertex] = 1;
+  fdata.initial_face_set = active_face_set;
+  fdata.masked_face_set = SCULPT_FACE_SET_NONE;
+  fdata.masked_face_set_it = 0;
+  SCULPT_floodfill_execute(ss, &flood, pose_face_sets_fk_find_masked_floodfill_cb, &fdata);
+  SCULPT_floodfill_free(&flood);
+
+  int count = 0;
+  float origin_acc[3] = {0.0f};
+  for (int i = 0; i < totvert; i++) {
+    if (fdata.floodfill_it[i] != 0 && SCULPT_vertex_has_face_set(ss, i, fdata.initial_face_set) &&
+        SCULPT_vertex_has_face_set(ss, i, fdata.masked_face_set)) {
+      add_v3_v3(origin_acc, SCULPT_vertex_co_get(ss, i));
+      count++;
+    }
+  }
+  MEM_freeN(fdata.floodfill_it);
+
+  if (count > 0) {
+    copy_v3_v3(ik_chain->segments[0].orig, origin_acc);
+    mul_v3_fl(ik_chain->segments[0].orig, 1.0f / count);
+  }
+  else {
+    zero_v3(ik_chain->segments[0].orig);
+  }
+
+  copy_v3_v3(ik_chain->segments[0].head, initial_location);
+
+  SCULPT_floodfill_init(ss, &flood);
+  SCULPT_floodfill_add_active(sd, ob, ss, &flood, radius);
+  fdata.fk_weights = ik_chain->segments[0].weights;
+  SCULPT_floodfill_execute(ss, &flood, pose_face_sets_fk_set_weights_floodfill_cb, &fdata);
+  SCULPT_floodfill_free(&flood);
+
+  pose_ik_chain_origin_heads_init(ik_chain, initial_location);
+  return ik_chain;
+}
+
 SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
                                              Object *ob,
                                              SculptSession *ss,
@@ -820,6 +913,9 @@ SculptPoseIKChain *SCULPT_pose_ik_chain_init(Sculpt *sd,
     case BRUSH_POSE_ORIGIN_FACE_SETS:
       return pose_ik_chain_init_face_sets(sd, ob, ss, br, radius);
       break;
+    case BRUSH_POSE_ORIGIN_FACE_SETS_FK:
+      return pose_ik_chain_init_face_sets_fk(sd, ob, ss, radius, initial_location);
+      break;
   }
   return NULL;
 }
index 2e449f10563bd86f9e86bc06a998e7a3aa724836..be7c894b4e4cfc364abdd15729687e6f6e7f0bab 100644 (file)
@@ -339,6 +339,7 @@ typedef enum eBrushPoseDeformType {
 typedef enum eBrushPoseOriginType {
   BRUSH_POSE_ORIGIN_TOPOLOGY = 0,
   BRUSH_POSE_ORIGIN_FACE_SETS = 1,
+  BRUSH_POSE_ORIGIN_FACE_SETS_FK = 2,
 } eBrushPoseOriginType;
 
 /* Gpencilsettings.Vertex_mode */
index 9e78eec9c250ca8a4d9d5538f10856dc36c39607..209e5a1ff8bc9c090a72f7c8123a3fa53a7faaee 100644 (file)
@@ -1978,6 +1978,11 @@ static void rna_def_brush(BlenderRNA *brna)
        0,
        "Face Sets",
        "Creates a pose segment per face sets, starting from the active face set"},
+      {BRUSH_POSE_ORIGIN_FACE_SETS_FK,
+       "FACE_SETS_FK",
+       0,
+       "Face Sets FK",
+       "Simulates an FK deformation using the Face Set under the cursor as control"},
       {0, NULL, 0, NULL, NULL},
   };