Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/sculpt_paint')
-rw-r--r--source/blender/editors/sculpt_paint/paint_cursor.c33
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h5
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c4
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c606
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h21
5 files changed, 475 insertions, 194 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c
index 9021666c001..9bc587de6da 100644
--- a/source/blender/editors/sculpt_paint/paint_cursor.c
+++ b/source/blender/editors/sculpt_paint/paint_cursor.c
@@ -1461,15 +1461,30 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
cursor_draw_point_with_symmetry(pos, ar, gi.active_vertex_co, sd, vc.obact, rds);
}
- /* Draw pose brush origin */
+ /* Draw pose brush origins. */
if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f);
if (update_previews) {
BKE_sculpt_update_object_for_edit(depsgraph, vc.obact, true, false);
- sculpt_pose_calc_pose_data(
- sd, vc.obact, ss, gi.location, rds, brush->pose_offset, ss->pose_origin, NULL);
+
+ /* Free the previous pose brush preview. */
+ if (ss->pose_ik_chain_preview) {
+ sculpt_pose_ik_chain_free(ss->pose_ik_chain_preview);
+ }
+
+ /* Generate a new pose brush preview from the current cursor location. */
+ ss->pose_ik_chain_preview = sculpt_pose_ik_chain_init(
+ sd, vc.obact, ss, brush, gi.location, rds);
+ }
+
+ /* Draw the pose brush rotation origins. */
+ for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) {
+ cursor_draw_point_screen_space(pos,
+ ar,
+ ss->pose_ik_chain_preview->segments[i].initial_orig,
+ vc.obact->obmat,
+ 3);
}
- cursor_draw_point_screen_space(pos, ar, ss->pose_origin, vc.obact->obmat, 5);
}
/* Draw 3D brush cursor */
@@ -1518,9 +1533,13 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused))
if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
immUniformColor4f(1.0f, 1.0f, 1.0f, 0.8f);
GPU_line_width(2.0f);
- immBegin(GPU_PRIM_LINES, 2);
- immVertex3fv(pos, ss->pose_origin);
- immVertex3fv(pos, gi.location);
+
+ immBegin(GPU_PRIM_LINES, ss->pose_ik_chain_preview->tot_segments * 2);
+ for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) {
+ immVertex3fv(pos, ss->pose_ik_chain_preview->segments[i].initial_orig);
+ immVertex3fv(pos, ss->pose_ik_chain_preview->segments[i].initial_head);
+ }
+
immEnd();
}
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 53beb981522..6f2e4a0055d 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -45,6 +45,7 @@ struct wmOperator;
struct wmOperatorType;
struct wmWindowManager;
enum ePaintMode;
+enum ePaintSymmetryFlags;
typedef struct CoNo {
float co[3];
@@ -306,8 +307,8 @@ bool mask_paint_poll(struct bContext *C);
bool paint_curve_poll(struct bContext *C);
bool facemask_paint_poll(struct bContext *C);
-void flip_v3_v3(float out[3], const float in[3], const char symm);
-void flip_qt_qt(float out[3], const float in[3], const char symm);
+void flip_v3_v3(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
+void flip_qt_qt(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
/* stroke operator */
typedef enum BrushStrokeMode {
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index a014fe7fdff..4a1b329fce5 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -412,7 +412,7 @@ static Image *imapaint_face_image(Object *ob, Mesh *me, int face_index)
}
/* Uses symm to selectively flip any axis of a coordinate. */
-void flip_v3_v3(float out[3], const float in[3], const char symm)
+void flip_v3_v3(float out[3], const float in[3], const ePaintSymmetryFlags symm)
{
if (symm & PAINT_SYMM_X) {
out[0] = -in[0];
@@ -434,7 +434,7 @@ void flip_v3_v3(float out[3], const float in[3], const char symm)
}
}
-void flip_qt_qt(float out[4], const float in[4], const char symm)
+void flip_qt_qt(float out[4], const float in[4], const ePaintSymmetryFlags symm)
{
float axis[3], angle;
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index 10772fa68ba..a96ca07cc9b 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -443,7 +443,7 @@ static void nearest_vertex_get_reduce(const void *__restrict UNUSED(userdata),
}
static int sculpt_nearest_vertex_get(
- Sculpt *sd, Object *ob, float co[3], float max_distance, bool use_original)
+ Sculpt *sd, Object *ob, const float co[3], float max_distance, bool use_original)
{
SculptSession *ss = ob->sculpt;
PBVHNode **nodes = NULL;
@@ -1330,11 +1330,16 @@ static void sculpt_automasking_init(Sculpt *sd, Object *ob)
/* ===== Sculpting =====
*/
-static void flip_v3(float v[3], const char symm)
+static void flip_v3(float v[3], const ePaintSymmetryFlags symm)
{
flip_v3_v3(v, v, symm);
}
+static void flip_qt(float quat[3], const ePaintSymmetryFlags symm)
+{
+ flip_qt_qt(quat, quat, symm);
+}
+
static float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle)
{
float mirror[3];
@@ -1921,7 +1926,8 @@ float tex_strength(SculptSession *ss,
bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v)
{
SculptSearchSphereData *data = data_v;
- float *center, nearest[3];
+ const float *center;
+ float nearest[3];
if (data->center) {
center = data->center;
}
@@ -3527,15 +3533,141 @@ static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, in
BKE_pbvh_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings);
}
+static ePaintSymmetryAreas sculpt_get_vertex_symm_area(const float co[3])
+{
+ ePaintSymmetryAreas symm_area = AREA_SYMM_DEFAULT;
+ if (co[0] < 0.0f) {
+ symm_area |= AREA_SYMM_X;
+ }
+ if (co[1] < 0.0f) {
+ symm_area |= AREA_SYMM_Y;
+ }
+ if (co[2] < 0.0f) {
+ symm_area |= AREA_SYMM_Z;
+ }
+ return symm_area;
+}
+
+static void sculpt_flip_v3_by_symm_area(float v[3],
+ const ePaintSymmetryFlags symm,
+ const ePaintSymmetryAreas symmarea,
+ const float pivot[3])
+{
+ for (char i = 0; i < 3; i++) {
+ ePaintSymmetryFlags symm_it = 1 << i;
+ if (symm & symm_it) {
+ if (symmarea & symm_it) {
+ flip_v3(v, symm_it);
+ }
+ if (pivot[0] < 0) {
+ flip_v3(v, symm_it);
+ }
+ }
+ }
+}
+
+static void sculpt_flip_quat_by_symm_area(float quat[3],
+ const ePaintSymmetryFlags symm,
+ const ePaintSymmetryAreas symmarea,
+ const float pivot[3])
+{
+ for (char i = 0; i < 3; i++) {
+ ePaintSymmetryFlags symm_it = 1 << i;
+ if (symm & symm_it) {
+ if (symmarea & symm_it) {
+ flip_qt(quat, symm_it);
+ }
+ if (pivot[0] < 0) {
+ flip_qt(quat, symm_it);
+ }
+ }
+ }
+}
+
+static void pose_solve_ik_chain(PoseIKChain *ik_chain, const float initial_target[3])
+{
+ PoseIKChainSegment *segments = ik_chain->segments;
+ int tot_segments = ik_chain->tot_segments;
+
+ float target[3];
+
+ /* Set the initial target. */
+ copy_v3_v3(target, initial_target);
+
+ /* Solve the positions and rotations of all segments in the chain. */
+ for (int i = 0; i < tot_segments; i++) {
+ float initial_orientation[3];
+ float current_orientation[3];
+ float current_head_position[3];
+ float current_origin_position[3];
+
+ /* Calculate the rotation to orientate the segment to the target from its initial state. */
+ sub_v3_v3v3(current_orientation, target, segments[i].orig);
+ normalize_v3(current_orientation);
+ sub_v3_v3v3(initial_orientation, segments[i].initial_head, segments[i].initial_orig);
+ normalize_v3(initial_orientation);
+ rotation_between_vecs_to_quat(segments[i].rot, initial_orientation, current_orientation);
+
+ /* Rotate the segment by calculating a new head position. */
+ madd_v3_v3v3fl(current_head_position, segments[i].orig, current_orientation, segments[i].len);
+
+ /* Move the origin of the segment towards the target. */
+ sub_v3_v3v3(current_origin_position, target, current_head_position);
+
+ /* Store the new head and origin positions to the segment. */
+ copy_v3_v3(segments[i].head, current_head_position);
+ add_v3_v3(segments[i].orig, current_origin_position);
+
+ /* Use the origin of this segment as target for the next segment in the chain. */
+ copy_v3_v3(target, segments[i].orig);
+ }
+
+ /* Move back the whole chain to preserve the anchor point. */
+ float anchor_diff[3];
+ sub_v3_v3v3(
+ anchor_diff, segments[tot_segments - 1].initial_orig, segments[tot_segments - 1].orig);
+
+ for (int i = 0; i < tot_segments; i++) {
+ add_v3_v3(segments[i].orig, anchor_diff);
+ add_v3_v3(segments[i].head, anchor_diff);
+ }
+}
+
+static void pose_solve_roll_chain(PoseIKChain *ik_chain, const Brush *brush, const float roll)
+{
+ PoseIKChainSegment *segments = ik_chain->segments;
+ int tot_segments = ik_chain->tot_segments;
+
+ for (int i = 0; i < tot_segments; i++) {
+ float initial_orientation[3];
+ float initial_rotation[4];
+ float current_rotation[4];
+
+ sub_v3_v3v3(initial_orientation, segments[i].initial_head, segments[i].initial_orig);
+ normalize_v3(initial_orientation);
+
+ /* Calculate the current roll angle using the brush curve. */
+ float current_roll = roll * BKE_brush_curve_strength(brush, i, tot_segments);
+
+ axis_angle_normalized_to_quat(initial_rotation, initial_orientation, 0.0f);
+ axis_angle_normalized_to_quat(current_rotation, initial_orientation, current_roll);
+
+ /* Store the difference of the rotations in the segment rotation. */
+ rotation_between_quats_to_quat(segments[i].rot, current_rotation, initial_rotation);
+ }
+}
+
static void do_pose_brush_task_cb_ex(void *__restrict userdata,
const int n,
const TaskParallelTLS *__restrict UNUSED(tls))
{
SculptThreadedTaskData *data = userdata;
SculptSession *ss = data->ob->sculpt;
+ PoseIKChain *ik_chain = ss->cache->pose_ik_chain;
+ PoseIKChainSegment *segments = ik_chain->segments;
PBVHVertexIter vd;
- float disp[3], val[3];
+ float disp[3], new_co[3];
float final_pos[3];
SculptOrigVertData orig_data;
@@ -3543,25 +3675,41 @@ static void do_pose_brush_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
-
sculpt_orig_vert_data_update(&orig_data, &vd);
- if (check_vertex_pivot_symmetry(
- orig_data.co, data->pose_initial_co, ss->cache->mirror_symmetry_pass)) {
- copy_v3_v3(val, orig_data.co);
- mul_m4_v3(data->transform_trans_inv, val);
- mul_m4_v3(data->transform_rot, val);
- mul_m4_v3(data->transform_trans, val);
- sub_v3_v3v3(disp, val, orig_data.co);
-
- mul_v3_fl(disp, ss->cache->pose_factor[vd.index]);
+
+ float total_disp[3];
+ zero_v3(total_disp);
+
+ ePaintSymmetryAreas symm_area = sculpt_get_vertex_symm_area(orig_data.co);
+
+ /* Calculate the displacement of each vertex for all the segments in the chain. */
+ for (int ik = 0; ik < ik_chain->tot_segments; ik++) {
+ copy_v3_v3(new_co, orig_data.co);
+
+ /* Get the transform matrix for the vertex symmetry area to calculate a displacement in the
+ * vertex. */
+ mul_m4_v3(segments[ik].pivot_mat_inv[(int)symm_area], new_co);
+ mul_m4_v3(segments[ik].trans_mat[(int)symm_area], new_co);
+ mul_m4_v3(segments[ik].pivot_mat[(int)symm_area], new_co);
+
+ /* Apply the segment weight of the vertex to the displacement. */
+ sub_v3_v3v3(disp, new_co, orig_data.co);
+ mul_v3_fl(disp, segments[ik].weights[vd.index]);
+
+ /* Apply the vertex mask to the displacement. */
float mask = vd.mask ? *vd.mask : 0.0f;
mul_v3_fl(disp, 1.0f - mask);
- add_v3_v3v3(final_pos, orig_data.co, disp);
- copy_v3_v3(vd.co, final_pos);
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
+ /* Accumulate the displacement. */
+ add_v3_v3(total_disp, disp);
+ }
+
+ /* Apply the accumulated displacement to the vertex. */
+ add_v3_v3v3(final_pos, orig_data.co, total_disp);
+ copy_v3_v3(vd.co, final_pos);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
}
}
BKE_pbvh_vertex_iter_end;
@@ -3571,32 +3719,75 @@ static void 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], rot_quat[4], initial_v[3], current_v[3], temp[3];
- float pose_origin[3];
- float pose_initial_co[3];
- float transform_rot[4][4], transform_trans[4][4], transform_trans_inv[4][4];
+ float grab_delta[3];
+ float ik_target[3];
+ const ePaintSymmetryFlags symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
- copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+ /* The pose brush applies all enabled symmetry axis in a sigle iteration, so the rest can be
+ * ignored. */
+ if (ss->cache->mirror_symmetry_pass != 0) {
+ return;
+ }
- copy_v3_v3(pose_origin, ss->cache->pose_origin);
- flip_v3(pose_origin, (char)ss->cache->mirror_symmetry_pass);
+ PoseIKChain *ik_chain = ss->cache->pose_ik_chain;
- copy_v3_v3(pose_initial_co, ss->cache->pose_initial_co);
- flip_v3(pose_initial_co, (char)ss->cache->mirror_symmetry_pass);
+ /* 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. */
- sub_v3_v3v3(initial_v, pose_initial_co, pose_origin);
- normalize_v3(initial_v);
+ 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);
- add_v3_v3v3(temp, pose_initial_co, grab_delta);
- sub_v3_v3v3(current_v, temp, pose_origin);
- normalize_v3(current_v);
+ /* Solve the IK positions */
+ pose_solve_ik_chain(ik_chain, ik_target);
+ }
+
+ /* Flip the segment chain in all symmetry axis and calculate the transform matrices for each
+ * possible combination. */
+ /* 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++) {
+ float symm_rot[4];
+ float symm_orig[3];
+ float symm_initial_orig[3];
+
+ ePaintSymmetryAreas symm_area = symm_it;
- rotation_between_vecs_to_quat(rot_quat, initial_v, current_v);
- unit_m4(transform_rot);
- unit_m4(transform_trans);
- quat_to_mat4(transform_rot, rot_quat);
- translate_m4(transform_trans, pose_origin[0], pose_origin[1], pose_origin[2]);
- invert_m4_m4(transform_trans_inv, transform_trans);
+ copy_qt_qt(symm_rot, ik_chain->segments[i].rot);
+ copy_v3_v3(symm_orig, ik_chain->segments[i].orig);
+ copy_v3_v3(symm_initial_orig, ik_chain->segments[i].initial_orig);
+
+ /* Flip the origins and rotation quats of each segment. */
+ sculpt_flip_quat_by_symm_area(symm_rot, symm, symm_area, ss->cache->orig_grab_location);
+ sculpt_flip_v3_by_symm_area(symm_orig, symm, symm_area, ss->cache->orig_grab_location);
+ sculpt_flip_v3_by_symm_area(
+ symm_initial_orig, symm, symm_area, ss->cache->orig_grab_location);
+
+ /* 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);
+
+ translate_m4(ik_chain->segments[i].trans_mat[symm_it],
+ symm_orig[0] - symm_initial_orig[0],
+ symm_orig[1] - symm_initial_orig[1],
+ symm_orig[2] - symm_initial_orig[2]);
+ translate_m4(
+ ik_chain->segments[i].pivot_mat[symm_it], symm_orig[0], symm_orig[1], symm_orig[2]);
+ invert_m4_m4(ik_chain->segments[i].pivot_mat_inv[symm_it],
+ ik_chain->segments[i].pivot_mat[symm_it]);
+ }
+ }
SculptThreadedTaskData data = {
.sd = sd,
@@ -3604,11 +3795,6 @@ static void do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
.brush = brush,
.nodes = nodes,
.grab_delta = grab_delta,
- .pose_origin = pose_origin,
- .pose_initial_co = pose_initial_co,
- .transform_rot = transform_rot,
- .transform_trans = transform_trans,
- .transform_trans_inv = transform_trans_inv,
};
PBVHParallelSettings settings;
@@ -3629,23 +3815,24 @@ static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata,
PoseGrowFactorTLSData *gftd = tls->userdata_chunk;
SculptSession *ss = data->ob->sculpt;
const char symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL;
- const float *active_co = sculpt_active_vertex_co_get(ss);
PBVHVertexIter vd;
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
SculptVertexNeighborIter ni;
float max = 0.0f;
+
+ /* Grow the factor. */
sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni)
{
float vmask_f = data->prev_mask[ni.index];
- if (vmask_f > max) {
- max = vmask_f;
- }
+ max = MAX2(vmask_f, max);
}
sculpt_vertex_neighbors_iter_end(ni);
- if (max != data->prev_mask[vd.index]) {
+
+ /* Keep the count of the vertices that where added to the factors in this grow iteration. */
+ if (max > data->prev_mask[vd.index]) {
data->pose_factor[vd.index] = max;
- if (check_vertex_pivot_symmetry(vd.co, active_co, symm)) {
+ if (check_vertex_pivot_symmetry(vd.co, data->pose_initial_co, symm)) {
add_v3_v3(gftd->pos_avg, vd.co);
gftd->pos_count++;
}
@@ -3665,9 +3852,16 @@ static void pose_brush_grow_factor_reduce(const void *__restrict UNUSED(userdata
join->pos_count += gftd->pos_count;
}
-/* Grow the factor until its boundary is near to the offset pose origin */
-static void sculpt_pose_grow_pose_factor(
- Sculpt *sd, Object *ob, SculptSession *ss, float pose_origin[3], float *pose_factor)
+/* Grow the factor until its boundary is near to the offset pose origin or outside the target
+ * distance. */
+static void sculpt_pose_grow_pose_factor(Sculpt *sd,
+ Object *ob,
+ SculptSession *ss,
+ float pose_origin[3],
+ float pose_target[3],
+ float max_len,
+ float *r_pose_origin,
+ float *pose_factor)
{
PBVHNode **nodes;
PBVH *pbvh = ob->sculpt->pbvh;
@@ -3681,6 +3875,8 @@ static void sculpt_pose_grow_pose_factor(
.totnode = totnode,
.pose_factor = pose_factor,
};
+
+ data.pose_initial_co = pose_target;
PBVHParallelSettings settings;
PoseGrowFactorTLSData gftd;
gftd.pos_count = 0;
@@ -3698,19 +3894,44 @@ static void sculpt_pose_grow_pose_factor(
gftd.pos_count = 0;
memcpy(data.prev_mask, pose_factor, sculpt_vertex_count_get(ss) * sizeof(float));
BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_grow_factor_task_cb_ex, &settings);
+
if (gftd.pos_count != 0) {
mul_v3_fl(gftd.pos_avg, 1.0f / (float)gftd.pos_count);
- float len = len_v3v3(gftd.pos_avg, pose_origin);
- if (len < prev_len) {
- prev_len = len;
- grow_next_iteration = true;
+ if (pose_origin) {
+ /* Test with pose origin. Used when growing the factors to compensate the Origin Offset. */
+ /* Stop when the factor's avg_pos starts moving away from the origin instead of getting
+ * closert to it. */
+ float len = len_v3v3(gftd.pos_avg, pose_origin);
+ if (len < prev_len) {
+ prev_len = len;
+ grow_next_iteration = true;
+ }
+ else {
+ grow_next_iteration = false;
+ memcpy(pose_factor, data.prev_mask, sculpt_vertex_count_get(ss) * sizeof(float));
+ }
}
else {
- grow_next_iteration = false;
- memcpy(pose_factor, data.prev_mask, sculpt_vertex_count_get(ss) * sizeof(float));
+ /* Test with length. Used to calculate the origin positions of the IK chain. */
+ /* Stops when the factors have grown enough to generate a new segment origin. */
+ float len = len_v3v3(gftd.pos_avg, pose_target);
+ if (len < max_len) {
+ prev_len = len;
+ grow_next_iteration = true;
+ }
+ else {
+ grow_next_iteration = false;
+ if (r_pose_origin) {
+ copy_v3_v3(r_pose_origin, gftd.pos_avg);
+ }
+ memcpy(pose_factor, data.prev_mask, sculpt_vertex_count_get(ss) * sizeof(float));
+ }
}
}
else {
+ if (r_pose_origin) {
+ copy_v3_v3(r_pose_origin, pose_target);
+ }
grow_next_iteration = false;
}
}
@@ -3736,10 +3957,6 @@ static bool sculpt_pose_brush_is_vertex_inside_brush_radius(const float vertex[3
return false;
}
-/* Calculate the pose origin and (Optionaly the pose factor) that is used when using the pose brush
- *
- * r_pose_origin must be a valid pointer. the r_pose_factor is optional. When set to NULL it won't
- * be calculated. */
typedef struct PoseFloodFillData {
float pose_initial_co[3];
float radius;
@@ -3774,6 +3991,10 @@ static bool pose_floodfill_cb(
return false;
}
+/* Calculate the pose origin and (Optionaly the pose factor) that is used when using the pose brush
+ *
+ * r_pose_origin must be a valid pointer. the r_pose_factor is optional. When set to NULL it won't
+ * be calculated. */
void sculpt_pose_calc_pose_data(Sculpt *sd,
Object *ob,
SculptSession *ss,
@@ -3812,8 +4033,11 @@ void sculpt_pose_calc_pose_data(Sculpt *sd,
madd_v3_v3fl(fdata.pose_origin, pose_d, radius * pose_offset);
copy_v3_v3(r_pose_origin, fdata.pose_origin);
+ /* Do the initial grow of the factors to get the first segment of the chain with Origin Offset.
+ */
if (pose_offset != 0.0f && r_pose_factor) {
- sculpt_pose_grow_pose_factor(sd, ob, ss, fdata.pose_origin, r_pose_factor);
+ sculpt_pose_grow_pose_factor(
+ sd, ob, ss, fdata.pose_origin, fdata.pose_origin, 0, NULL, r_pose_factor);
}
}
@@ -3827,33 +4051,137 @@ static void pose_brush_init_task_cb_ex(void *__restrict userdata,
BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE)
{
SculptVertexNeighborIter ni;
- float avg = 0;
- int total = 0;
+ float avg = 0.0f;
+ int total = 0.0f;
sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni)
{
- avg += ss->cache->pose_factor[ni.index];
+ avg += data->pose_factor[ni.index];
total++;
}
sculpt_vertex_neighbors_iter_end(ni);
if (total > 0) {
- ss->cache->pose_factor[vd.index] = avg / (float)total;
+ data->pose_factor[vd.index] = avg / total;
}
}
BKE_pbvh_vertex_iter_end;
}
-static void sculpt_pose_brush_init(
- Sculpt *sd, Object *ob, SculptSession *ss, Brush *br, float initial_location[3], float radius)
+void sculpt_pose_ik_chain_free(PoseIKChain *ik_chain)
{
- float *pose_factor = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(float), "Pose factor");
+ for (int i = 0; i < ik_chain->tot_segments; i++) {
+ MEM_SAFE_FREE(ik_chain->segments[i].weights);
+ }
+ MEM_SAFE_FREE(ik_chain->segments);
+ MEM_SAFE_FREE(ik_chain);
+}
+
+PoseIKChain *sculpt_pose_ik_chain_init(Sculpt *sd,
+ Object *ob,
+ SculptSession *ss,
+ Brush *br,
+ const float initial_location[3],
+ const float radius)
+{
+
+ float chain_end[3];
+ float chain_segment_len = len_v3v3(initial_location, chain_end) / br->pose_ik_segments;
+ chain_segment_len = radius * (1.0f + br->pose_offset);
+ float next_chain_segment_target[3];
+
+ int totvert = sculpt_vertex_count_get(ss);
+ int nearest_vertex_index = sculpt_nearest_vertex_get(sd, ob, initial_location, FLT_MAX, true);
+
+ /* Init the buffers used to keep track of the changes in the pose factors as more segments are
+ * added to the IK chain. */
+
+ /* This stores the whole pose factors values as they grow through the mesh. */
+ float *pose_factor_grow = MEM_callocN(totvert * sizeof(float), "Pose Factor Grow");
+
+ /* This stores the previous status of the factors when growing a new iteration. */
+ float *pose_factor_grow_prev = MEM_callocN(totvert * sizeof(float),
+ "Pose Factor Grow Prev Iteration");
+
+ pose_factor_grow[nearest_vertex_index] = 1.0f;
+
+ /* Init the IK chain with empty weights. */
+ PoseIKChain *ik_chain = MEM_callocN(sizeof(PoseIKChain), "Pose IK Chain");
+ ik_chain->tot_segments = br->pose_ik_segments;
+ ik_chain->segments = MEM_callocN(ik_chain->tot_segments * sizeof(PoseIKChainSegment),
+ "Pose IK Chain Segments");
+ for (int i = 0; i < br->pose_ik_segments; i++) {
+ ik_chain->segments[i].weights = MEM_callocN(totvert * sizeof(float), "Pose IK weights");
+ }
+
+ /* 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);
+ sculpt_pose_calc_pose_data(sd,
+ ob,
+ ss,
+ next_chain_segment_target,
+ radius,
+ br->pose_offset,
+ ik_chain->segments[0].orig,
+ pose_factor_grow);
+
+ copy_v3_v3(next_chain_segment_target, ik_chain->segments[0].orig);
+
+ /* Init the weights of this segment and store the status of the pose factors to start calculating
+ * new segment origins. */
+ for (int j = 0; j < totvert; j++) {
+ ik_chain->segments[0].weights[j] = pose_factor_grow[j];
+ pose_factor_grow_prev[j] = pose_factor_grow[j];
+ }
+
+ /* Calculate the next segments in the chain growing the pose factors. */
+ for (int i = 1; i < ik_chain->tot_segments; i++) {
+
+ /* Grow the factors to get the new segment origin. */
+ sculpt_pose_grow_pose_factor(sd,
+ ob,
+ ss,
+ NULL,
+ next_chain_segment_target,
+ chain_segment_len,
+ ik_chain->segments[i].orig,
+ pose_factor_grow);
+ copy_v3_v3(next_chain_segment_target, ik_chain->segments[i].orig);
+
+ /* Create the weights for this segment from the difference between the previous grow factor
+ * iteration an the current iteration. */
+ for (int j = 0; j < totvert; j++) {
+ ik_chain->segments[i].weights[j] = pose_factor_grow[j] - pose_factor_grow_prev[j];
+ /* Store the current grow factor status for the next interation. */
+ pose_factor_grow_prev[j] = pose_factor_grow[j];
+ }
+ }
+
+ /* Init the origin/head pairs of all the segments from the calculated origins. */
+ float origin[3];
+ float head[3];
+ for (int i = 0; i < ik_chain->tot_segments; i++) {
+ if (i == 0) {
+ copy_v3_v3(head, initial_location);
+ copy_v3_v3(origin, ik_chain->segments[i].orig);
+ }
+ else {
+ copy_v3_v3(head, ik_chain->segments[i - 1].orig);
+ copy_v3_v3(origin, ik_chain->segments[i].orig);
+ }
+ copy_v3_v3(ik_chain->segments[i].orig, origin);
+ 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);
+ }
- sculpt_pose_calc_pose_data(
- sd, ob, ss, initial_location, radius, br->pose_offset, ss->cache->pose_origin, pose_factor);
+ MEM_freeN(pose_factor_grow);
+ MEM_freeN(pose_factor_grow_prev);
- copy_v3_v3(ss->cache->pose_initial_co, initial_location);
- ss->cache->pose_factor = pose_factor;
+ return ik_chain;
+}
+static void sculpt_pose_brush_init(Sculpt *sd, Object *ob, SculptSession *ss, Brush *br)
+{
PBVHNode **nodes;
PBVH *pbvh = ob->sculpt->pbvh;
int totnode;
@@ -3867,11 +4195,18 @@ static void sculpt_pose_brush_init(
.nodes = nodes,
};
- /* Smooth the pose brush factor for cleaner deformation */
- for (int i = 0; i < br->pose_smooth_iterations; i++) {
- PBVHParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
- BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings);
+ /* Init the IK chain that is going to be used to deform the vertices. */
+ ss->cache->pose_ik_chain = sculpt_pose_ik_chain_init(
+ sd, ob, ss, br, ss->cache->true_location, ss->cache->radius);
+
+ /* Smooth the weights of each segment for cleaner deformation. */
+ for (int ik = 0; ik < ss->cache->pose_ik_chain->tot_segments; ik++) {
+ data.pose_factor = ss->cache->pose_ik_chain->segments[ik].weights;
+ for (int i = 0; i < br->pose_smooth_iterations; i++) {
+ PBVHParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, (sd->flags & SCULPT_USE_OPENMP), totnode);
+ BKE_pbvh_parallel_range(0, totnode, &data, pose_brush_init_task_cb_ex, &settings);
+ }
}
MEM_SAFE_FREE(nodes);
@@ -5625,24 +5960,14 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
/* Build a list of all nodes that are potentially within the brush's area of influence */
/* These brushes need to update all nodes as they are not constrained by the brush radius */
- if (brush->sculpt_tool == SCULPT_TOOL_ELASTIC_DEFORM) {
+ /* Elastic deform needs all nodes to avoid artifacts as the effect of the brush is not
+ * constrained by the radius */
+ /* Pose needs all nodes because it applies all symmetry iterations at the same time and the IK
+ * chain can grow to any area of the model. */
+ /* This can be optimized by filtering the nodes after calculating the chain. */
+ if (ELEM(brush->sculpt_tool, SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_POSE)) {
BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
}
- else if (brush->sculpt_tool == SCULPT_TOOL_POSE) {
- /* After smoothing the pose factor an arbitrary number of times, the pose factor values can
- * expand to nodes that are not inside the original radius of the brush. Using a slightly
- * bigger radius should prevent those artifacts. */
- /* We can optimize this further by removing the nodes that have all 0 values in the pose factor
- * after calculating it. */
- float final_radius = ss->cache->radius * 1.5f * (1.0f + brush->pose_offset);
- SculptSearchSphereData data = {
- .ss = ss,
- .sd = sd,
- .radius_squared = final_radius * final_radius,
- .original = true,
- };
- BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, &totnode);
- }
else {
const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true :
ss->cache->original;
@@ -5686,7 +6011,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
if (brush->sculpt_tool == SCULPT_TOOL_POSE && ss->cache->first_time &&
ss->cache->mirror_symmetry_pass == 0) {
- sculpt_pose_brush_init(sd, ob, ss, brush, ss->cache->location, ss->cache->radius);
+ sculpt_pose_brush_init(sd, ob, ss, brush);
}
bool invert = ss->cache->pen_flip || ss->cache->invert || brush->flag & BRUSH_DIR_IN;
@@ -6301,8 +6626,8 @@ void sculpt_cache_free(StrokeCache *cache)
if (cache->dial) {
MEM_freeN(cache->dial);
}
- if (cache->pose_factor) {
- MEM_freeN(cache->pose_factor);
+ if (cache->pose_ik_chain) {
+ sculpt_pose_ik_chain_free(cache->pose_ik_chain);
}
MEM_freeN(cache);
}
@@ -9969,79 +10294,6 @@ void ED_sculpt_init_transform(struct bContext *C)
sculpt_filter_cache_init(ob, sd);
}
-typedef enum PaintSymmetryAreas {
- AREA_SYMM_X = (1 << 0),
- AREA_SYMM_Y = (1 << 1),
- AREA_SYMM_Z = (1 << 2),
-} PaintSymmetryAreas;
-
-static char sculpt_get_vertex_symm_area(float co[3])
-{
- float vco[3];
- char symm_area = 0;
- copy_v3_v3(vco, co);
- if (vco[0] < 0) {
- symm_area |= AREA_SYMM_X;
- }
- if (vco[1] < 0) {
- symm_area |= AREA_SYMM_Y;
- }
- if (vco[2] < 0) {
- symm_area |= AREA_SYMM_Z;
- }
- return symm_area;
-}
-
-static void flip_qt(float qt[4], char symm)
-{
- float euler[3];
- if (symm & PAINT_SYMM_X) {
- quat_to_eul(euler, qt);
- euler[1] = -euler[1];
- euler[2] = -euler[2];
- eul_to_quat(qt, euler);
- }
- if (symm & PAINT_SYMM_Y) {
- quat_to_eul(euler, qt);
- euler[0] = -euler[0];
- euler[2] = -euler[2];
- eul_to_quat(qt, euler);
- }
- if (symm & PAINT_SYMM_Z) {
- quat_to_eul(euler, qt);
- euler[0] = -euler[0];
- euler[1] = -euler[1];
- eul_to_quat(qt, euler);
- }
-}
-
-static void sculpt_flip_transform_by_symm_area(
- float disp[3], float rot[4], char symm, char symmarea, float pivot[3])
-{
-
- for (char i = 0; i < 3; i++) {
- char symm_it = 1 << i;
- if (symm & symm_it) {
- if (symmarea & symm_it) {
- if (disp) {
- flip_v3(disp, symm_it);
- }
- if (rot) {
- flip_qt(rot, symm_it);
- }
- }
- if (pivot[0] < 0) {
- if (disp) {
- flip_v3(disp, symm_it);
- }
- if (rot) {
- flip_qt(rot, symm_it);
- }
- }
- }
- }
-}
-
static void sculpt_transform_task_cb(void *__restrict userdata,
const int i,
const TaskParallelTLS *__restrict UNUSED(tls))
@@ -10103,7 +10355,9 @@ void ED_sculpt_update_modal_transform(struct bContext *C)
transform_mat[4][4];
copy_v3_v3(final_pivot_pos, ss->pivot_pos);
- for (int i = 0; i < 8; i++) {
+ for (int i = 0; i < PAINT_SYMM_AREAS; i++) {
+ ePaintSymmetryAreas v_symm = i;
+
copy_v3_v3(final_pivot_pos, ss->pivot_pos);
unit_m4(pivot_mat);
@@ -10114,20 +10368,20 @@ void ED_sculpt_update_modal_transform(struct bContext *C)
/* Translation matrix */
sub_v3_v3v3(d_t, ss->pivot_pos, ss->init_pivot_pos);
- sculpt_flip_transform_by_symm_area(d_t, NULL, symm, (char)i, ss->init_pivot_pos);
+ sculpt_flip_v3_by_symm_area(d_t, symm, v_symm, ss->init_pivot_pos);
translate_m4(t_mat, d_t[0], d_t[1], d_t[2]);
/* Rotation matrix */
sub_qt_qtqt(d_r, ss->pivot_rot, ss->init_pivot_rot);
normalize_qt(d_r);
- sculpt_flip_transform_by_symm_area(NULL, d_r, symm, (char)i, ss->init_pivot_pos);
+ sculpt_flip_quat_by_symm_area(d_r, symm, v_symm, ss->init_pivot_pos);
quat_to_mat4(r_mat, d_r);
/* Scale matrix */
size_to_mat4(s_mat, ss->pivot_scale);
/* Pivot matrix */
- sculpt_flip_transform_by_symm_area(final_pivot_pos, NULL, symm, (char)i, ss->init_pivot_pos);
+ sculpt_flip_v3_by_symm_area(final_pivot_pos, symm, v_symm, ss->init_pivot_pos);
translate_m4(pivot_mat, final_pivot_pos[0], final_pivot_pos[1], final_pivot_pos[2]);
invert_m4_m4(pivot_imat, pivot_mat);
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index e9ad31d6f25..e8a6369c3b5 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -37,6 +37,7 @@ struct KeyBlock;
struct Object;
struct SculptUndoNode;
struct bContext;
+struct PoseIKChainSegment;
bool sculpt_mode_poll(struct bContext *C);
bool sculpt_mode_poll_view3d(struct bContext *C);
@@ -74,6 +75,15 @@ void sculpt_pose_calc_pose_data(struct Sculpt *sd,
float *r_pose_origin,
float *r_pose_factor);
+struct PoseIKChain *sculpt_pose_ik_chain_init(struct Sculpt *sd,
+ struct Object *ob,
+ struct SculptSession *ss,
+ struct Brush *br,
+ const float initial_location[3],
+ const float radius);
+
+void sculpt_pose_ik_chain_free(struct PoseIKChain *ik_chain);
+
/* Sculpt PBVH abstraction API */
const float *sculpt_vertex_co_get(struct SculptSession *ss, int index);
@@ -201,10 +211,9 @@ typedef struct SculptThreadedTaskData {
float *prev_mask;
- float *pose_origin;
- float *pose_initial_co;
float *pose_factor;
- float (*transform_rot)[4], (*transform_trans)[4], (*transform_trans_inv)[4];
+ float *pose_initial_co;
+ int pose_chain_segment;
float multiplane_scrape_angle;
float multiplane_scrape_planes[2][4];
@@ -250,7 +259,7 @@ typedef struct {
struct Sculpt *sd;
struct SculptSession *ss;
float radius_squared;
- float *center;
+ const float *center;
bool original;
bool ignore_fully_masked;
} SculptSearchSphereData;
@@ -379,9 +388,7 @@ typedef struct StrokeCache {
float anchored_location[3];
/* Pose brush */
- float *pose_factor;
- float pose_initial_co[3];
- float pose_origin[3];
+ struct PoseIKChain *pose_ik_chain;
float vertex_rotation; /* amount to rotate the vertices when using rotate brush */
struct Dial *dial;