Sculpt: Pose Brush Scale/Transform deform mode

This is an alternative deformation brush for the Pose Brush intended
quickly change the proportions of the mesh. The regular mode scales
using the segment's origin as a pivot. The inverted mode drags the
entire segment using the grab delta.

The only difference with the regular pose brush is that it is not
compatible with IK, so the option is disabled and set to 1 segment. The
rest of the options should work as expected.

Reviewed By: sergey

Differential Revision: https://developer.blender.org/D7374
This commit is contained in:
Pablo Dobarro 2020-05-19 01:04:37 +02:00
parent 28d81f7b24
commit 6d4dc22e17
5 changed files with 149 additions and 25 deletions

View File

@ -619,10 +619,12 @@ def brush_settings(layout, context, brush, popover=False):
if brush.sculpt_tool == 'POSE':
layout.separator()
layout.prop(brush, "pose_deform_type")
layout.prop(brush, "pose_origin_type")
layout.prop(brush, "pose_offset")
layout.prop(brush, "pose_smooth_iterations")
layout.prop(brush, "pose_ik_segments")
if brush.pose_deform_type == 'ROTATE_TWIST':
layout.prop(brush, "pose_ik_segments")
layout.prop(brush, "use_pose_ik_anchored")
layout.separator()

View File

@ -239,6 +239,7 @@ typedef struct SculptPoseIKChainSegment {
float initial_orig[3];
float initial_head[3];
float len;
float scale;
float rot[4];
float *weights;

View File

@ -131,6 +131,32 @@ static void pose_solve_roll_chain(SculptPoseIKChain *ik_chain,
}
}
static void pose_solve_translate_chain(SculptPoseIKChain *ik_chain, const float delta[3])
{
SculptPoseIKChainSegment *segments = ik_chain->segments;
const int tot_segments = ik_chain->tot_segments;
for (int i = 0; i < tot_segments; i++) {
/* Move the origin and head of each segment by delta. */
add_v3_v3v3(segments[i].head, segments[i].initial_head, delta);
add_v3_v3v3(segments[i].orig, segments[i].initial_orig, delta);
/* Reset the segment rotation. */
unit_qt(segments[i].rot);
}
}
static void pose_solve_scale_chain(SculptPoseIKChain *ik_chain, const float scale)
{
SculptPoseIKChainSegment *segments = ik_chain->segments;
const int tot_segments = ik_chain->tot_segments;
for (int i = 0; i < tot_segments; i++) {
/* Assign the scale to each segment. */
segments[i].scale = scale;
}
}
static void do_pose_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
@ -600,9 +626,21 @@ static void pose_ik_chain_origin_heads_init(SculptPoseIKChain *ik_chain,
copy_v3_v3(ik_chain->segments[i].initial_orig, origin);
copy_v3_v3(ik_chain->segments[i].initial_head, head);
ik_chain->segments[i].len = len_v3v3(head, origin);
ik_chain->segments[i].scale = 1.0f;
}
}
static int pose_brush_num_effective_segments(const Brush *brush)
{
/* Scaling multiple segments at the same time is not supported as the IK solver can't handle
* changes in the segment's length. It will also required a better weight distribution to avoid
* artifacts in the areas affected by multiple segments. */
if (brush->pose_deform_type == BRUSH_POSE_DEFORM_SCALE_TRASLATE) {
return 1;
}
return brush->pose_ik_segments;
}
static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd,
Object *ob,
SculptSession *ss,
@ -629,7 +667,8 @@ static SculptPoseIKChain *pose_ik_chain_init_topology(Sculpt *sd,
pose_factor_grow[nearest_vertex_index] = 1.0f;
SculptPoseIKChain *ik_chain = pose_ik_chain_new(br->pose_ik_segments, totvert);
const int tot_segments = pose_brush_num_effective_segments(br);
SculptPoseIKChain *ik_chain = pose_ik_chain_new(tot_segments, totvert);
/* Calculate the first segment in the chain using the brush radius and the pose origin offset. */
copy_v3_v3(next_chain_segment_target, initial_location);
@ -688,7 +727,9 @@ static SculptPoseIKChain *pose_ik_chain_init_face_sets(
int totvert = SCULPT_vertex_count_get(ss);
SculptPoseIKChain *ik_chain = pose_ik_chain_new(br->pose_ik_segments, totvert);
const int tot_segments = pose_brush_num_effective_segments(br);
SculptPoseIKChain *ik_chain = pose_ik_chain_new(tot_segments, totvert);
GSet *visited_face_sets = BLI_gset_int_new_ex("visited_face_sets", ik_chain->tot_segments);
@ -802,13 +843,86 @@ void SCULPT_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br
MEM_SAFE_FREE(nodes);
}
static void sculpt_pose_do_translate_deform(SculptSession *ss, Brush *brush)
{
SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain;
BKE_curvemapping_initialize(brush->curve);
pose_solve_translate_chain(ik_chain, ss->cache->grab_delta);
}
static void sculpt_pose_do_scale_deform(SculptSession *ss, Brush *brush)
{
float ik_target[3];
SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain;
copy_v3_v3(ik_target, ss->cache->true_location);
add_v3_v3(ik_target, ss->cache->grab_delta);
/* Solve the IK for the first segment to include rotation as part of scale. */
pose_solve_ik_chain(ik_chain, ik_target, brush->flag2 & BRUSH_POSE_IK_ANCHORED);
/* Calculate a scale factor based on the grab delta. */
float plane[4];
float segment_dir[3];
sub_v3_v3v3(segment_dir, ik_chain->segments[0].initial_head, ik_chain->segments[0].initial_orig);
normalize_v3(segment_dir);
plane_from_point_normal_v3(plane, ik_chain->segments[0].initial_head, segment_dir);
const float segment_len = ik_chain->segments[0].len;
const float scale = segment_len / (segment_len - dist_signed_to_plane_v3(ik_target, plane));
/* Write the scale into the segments. */
pose_solve_scale_chain(ik_chain, scale);
}
static void sculpt_pose_do_twist_deform(SculptSession *ss, Brush *brush)
{
SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain;
/* Calculate the maximum roll. 0.02 radians per pixel works fine. */
float roll = (ss->cache->initial_mouse[0] - ss->cache->mouse[0]) * ss->cache->bstrength * 0.02f;
BKE_curvemapping_initialize(brush->curve);
pose_solve_roll_chain(ik_chain, brush, roll);
}
static void sculpt_pose_do_rotate_deform(SculptSession *ss, Brush *brush)
{
float ik_target[3];
SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain;
/* Calculate the IK target. */
copy_v3_v3(ik_target, ss->cache->true_location);
add_v3_v3(ik_target, ss->cache->grab_delta);
/* Solve the IK positions. */
pose_solve_ik_chain(ik_chain, ik_target, brush->flag2 & BRUSH_POSE_IK_ANCHORED);
}
static void sculpt_pose_do_rotate_twist_deform(SculptSession *ss, Brush *brush)
{
if (ss->cache->invert) {
sculpt_pose_do_twist_deform(ss, brush);
}
else {
sculpt_pose_do_rotate_deform(ss, brush);
}
}
static void sculpt_pose_do_scale_translate_deform(SculptSession *ss, Brush *brush)
{
if (ss->cache->invert) {
sculpt_pose_do_translate_deform(ss, brush);
}
else {
sculpt_pose_do_scale_deform(ss, brush);
}
}
/* Main Brush Function. */
void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
{
SculptSession *ss = ob->sculpt;
Brush *brush = BKE_paint_brush(&sd->paint);
float grab_delta[3];
float ik_target[3];
const ePaintSymmetryFlags symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
/* The pose brush applies all enabled symmetry axis in a single iteration, so the rest can be
@ -818,26 +932,15 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
}
SculptPoseIKChain *ik_chain = ss->cache->pose_ik_chain;
copy_v3_v3(grab_delta, ss->cache->grab_delta);
/* Solve the positions and rotations of the IK chain. */
if (ss->cache->invert) {
/* Roll Mode. */
/* Calculate the maximum roll. 0.02 radians per pixel works fine. */
float roll = (ss->cache->initial_mouse[0] - ss->cache->mouse[0]) * ss->cache->bstrength *
0.02f;
BKE_curvemapping_initialize(brush->curve);
pose_solve_roll_chain(ik_chain, brush, roll);
}
else {
/* IK follow target mode. */
/* Calculate the IK target. */
copy_v3_v3(grab_delta, ss->cache->grab_delta);
copy_v3_v3(ik_target, ss->cache->true_location);
add_v3_v3(ik_target, ss->cache->grab_delta);
/* Solve the IK positions. */
pose_solve_ik_chain(ik_chain, ik_target, brush->flag2 & BRUSH_POSE_IK_ANCHORED);
switch (brush->pose_deform_type) {
case BRUSH_POSE_DEFORM_ROTATE_TWIST:
sculpt_pose_do_rotate_twist_deform(ss, brush);
break;
case BRUSH_POSE_DEFORM_SCALE_TRASLATE:
sculpt_pose_do_scale_translate_deform(ss, brush);
break;
}
/* Flip the segment chain in all symmetry axis and calculate the transform matrices for each
@ -845,7 +948,7 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
/* This can be optimized by skipping the calculation of matrices where the symmetry is not
* enabled. */
for (int symm_it = 0; symm_it < PAINT_SYMM_AREAS; symm_it++) {
for (int i = 0; i < brush->pose_ik_segments; i++) {
for (int i = 0; i < ik_chain->tot_segments; i++) {
float symm_rot[4];
float symm_orig[3];
float symm_initial_orig[3];
@ -865,6 +968,7 @@ void SCULPT_do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
/* Create the transform matrix and store it in the segment. */
unit_m4(ik_chain->segments[i].pivot_mat[symm_it]);
quat_to_mat4(ik_chain->segments[i].trans_mat[symm_it], symm_rot);
mul_m4_fl(ik_chain->segments[i].trans_mat[symm_it], ik_chain->segments[i].scale);
translate_m4(ik_chain->segments[i].trans_mat[symm_it],
symm_orig[0] - symm_initial_orig[0],

View File

@ -331,6 +331,11 @@ typedef enum eBrushClothForceFalloffType {
BRUSH_CLOTH_FORCE_FALLOFF_PLANE = 1,
} eBrushClothForceFalloffType;
typedef enum eBrushPoseDeformType {
BRUSH_POSE_DEFORM_ROTATE_TWIST = 0,
BRUSH_POSE_DEFORM_SCALE_TRASLATE = 1,
} eBrushPoseDeformType;
typedef enum eBrushPoseOriginType {
BRUSH_POSE_ORIGIN_TOPOLOGY = 0,
BRUSH_POSE_ORIGIN_FACE_SETS = 1,
@ -478,7 +483,7 @@ typedef struct Brush {
char gpencil_sculpt_tool;
/** Active grease pencil weight tool. */
char gpencil_weight_tool;
char _pad1[6];
char _pad1[2];
float autosmooth_factor;
@ -510,6 +515,7 @@ typedef struct Brush {
float elastic_deform_volume_preservation;
/* pose */
int pose_deform_type;
float pose_offset;
int pose_smooth_iterations;
int pose_ik_segments;

View File

@ -1960,6 +1960,12 @@ static void rna_def_brush(BlenderRNA *brna)
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem brush_pose_deform_type_items[] = {
{BRUSH_POSE_DEFORM_ROTATE_TWIST, "ROTATE_TWIST", 0, "Rotate/Twist", ""},
{BRUSH_POSE_DEFORM_SCALE_TRASLATE, "SCALE_TRANSLATE", 0, "Scale/Translate", ""},
{0, NULL, 0, NULL, NULL},
};
static const EnumPropertyItem brush_pose_origin_type_items[] = {
{BRUSH_POSE_ORIGIN_TOPOLOGY,
"TOPOLOGY",
@ -2095,6 +2101,11 @@ static void rna_def_brush(BlenderRNA *brna)
RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "pose_deform_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, brush_pose_deform_type_items);
RNA_def_property_ui_text(prop, "Deformation", "Deformation type that is used in the brush");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "pose_origin_type", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, brush_pose_origin_type_items);
RNA_def_property_ui_text(prop,