diff options
-rw-r--r-- | release/scripts/startup/bl_ui/space_view3d_toolbar.py | 7 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_cursor.c | 7 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 146 | ||||
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt_intern.h | 5 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_brush_types.h | 5 | ||||
-rw-r--r-- | source/blender/makesrna/intern/rna_brush.c | 7 |
6 files changed, 157 insertions, 20 deletions
diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 96a27ae796c..03788ed410a 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -424,9 +424,10 @@ class VIEW3D_PT_tools_brush(Panel, View3DPaintPanel): row.prop(brush, "elastic_deform_type") row = col.row() row.prop(brush, "elastic_deform_volume_preservation", slider=True) - - - if brush.sculpt_tool == 'GRAB': + elif brush.sculpt_tool == 'POSE': + row = col.row() + row.prop(brush, "pose_offset") + elif brush.sculpt_tool == 'GRAB': col.separator() row = col.row() row.prop(brush, "use_grab_active_vertex") diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index df3d4b115cc..286cf97c20e 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1352,10 +1352,12 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) SculptCursorGeometryInfo gi; float mouse[2] = {x - ar->winrct.xmin, y - ar->winrct.ymin}; + int prev_active_vertex_index = -1; bool is_cursor_over_mesh = false; /* Update the active vertex */ - if ((mode == PAINT_MODE_SCULPT) && !ups->stroke_active) { + if ((mode == PAINT_MODE_SCULPT) && ss && !ups->stroke_active) { + prev_active_vertex_index = ss->active_vertex_index; is_cursor_over_mesh = sculpt_cursor_geometry_info_update( C, &gi, mouse, !(brush->falloff_shape & BRUSH_AIRBRUSH)); } @@ -1374,7 +1376,6 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) } if (!ups->stroke_active) { - int prev_active_vertex_index = ss->active_vertex_index; bool update_previews = false; if (is_cursor_over_mesh && !alpha_overlay_active) { @@ -1404,7 +1405,7 @@ static void paint_draw_cursor(bContext *C, int x, int y, void *UNUSED(unused)) 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, ss->pose_origin, NULL); + sd, vc.obact, ss, gi.location, rds, brush->pose_offset, ss->pose_origin, NULL); } cursor_draw_point_screen_space(pos, ar, ss->pose_origin, vc.obact->obmat, 5); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 5a5d29806d7..b15262064b3 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -3574,32 +3574,107 @@ static void do_pose_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) BLI_task_parallel_range(0, totnode, &data, do_pose_brush_task_cb_ex, &settings); } -static void pose_brush_init_task_cb_ex(void *__restrict userdata, - const int n, - const TaskParallelTLS *__restrict UNUSED(tls)) +typedef struct PoseGrowFactorTLSData { + float pos_avg[3]; + int tot_pos_avg; +} PoseGrowFactorTLSData; + +static void pose_brush_grow_factor_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict tls) { SculptThreadedTaskData *data = userdata; + PoseGrowFactorTLSData *gftd = tls->userdata_chunk; SculptSession *ss = data->ob->sculpt; + const char symm = data->sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; PBVHVertexIter vd; BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { SculptVertexNeighborIter ni; - float avg = 0; - int total = 0; + float max = 0.0f; sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) { - avg += ss->cache->pose_factor[ni.index]; - total++; + float vmask_f = data->prev_mask[ni.index]; + if (vmask_f > max) { + max = vmask_f; + } } sculpt_vertex_neighbors_iter_end(ni); - - if (total > 0) { - ss->cache->pose_factor[vd.index] = avg / (float)total; + if (max != data->pose_factor[vd.index]) { + if (check_vertex_pivot_symmetry(vd.co, ss->cache->pose_initial_co, symm)) { + add_v3_v3(gftd->pos_avg, vd.co); + gftd->tot_pos_avg++; + } } + data->pose_factor[vd.index] = max; } + BKE_pbvh_vertex_iter_end; } +static void pose_brush_grow_factor_finalize(void *__restrict userdata, void *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + PoseGrowFactorTLSData *gftd = tls; + add_v3_v3(data->tot_pos_avg, gftd->pos_avg); + data->tot_pos_count += gftd->tot_pos_avg; +} + +/* 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) +{ + PBVHNode **nodes; + PBVH *pbvh = ob->sculpt->pbvh; + int totnode; + + BKE_pbvh_search_gather(pbvh, NULL, NULL, &nodes, &totnode); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = nodes, + .totnode = totnode, + .pose_factor = pose_factor, + }; + TaskParallelSettings settings; + PoseGrowFactorTLSData gftd; + gftd.tot_pos_avg = 0; + zero_v3(gftd.pos_avg); + BLI_parallel_range_settings_defaults(&settings); + settings.func_finalize = pose_brush_grow_factor_finalize; + settings.userdata_chunk = &gftd; + settings.userdata_chunk_size = sizeof(PoseGrowFactorTLSData); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + + bool grow_next_iteration = true; + float prev_len = FLT_MAX; + data.prev_mask = MEM_mallocN(sculpt_vertex_count_get(ss) * sizeof(float), "prev mask"); + while (grow_next_iteration) { + zero_v3(data.tot_pos_avg); + data.tot_pos_count = 0; + zero_v3(gftd.pos_avg); + gftd.tot_pos_avg = 0; + memcpy(data.prev_mask, pose_factor, sculpt_vertex_count_get(ss) * sizeof(float)); + BLI_task_parallel_range(0, totnode, &data, pose_brush_grow_factor_task_cb_ex, &settings); + if (data.tot_pos_count != 0) { + mul_v3_fl(data.tot_pos_avg, 1.0f / (float)data.tot_pos_count); + float len = len_v3v3(data.tot_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; + } + } + MEM_freeN(data.prev_mask); +} + static bool sculpt_pose_brush_is_vertex_inside_brush_radius(float vertex[3], const float br_co[3], float radius, @@ -3626,6 +3701,7 @@ void sculpt_pose_calc_pose_data(Sculpt *sd, SculptSession *ss, float initial_location[3], float radius, + float pose_offset, float *r_pose_origin, float *r_pose_factor) { @@ -3708,7 +3784,43 @@ void sculpt_pose_calc_pose_data(Sculpt *sd, if (tot_co > 0) { mul_v3_fl(pose_origin, 1.0f / (float)tot_co); } + + /* Offset the pose origin */ + float pose_d[3]; + sub_v3_v3v3(pose_d, pose_origin, pose_initial_co); + normalize_v3(pose_d); + madd_v3_v3fl(pose_origin, pose_d, radius * pose_offset); copy_v3_v3(r_pose_origin, pose_origin); + + if (pose_offset != 0 && calc_pose_factor) { + sculpt_pose_grow_pose_factor(sd, ob, ss, pose_origin, r_pose_factor); + } +} + +static void pose_brush_init_task_cb_ex(void *__restrict userdata, + const int n, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + SculptVertexNeighborIter ni; + float avg = 0; + int total = 0; + sculpt_vertex_neighbors_iter_begin(ss, vd.index, ni) + { + avg += ss->cache->pose_factor[ni.index]; + total++; + } + sculpt_vertex_neighbors_iter_end(ni); + + if (total > 0) { + ss->cache->pose_factor[vd.index] = avg / (float)total; + } + } + BKE_pbvh_vertex_iter_end; } static void sculpt_pose_brush_init( @@ -3717,12 +3829,11 @@ static void sculpt_pose_brush_init( float *pose_factor = MEM_callocN(sculpt_vertex_count_get(ss) * sizeof(float), "Pose factor"); sculpt_pose_calc_pose_data( - sd, ob, ss, initial_location, radius, ss->cache->pose_origin, pose_factor); + sd, ob, ss, initial_location, radius, br->pose_offset, ss->cache->pose_origin, pose_factor); copy_v3_v3(ss->cache->pose_initial_co, initial_location); ss->cache->pose_factor = pose_factor; - /* Smooth the pose brush factor for cleaner deformation */ PBVHNode **nodes; PBVH *pbvh = ob->sculpt->pbvh; int totnode; @@ -3736,6 +3847,7 @@ static void sculpt_pose_brush_init( .nodes = nodes, }; + /* Smooth the pose brush factor for cleaner deformation */ for (int i = 0; i < 4; i++) { TaskParallelSettings settings; BLI_parallel_range_settings_defaults(&settings); @@ -5082,6 +5194,16 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe }; BKE_pbvh_search_gather(ss->pbvh, NULL, &data, &nodes, &totnode); } + else if (brush->sculpt_tool == SCULPT_TOOL_POSE) { + float final_radius = ss->cache->radius * (1 + brush->pose_offset); + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = final_radius * final_radius, + .original = true, + }; + BKE_pbvh_search_gather(ss->pbvh, NULL, &data, &nodes, &totnode); + } else { const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index ba7c2e601ff..94ceb25c011 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -63,6 +63,7 @@ void sculpt_pose_calc_pose_data(struct Sculpt *sd, struct SculptSession *ss, float initial_location[3], float radius, + float pose_offset, float *r_pose_origin, float *r_pose_factor); @@ -198,8 +199,12 @@ typedef struct SculptThreadedTaskData { float *pose_origin; float *pose_initial_co; + float *pose_factor; float (*transform_rot)[4], (*transform_trans)[4], (*transform_trans_inv)[4]; + float tot_pos_avg[3]; + int tot_pos_count; + float max_distance_squared; float nearest_vertex_search_co[3]; int nearest_vertex_index; diff --git a/source/blender/makesdna/DNA_brush_types.h b/source/blender/makesdna/DNA_brush_types.h index a1b50e8f125..aec28c0fe75 100644 --- a/source/blender/makesdna/DNA_brush_types.h +++ b/source/blender/makesdna/DNA_brush_types.h @@ -321,11 +321,12 @@ typedef struct Brush { int curve_preset; int automasking_flags; - char _pad1[4]; - int elastic_deform_type; float elastic_deform_volume_preservation; + /* pose */ + float pose_offset; + /* overlay */ int texture_overlay_alpha; int mask_overlay_alpha; diff --git a/source/blender/makesrna/intern/rna_brush.c b/source/blender/makesrna/intern/rna_brush.c index 9633f6595e3..8b111830cdd 100644 --- a/source/blender/makesrna/intern/rna_brush.c +++ b/source/blender/makesrna/intern/rna_brush.c @@ -1863,6 +1863,13 @@ static void rna_def_brush(BlenderRNA *brna) RNA_def_property_ui_text(prop, "Crease Brush Pinch Factor", "How much the crease brush pinches"); RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "pose_offset", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, NULL, "pose_offset"); + RNA_def_property_range(prop, 0.0f, 2.0f); + RNA_def_property_ui_text( + prop, "Pose Origin Offset", "Offset of the pose origin in relation to the brush radius"); + RNA_def_property_update(prop, 0, "rna_Brush_update"); + prop = RNA_def_property(srna, "auto_smooth_factor", PROP_FLOAT, PROP_FACTOR); RNA_def_property_float_sdna(prop, NULL, "autosmooth_factor"); RNA_def_property_float_default(prop, 0); |