Sculpt: Pose Brush Face Sets FK mode

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
This commit is contained in:
Pablo Dobarro 2020-05-26 23:54:53 +02:00
parent 5c54a609a9
commit 3778f168f6
4 changed files with 103 additions and 1 deletions

View 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()

View 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;
}

View 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 */

View 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},
};