diff options
-rw-r--r-- | release/scripts/startup/bl_ui/properties_paint_common.py | 4 | ||||
-rw-r--r-- | source/blender/blenkernel/BKE_paint.h | 1 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_pose.c | 150 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 8 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 11 |
5 files changed, 149 insertions, 25 deletions
diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 92d421f63a8..dbe3ced04b3 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -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() diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index a7ece2e3167..ac292432acc 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -239,6 +239,7 @@ typedef struct SculptPoseIKChainSegment { float initial_orig[3]; float initial_head[3]; float len; + float scale; float rot[4]; float *weights; diff --git a/source/blender/editors/sculpt_paint/sculpt_pose.c b/source/blender/editors/sculpt_paint/sculpt_pose.c index c7511dfc80f..b5e2652df7c 100644 --- a/source/blender/editors/sculpt_paint/sculpt_pose.c +++ b/source/blender/editors/sculpt_paint/sculpt_pose.c @@ -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,7 +626,19 @@ 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, @@ -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], diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index c143c2a442a..2e449f10563 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -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; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index d3b607fcb76..9e78eec9c25 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -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, |