diff options
Diffstat (limited to 'source/blender/editors/sculpt_paint/sculpt.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/sculpt.c | 9838 |
1 files changed, 4949 insertions, 4889 deletions
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index ad2cfb5f9fc..9309def4a77 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -22,7 +22,6 @@ * \ingroup edsculpt */ - #include "MEM_guardedalloc.h" #include "BLI_math.h" @@ -103,188 +102,175 @@ * (used for flushing updates at enter/exit sculpt mode) */ static bool sculpt_has_active_modifiers(Scene *scene, Object *ob) { - ModifierData *md; - VirtualModifierData virtualModifierData; + ModifierData *md; + VirtualModifierData virtualModifierData; - md = modifiers_getVirtualModifierList(ob, &virtualModifierData); + md = modifiers_getVirtualModifierList(ob, &virtualModifierData); - /* exception for shape keys because we can edit those */ - for (; md; md = md->next) { - if (modifier_isEnabled(scene, md, eModifierMode_Realtime)) - return 1; - } + /* exception for shape keys because we can edit those */ + for (; md; md = md->next) { + if (modifier_isEnabled(scene, md, eModifierMode_Realtime)) + return 1; + } - return 0; + return 0; } static bool sculpt_tool_needs_original(const char sculpt_tool) { - return ELEM(sculpt_tool, - SCULPT_TOOL_GRAB, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB, - SCULPT_TOOL_LAYER); + return ELEM( + sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB, SCULPT_TOOL_LAYER); } static bool sculpt_tool_is_proxy_used(const char sculpt_tool) { - return ELEM(sculpt_tool, - SCULPT_TOOL_SMOOTH, - SCULPT_TOOL_LAYER); + return ELEM(sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_LAYER); } -static bool sculpt_brush_use_topology_rake( - const SculptSession *ss, const Brush *brush) +static bool sculpt_brush_use_topology_rake(const SculptSession *ss, const Brush *brush) { - return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(brush->sculpt_tool) && - (brush->topology_rake_factor > 0.0f) && - (ss->bm != NULL); + return SCULPT_TOOL_HAS_TOPOLOGY_RAKE(brush->sculpt_tool) && + (brush->topology_rake_factor > 0.0f) && (ss->bm != NULL); } /** * Test whether the #StrokeCache.sculpt_normal needs update in #do_brush_action */ -static int sculpt_brush_needs_normal( - const SculptSession *ss, const Brush *brush) +static int sculpt_brush_needs_normal(const SculptSession *ss, const Brush *brush) { - return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && - (ss->cache->normal_weight > 0.0f)) || + return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool) && + (ss->cache->normal_weight > 0.0f)) || - ELEM(brush->sculpt_tool, - SCULPT_TOOL_BLOB, - SCULPT_TOOL_CREASE, - SCULPT_TOOL_DRAW, - SCULPT_TOOL_LAYER, - SCULPT_TOOL_NUDGE, - SCULPT_TOOL_ROTATE, - SCULPT_TOOL_THUMB) || + ELEM(brush->sculpt_tool, + SCULPT_TOOL_BLOB, + SCULPT_TOOL_CREASE, + SCULPT_TOOL_DRAW, + SCULPT_TOOL_LAYER, + SCULPT_TOOL_NUDGE, + SCULPT_TOOL_ROTATE, + SCULPT_TOOL_THUMB) || - (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)) || - sculpt_brush_use_topology_rake(ss, brush); + (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA)) || + sculpt_brush_use_topology_rake(ss, brush); } /** \} */ static bool sculpt_brush_needs_rake_rotation(const Brush *brush) { - return SCULPT_TOOL_HAS_RAKE(brush->sculpt_tool) && (brush->rake_factor != 0.0f); + return SCULPT_TOOL_HAS_RAKE(brush->sculpt_tool) && (brush->rake_factor != 0.0f); } typedef enum StrokeFlags { - CLIP_X = 1, - CLIP_Y = 2, - CLIP_Z = 4, + CLIP_X = 1, + CLIP_Y = 2, + CLIP_Z = 4, } StrokeFlags; /************** Access to original unmodified vertex data *************/ typedef struct { - BMLog *bm_log; + BMLog *bm_log; - SculptUndoNode *unode; - float (*coords)[3]; - short (*normals)[3]; - const float *vmasks; + SculptUndoNode *unode; + float (*coords)[3]; + short (*normals)[3]; + const float *vmasks; - /* Original coordinate, normal, and mask */ - const float *co; - const short *no; - float mask; + /* Original coordinate, normal, and mask */ + const float *co; + const short *no; + float mask; } SculptOrigVertData; - /* Initialize a SculptOrigVertData for accessing original vertex data; * handles BMesh, mesh, and multires */ static void sculpt_orig_vert_data_unode_init(SculptOrigVertData *data, Object *ob, SculptUndoNode *unode) { - SculptSession *ss = ob->sculpt; - BMesh *bm = ss->bm; + SculptSession *ss = ob->sculpt; + BMesh *bm = ss->bm; - memset(data, 0, sizeof(*data)); - data->unode = unode; + memset(data, 0, sizeof(*data)); + data->unode = unode; - if (bm) { - data->bm_log = ss->bm_log; - } - else { - data->coords = data->unode->co; - data->normals = data->unode->no; - data->vmasks = data->unode->mask; - } + if (bm) { + data->bm_log = ss->bm_log; + } + else { + data->coords = data->unode->co; + data->normals = data->unode->no; + data->vmasks = data->unode->mask; + } } /* Initialize a SculptOrigVertData for accessing original vertex data; * handles BMesh, mesh, and multires */ -static void sculpt_orig_vert_data_init(SculptOrigVertData *data, - Object *ob, - PBVHNode *node) +static void sculpt_orig_vert_data_init(SculptOrigVertData *data, Object *ob, PBVHNode *node) { - SculptUndoNode *unode; - unode = sculpt_undo_push_node(ob, node, SCULPT_UNDO_COORDS); - sculpt_orig_vert_data_unode_init(data, ob, unode); + SculptUndoNode *unode; + unode = sculpt_undo_push_node(ob, node, SCULPT_UNDO_COORDS); + sculpt_orig_vert_data_unode_init(data, ob, unode); } /* Update a SculptOrigVertData for a particular vertex from the PBVH * iterator */ -static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, - PBVHVertexIter *iter) -{ - if (orig_data->unode->type == SCULPT_UNDO_COORDS) { - if (orig_data->bm_log) { - BM_log_original_vert_data( - orig_data->bm_log, iter->bm_vert, - &orig_data->co, &orig_data->no); - } - else { - orig_data->co = orig_data->coords[iter->i]; - orig_data->no = orig_data->normals[iter->i]; - } - } - else if (orig_data->unode->type == SCULPT_UNDO_MASK) { - if (orig_data->bm_log) { - orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); - } - else { - orig_data->mask = orig_data->vmasks[iter->i]; - } - } +static void sculpt_orig_vert_data_update(SculptOrigVertData *orig_data, PBVHVertexIter *iter) +{ + if (orig_data->unode->type == SCULPT_UNDO_COORDS) { + if (orig_data->bm_log) { + BM_log_original_vert_data(orig_data->bm_log, iter->bm_vert, &orig_data->co, &orig_data->no); + } + else { + orig_data->co = orig_data->coords[iter->i]; + orig_data->no = orig_data->normals[iter->i]; + } + } + else if (orig_data->unode->type == SCULPT_UNDO_MASK) { + if (orig_data->bm_log) { + orig_data->mask = BM_log_original_mask(orig_data->bm_log, iter->bm_vert); + } + else { + orig_data->mask = orig_data->vmasks[iter->i]; + } + } } static void sculpt_rake_data_update(struct SculptRakeData *srd, const float co[3]) { - float rake_dist = len_v3v3(srd->follow_co, co); - if (rake_dist > srd->follow_dist) { - interp_v3_v3v3(srd->follow_co, srd->follow_co, co, rake_dist - srd->follow_dist); - } + float rake_dist = len_v3v3(srd->follow_co, co); + if (rake_dist > srd->follow_dist) { + interp_v3_v3v3(srd->follow_co, srd->follow_co, co, rake_dist - srd->follow_dist); + } } - -static void sculpt_rake_rotate( - const SculptSession *ss, const float sculpt_co[3], const float v_co[3], float factor, float r_delta[3]) +static void sculpt_rake_rotate(const SculptSession *ss, + const float sculpt_co[3], + const float v_co[3], + float factor, + float r_delta[3]) { - float vec_rot[3]; + float vec_rot[3]; #if 0 - /* lerp */ - sub_v3_v3v3(vec_rot, v_co, sculpt_co); - mul_qt_v3(ss->cache->rake_rotation_symmetry, vec_rot); - add_v3_v3(vec_rot, sculpt_co); - sub_v3_v3v3(r_delta, vec_rot, v_co); - mul_v3_fl(r_delta, factor); + /* lerp */ + sub_v3_v3v3(vec_rot, v_co, sculpt_co); + mul_qt_v3(ss->cache->rake_rotation_symmetry, vec_rot); + add_v3_v3(vec_rot, sculpt_co); + sub_v3_v3v3(r_delta, vec_rot, v_co); + mul_v3_fl(r_delta, factor); #else - /* slerp */ - float q_interp[4]; - sub_v3_v3v3(vec_rot, v_co, sculpt_co); + /* slerp */ + float q_interp[4]; + sub_v3_v3v3(vec_rot, v_co, sculpt_co); - copy_qt_qt(q_interp, ss->cache->rake_rotation_symmetry); - pow_qt_fl_normalized(q_interp, factor); - mul_qt_v3(q_interp, vec_rot); + copy_qt_qt(q_interp, ss->cache->rake_rotation_symmetry); + pow_qt_fl_normalized(q_interp, factor); + mul_qt_v3(q_interp, vec_rot); - add_v3_v3(vec_rot, sculpt_co); - sub_v3_v3v3(r_delta, vec_rot, v_co); + add_v3_v3(vec_rot, sculpt_co); + sub_v3_v3v3(r_delta, vec_rot, v_co); #endif - } /** @@ -292,26 +278,29 @@ static void sculpt_rake_rotate( * * \param grab_delta: Typically from `ss->cache->grab_delta_symmetry`. */ -static void sculpt_project_v3_normal_align(SculptSession *ss, const float normal_weight, float grab_delta[3]) +static void sculpt_project_v3_normal_align(SculptSession *ss, + const float normal_weight, + float grab_delta[3]) { - /* signed to support grabbing in (to make a hole) as well as out. */ - const float len_signed = dot_v3v3(ss->cache->sculpt_normal_symm, grab_delta); + /* signed to support grabbing in (to make a hole) as well as out. */ + const float len_signed = dot_v3v3(ss->cache->sculpt_normal_symm, grab_delta); - /* this scale effectively projects the offset so dragging follows the cursor, - * as the normal points towards the view, the scale increases. */ - float len_view_scale; - { - float view_aligned_normal[3]; - project_plane_v3_v3v3(view_aligned_normal, ss->cache->sculpt_normal_symm, ss->cache->view_normal); - len_view_scale = fabsf(dot_v3v3(view_aligned_normal, ss->cache->sculpt_normal_symm)); - len_view_scale = (len_view_scale > FLT_EPSILON) ? 1.0f / len_view_scale : 1.0f; - } + /* this scale effectively projects the offset so dragging follows the cursor, + * as the normal points towards the view, the scale increases. */ + float len_view_scale; + { + float view_aligned_normal[3]; + project_plane_v3_v3v3( + view_aligned_normal, ss->cache->sculpt_normal_symm, ss->cache->view_normal); + len_view_scale = fabsf(dot_v3v3(view_aligned_normal, ss->cache->sculpt_normal_symm)); + len_view_scale = (len_view_scale > FLT_EPSILON) ? 1.0f / len_view_scale : 1.0f; + } - mul_v3_fl(grab_delta, 1.0f - normal_weight); - madd_v3_v3fl(grab_delta, ss->cache->sculpt_normal_symm, (len_signed * normal_weight) * len_view_scale); + mul_v3_fl(grab_delta, 1.0f - normal_weight); + madd_v3_v3fl( + grab_delta, ss->cache->sculpt_normal_symm, (len_signed * normal_weight) * len_view_scale); } - /** \name SculptProjectVector * * Fast-path for #project_plane_v3_v3v3 @@ -319,43 +308,39 @@ static void sculpt_project_v3_normal_align(SculptSession *ss, const float normal * \{ */ typedef struct SculptProjectVector { - float plane[3]; - float len_sq; - float len_sq_inv_neg; - bool is_valid; + float plane[3]; + float len_sq; + float len_sq_inv_neg; + bool is_valid; } SculptProjectVector; /** * \param plane: Direction, can be any length. */ -static void sculpt_project_v3_cache_init( - SculptProjectVector *spvc, const float plane[3]) +static void sculpt_project_v3_cache_init(SculptProjectVector *spvc, const float plane[3]) { - copy_v3_v3(spvc->plane, plane); - spvc->len_sq = len_squared_v3(spvc->plane); - spvc->is_valid = (spvc->len_sq > FLT_EPSILON); - spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f; + copy_v3_v3(spvc->plane, plane); + spvc->len_sq = len_squared_v3(spvc->plane); + spvc->is_valid = (spvc->len_sq > FLT_EPSILON); + spvc->len_sq_inv_neg = (spvc->is_valid) ? -1.0f / spvc->len_sq : 0.0f; } /** * Calculate the projection. */ -static void sculpt_project_v3( - const SculptProjectVector *spvc, const float vec[3], - float r_vec[3]) +static void sculpt_project_v3(const SculptProjectVector *spvc, const float vec[3], float r_vec[3]) { #if 0 - project_plane_v3_v3v3(r_vec, vec, spvc->plane); + project_plane_v3_v3v3(r_vec, vec, spvc->plane); #else - /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */ - madd_v3_v3fl(r_vec, spvc->plane, dot_v3v3(vec, spvc->plane) * spvc->len_sq_inv_neg); + /* inline the projection, cache `-1.0 / dot_v3_v3(v_proj, v_proj)` */ + madd_v3_v3fl(r_vec, spvc->plane, dot_v3v3(vec, spvc->plane) * spvc->len_sq_inv_neg); #endif } /** \} */ - /**********************************************************************/ /* Returns true if the stroke will use dynamic topology, false @@ -364,360 +349,351 @@ static void sculpt_project_v3( * Factors: some brushes like grab cannot do dynamic topology. * Others, like smooth, are better without. Same goes for alt- * key smoothing. */ -static bool sculpt_stroke_is_dynamic_topology( - const SculptSession *ss, const Brush *brush) +static bool sculpt_stroke_is_dynamic_topology(const SculptSession *ss, const Brush *brush) { - return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && + return ((BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) && - (!ss->cache || (!ss->cache->alt_smooth)) && + (!ss->cache || (!ss->cache->alt_smooth)) && - /* Requires mesh restore, which doesn't work with - * dynamic-topology */ - !(brush->flag & BRUSH_ANCHORED) && - !(brush->flag & BRUSH_DRAG_DOT) && + /* Requires mesh restore, which doesn't work with + * dynamic-topology */ + !(brush->flag & BRUSH_ANCHORED) && !(brush->flag & BRUSH_DRAG_DOT) && - SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); + SCULPT_TOOL_HAS_DYNTOPO(brush->sculpt_tool)); } /*** paint mesh ***/ -static void paint_mesh_restore_co_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void paint_mesh_restore_co_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; - SculptUndoNode *unode; - SculptUndoType type = (data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); + SculptUndoNode *unode; + SculptUndoType type = (data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : + SCULPT_UNDO_COORDS); - if (ss->bm) { - unode = sculpt_undo_push_node(data->ob, data->nodes[n], type); - } - else { - unode = sculpt_undo_get_node(data->nodes[n]); - } + if (ss->bm) { + unode = sculpt_undo_push_node(data->ob, data->nodes[n], type); + } + else { + unode = sculpt_undo_get_node(data->nodes[n]); + } - if (unode) { - PBVHVertexIter vd; - SculptOrigVertData orig_data; + if (unode) { + PBVHVertexIter vd; + SculptOrigVertData orig_data; - sculpt_orig_vert_data_unode_init(&orig_data, data->ob, unode); + sculpt_orig_vert_data_unode_init(&orig_data, data->ob, unode); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); - if (orig_data.unode->type == SCULPT_UNDO_COORDS) { - copy_v3_v3(vd.co, orig_data.co); - if (vd.no) - copy_v3_v3_short(vd.no, orig_data.no); - else - normal_short_to_float_v3(vd.fno, orig_data.no); - } - else if (orig_data.unode->type == SCULPT_UNDO_MASK) { - *vd.mask = orig_data.mask; - } + if (orig_data.unode->type == SCULPT_UNDO_COORDS) { + copy_v3_v3(vd.co, orig_data.co); + if (vd.no) + copy_v3_v3_short(vd.no, orig_data.no); + else + normal_short_to_float_v3(vd.fno, orig_data.no); + } + else if (orig_data.unode->type == SCULPT_UNDO_MASK) { + *vd.mask = orig_data.mask; + } - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_mark_update(data->nodes[n]); - } + BKE_pbvh_node_mark_update(data->nodes[n]); + } } static void paint_mesh_restore_co(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - PBVHNode **nodes; - int totnode; + PBVHNode **nodes; + int totnode; - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - /* Disable OpenMP when dynamic-topology is enabled. Otherwise, new entries might be inserted by - * sculpt_undo_push_node() into the GHash used internally by BM_log_original_vert_co() by a different thread. - * See T33787. */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + /* Disable OpenMP when dynamic-topology is enabled. Otherwise, new entries might be inserted by + * sculpt_undo_push_node() into the GHash used internally by BM_log_original_vert_co() by a different thread. + * See T33787. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && !ss->bm && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - paint_mesh_restore_co_task_cb, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && !ss->bm && + totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, paint_mesh_restore_co_task_cb, &settings); - if (nodes) - MEM_freeN(nodes); + if (nodes) + MEM_freeN(nodes); } /*** BVH Tree ***/ static void sculpt_extend_redraw_rect_previous(Object *ob, rcti *rect) { - /* expand redraw rect with redraw rect from previous step to - * prevent partial-redraw issues caused by fast strokes. This is - * needed here (not in sculpt_flush_update) as it was before - * because redraw rectangle should be the same in both of - * optimized PBVH draw function and 3d view redraw (if not -- some - * mesh parts could disappear from screen (sergey) */ - SculptSession *ss = ob->sculpt; + /* expand redraw rect with redraw rect from previous step to + * prevent partial-redraw issues caused by fast strokes. This is + * needed here (not in sculpt_flush_update) as it was before + * because redraw rectangle should be the same in both of + * optimized PBVH draw function and 3d view redraw (if not -- some + * mesh parts could disappear from screen (sergey) */ + SculptSession *ss = ob->sculpt; - if (ss->cache) { - if (!BLI_rcti_is_empty(&ss->cache->previous_r)) - BLI_rcti_union(rect, &ss->cache->previous_r); - } + if (ss->cache) { + if (!BLI_rcti_is_empty(&ss->cache->previous_r)) + BLI_rcti_union(rect, &ss->cache->previous_r); + } } /* Get a screen-space rectangle of the modified area */ -bool sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, - Object *ob, rcti *rect) +bool sculpt_get_redraw_rect(ARegion *ar, RegionView3D *rv3d, Object *ob, rcti *rect) { - PBVH *pbvh = ob->sculpt->pbvh; - float bb_min[3], bb_max[3]; + PBVH *pbvh = ob->sculpt->pbvh; + float bb_min[3], bb_max[3]; - if (!pbvh) - return 0; + if (!pbvh) + return 0; - BKE_pbvh_redraw_BB(pbvh, bb_min, bb_max); + BKE_pbvh_redraw_BB(pbvh, bb_min, bb_max); - /* convert 3D bounding box to screen space */ - if (!paint_convert_bb_to_rect(rect, - bb_min, - bb_max, - ar, - rv3d, - ob)) - { - return 0; - } + /* convert 3D bounding box to screen space */ + if (!paint_convert_bb_to_rect(rect, bb_min, bb_max, ar, rv3d, ob)) { + return 0; + } - - return 1; + return 1; } void ED_sculpt_redraw_planes_get(float planes[4][4], ARegion *ar, Object *ob) { - PBVH *pbvh = ob->sculpt->pbvh; - /* copy here, original will be used below */ - rcti rect = ob->sculpt->cache->current_r; + PBVH *pbvh = ob->sculpt->pbvh; + /* copy here, original will be used below */ + rcti rect = ob->sculpt->cache->current_r; - sculpt_extend_redraw_rect_previous(ob, &rect); + sculpt_extend_redraw_rect_previous(ob, &rect); - paint_calc_redraw_planes(planes, ar, ob, &rect); + paint_calc_redraw_planes(planes, ar, ob, &rect); - /* we will draw this rect, so now we can set it as the previous partial rect. - * Note that we don't update with the union of previous/current (rect), only with - * the current. Thus we avoid the rectangle needlessly growing to include - * all the stroke area */ - ob->sculpt->cache->previous_r = ob->sculpt->cache->current_r; + /* we will draw this rect, so now we can set it as the previous partial rect. + * Note that we don't update with the union of previous/current (rect), only with + * the current. Thus we avoid the rectangle needlessly growing to include + * all the stroke area */ + ob->sculpt->cache->previous_r = ob->sculpt->cache->current_r; - /* clear redraw flag from nodes */ - if (pbvh) - BKE_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL); + /* clear redraw flag from nodes */ + if (pbvh) + BKE_pbvh_update(pbvh, PBVH_UpdateRedraw, NULL); } /************************ Brush Testing *******************/ void sculpt_brush_test_init(SculptSession *ss, SculptBrushTest *test) { - RegionView3D *rv3d = ss->cache->vc->rv3d; + RegionView3D *rv3d = ss->cache->vc->rv3d; - test->radius_squared = ss->cache->radius_squared; - copy_v3_v3(test->location, ss->cache->location); - test->dist = 0.0f; /* just for initialize */ + test->radius_squared = ss->cache->radius_squared; + copy_v3_v3(test->location, ss->cache->location); + test->dist = 0.0f; /* just for initialize */ - /* Only for 2D projection. */ - zero_v4(test->plane_view); - zero_v4(test->plane_tool); + /* Only for 2D projection. */ + zero_v4(test->plane_view); + zero_v4(test->plane_tool); - test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; + test->mirror_symmetry_pass = ss->cache->mirror_symmetry_pass; - if (rv3d->rflag & RV3D_CLIPPING) { - test->clip_rv3d = rv3d; - } - else { - test->clip_rv3d = NULL; - } + if (rv3d->rflag & RV3D_CLIPPING) { + test->clip_rv3d = rv3d; + } + else { + test->clip_rv3d = NULL; + } } BLI_INLINE bool sculpt_brush_test_clipping(const SculptBrushTest *test, const float co[3]) { - RegionView3D *rv3d = test->clip_rv3d; - if (!rv3d) { - return false; - } - float symm_co[3]; - flip_v3_v3(symm_co, co, test->mirror_symmetry_pass); - return ED_view3d_clipping_test(rv3d, symm_co, true); + RegionView3D *rv3d = test->clip_rv3d; + if (!rv3d) { + return false; + } + float symm_co[3]; + flip_v3_v3(symm_co, co, test->mirror_symmetry_pass); + return ED_view3d_clipping_test(rv3d, symm_co, true); } bool sculpt_brush_test_sphere(SculptBrushTest *test, const float co[3]) { - float distsq = len_squared_v3v3(co, test->location); + float distsq = len_squared_v3v3(co, test->location); - if (distsq <= test->radius_squared) { - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } - test->dist = sqrtf(distsq); - return 1; - } - else { - return 0; - } + if (distsq <= test->radius_squared) { + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } + test->dist = sqrtf(distsq); + return 1; + } + else { + return 0; + } } bool sculpt_brush_test_sphere_sq(SculptBrushTest *test, const float co[3]) { - float distsq = len_squared_v3v3(co, test->location); + float distsq = len_squared_v3v3(co, test->location); - if (distsq <= test->radius_squared) { - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } - test->dist = distsq; - return 1; - } - else { - return 0; - } + if (distsq <= test->radius_squared) { + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } + test->dist = distsq; + return 1; + } + else { + return 0; + } } bool sculpt_brush_test_sphere_fast(const SculptBrushTest *test, const float co[3]) { - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } - return len_squared_v3v3(co, test->location) <= test->radius_squared; + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } + return len_squared_v3v3(co, test->location) <= test->radius_squared; } bool sculpt_brush_test_circle_sq(SculptBrushTest *test, const float co[3]) { - float co_proj[3]; - closest_to_plane_normalized_v3(co_proj, test->plane_view, co); - float distsq = len_squared_v3v3(co_proj, test->location); + float co_proj[3]; + closest_to_plane_normalized_v3(co_proj, test->plane_view, co); + float distsq = len_squared_v3v3(co_proj, test->location); - if (distsq <= test->radius_squared) { - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } - test->dist = distsq; - return 1; - } - else { - return 0; - } + if (distsq <= test->radius_squared) { + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } + test->dist = distsq; + return 1; + } + else { + return 0; + } } bool sculpt_brush_test_cube(SculptBrushTest *test, const float co[3], float local[4][4]) { - float side = M_SQRT1_2; - float local_co[3]; + float side = M_SQRT1_2; + float local_co[3]; - if (sculpt_brush_test_clipping(test, co)) { - return 0; - } + if (sculpt_brush_test_clipping(test, co)) { + return 0; + } - mul_v3_m4v3(local_co, local, co); + mul_v3_m4v3(local_co, local, co); - local_co[0] = fabsf(local_co[0]); - local_co[1] = fabsf(local_co[1]); - local_co[2] = fabsf(local_co[2]); + local_co[0] = fabsf(local_co[0]); + local_co[1] = fabsf(local_co[1]); + local_co[2] = fabsf(local_co[2]); - if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { - float p = 4.0f; + if (local_co[0] <= side && local_co[1] <= side && local_co[2] <= side) { + float p = 4.0f; - test->dist = ((powf(local_co[0], p) + - powf(local_co[1], p) + - powf(local_co[2], p)) / powf(side, p)); + test->dist = ((powf(local_co[0], p) + powf(local_co[1], p) + powf(local_co[2], p)) / + powf(side, p)); - return 1; - } - else { - return 0; - } + return 1; + } + else { + return 0; + } } -SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape( - SculptSession *ss, SculptBrushTest *test, char falloff_shape) +SculptBrushTestFn sculpt_brush_test_init_with_falloff_shape(SculptSession *ss, + SculptBrushTest *test, + char falloff_shape) { - sculpt_brush_test_init(ss, test); - SculptBrushTestFn sculpt_brush_test_sq_fn; - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - sculpt_brush_test_sq_fn = sculpt_brush_test_sphere_sq; - } - else { - /* PAINT_FALLOFF_SHAPE_TUBE */ - plane_from_point_normal_v3(test->plane_view, test->location, ss->cache->view_normal); - sculpt_brush_test_sq_fn = sculpt_brush_test_circle_sq; - } - return sculpt_brush_test_sq_fn; + sculpt_brush_test_init(ss, test); + SculptBrushTestFn sculpt_brush_test_sq_fn; + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + sculpt_brush_test_sq_fn = sculpt_brush_test_sphere_sq; + } + else { + /* PAINT_FALLOFF_SHAPE_TUBE */ + plane_from_point_normal_v3(test->plane_view, test->location, ss->cache->view_normal); + sculpt_brush_test_sq_fn = sculpt_brush_test_circle_sq; + } + return sculpt_brush_test_sq_fn; } -const float *sculpt_brush_frontface_normal_from_falloff_shape( - SculptSession *ss, char falloff_shape) +const float *sculpt_brush_frontface_normal_from_falloff_shape(SculptSession *ss, + char falloff_shape) { - if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - return ss->cache->sculpt_normal_symm; - } - else { - /* PAINT_FALLOFF_SHAPE_TUBE */ - return ss->cache->view_normal; - } + if (falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + return ss->cache->sculpt_normal_symm; + } + else { + /* PAINT_FALLOFF_SHAPE_TUBE */ + return ss->cache->view_normal; + } } - -static float frontface(const Brush *br, const float sculpt_normal[3], - const short no[3], const float fno[3]) +static float frontface(const Brush *br, + const float sculpt_normal[3], + const short no[3], + const float fno[3]) { - if (br->flag & BRUSH_FRONTFACE) { - float dot; + if (br->flag & BRUSH_FRONTFACE) { + float dot; - if (no) { - float tmp[3]; + if (no) { + float tmp[3]; - normal_short_to_float_v3(tmp, no); - dot = dot_v3v3(tmp, sculpt_normal); - } - else { - dot = dot_v3v3(fno, sculpt_normal); - } - return dot > 0 ? dot : 0; - } - else { - return 1; - } + normal_short_to_float_v3(tmp, no); + dot = dot_v3v3(tmp, sculpt_normal); + } + else { + dot = dot_v3v3(fno, sculpt_normal); + } + return dot > 0 ? dot : 0; + } + else { + return 1; + } } #if 0 static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float location[3], const float area_no[3]) { - if (sculpt_brush_test_sphere_fast(test, co)) { - float t1[3], t2[3], t3[3], dist; + if (sculpt_brush_test_sphere_fast(test, co)) { + float t1[3], t2[3], t3[3], dist; - sub_v3_v3v3(t1, location, co); - sub_v3_v3v3(t2, x2, location); + sub_v3_v3v3(t1, location, co); + sub_v3_v3v3(t2, x2, location); - cross_v3_v3v3(t3, area_no, t1); + cross_v3_v3v3(t3, area_no, t1); - dist = len_v3(t3) / len_v3(t2); + dist = len_v3(t3) / len_v3(t2); - test->dist = dist; + test->dist = dist; - return 1; - } + return 1; + } - return 0; + return 0; } #endif @@ -726,70 +702,73 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test, float co[3], float loca */ static void flip_v3(float v[3], const char symm) { - flip_v3_v3(v, v, symm); + flip_v3_v3(v, v, symm); } static float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle) { - float mirror[3]; - float distsq; + float mirror[3]; + float distsq; - /* flip_v3_v3(mirror, cache->traced_location, symm); */ - flip_v3_v3(mirror, cache->true_location, symm); + /* flip_v3_v3(mirror, cache->traced_location, symm); */ + flip_v3_v3(mirror, cache->true_location, symm); - if (axis != 0) { - float mat[3][3]; - axis_angle_to_mat3_single(mat, axis, angle); - mul_m3_v3(mat, mirror); - } + if (axis != 0) { + float mat[3][3]; + axis_angle_to_mat3_single(mat, axis, angle); + mul_m3_v3(mat, mirror); + } - /* distsq = len_squared_v3v3(mirror, cache->traced_location); */ - distsq = len_squared_v3v3(mirror, cache->true_location); + /* distsq = len_squared_v3v3(mirror, cache->traced_location); */ + distsq = len_squared_v3v3(mirror, cache->true_location); - if (distsq <= 4.0f * (cache->radius_squared)) - return (2.0f * (cache->radius) - sqrtf(distsq)) / (2.0f * (cache->radius)); - else - return 0; + if (distsq <= 4.0f * (cache->radius_squared)) + return (2.0f * (cache->radius) - sqrtf(distsq)) / (2.0f * (cache->radius)); + else + return 0; } -static float calc_radial_symmetry_feather(Sculpt *sd, StrokeCache *cache, const char symm, const char axis) +static float calc_radial_symmetry_feather(Sculpt *sd, + StrokeCache *cache, + const char symm, + const char axis) { - int i; - float overlap; + int i; + float overlap; - overlap = 0; - for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { - const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; - overlap += calc_overlap(cache, symm, axis, angle); - } + overlap = 0; + for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { + const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; + overlap += calc_overlap(cache, symm, axis, angle); + } - return overlap; + return overlap; } static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) { - if (sd->paint.symmetry_flags & PAINT_SYMMETRY_FEATHER) { - float overlap; - int symm = cache->symmetry; - int i; + if (sd->paint.symmetry_flags & PAINT_SYMMETRY_FEATHER) { + float overlap; + int symm = cache->symmetry; + int i; - overlap = 0; - for (i = 0; i <= symm; i++) { - if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + overlap = 0; + for (i = 0; i <= symm; i++) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - overlap += calc_overlap(cache, i, 0, 0); + overlap += calc_overlap(cache, i, 0, 0); - overlap += calc_radial_symmetry_feather(sd, cache, i, 'X'); - overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y'); - overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z'); - } - } + overlap += calc_radial_symmetry_feather(sd, cache, i, 'X'); + overlap += calc_radial_symmetry_feather(sd, cache, i, 'Y'); + overlap += calc_radial_symmetry_feather(sd, cache, i, 'Z'); + } + } - return 1 / overlap; - } - else { - return 1; - } + return 1 / overlap; + } + else { + return 1; + } } /** \name Calculate Normal and Center @@ -805,393 +784,395 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache) * \note These are all _very_ similar, when changing one, check others. * \{ */ -static void calc_area_normal_and_center_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - float (*area_nos)[3] = data->area_nos; - float (*area_cos)[3] = data->area_cos; - - PBVHVertexIter vd; - SculptUndoNode *unode = NULL; - - float private_co[2][3] = {{0.0f}}; - float private_no[2][3] = {{0.0f}}; - int private_count[2] = {0}; - bool use_original = false; - - if (ss->cache->original) { - unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); - use_original = (unode->co || unode->bm_entry); - } - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - - /* when the mesh is edited we can't rely on original coords - * (original mesh may not even have verts in brush radius) */ - if (use_original && data->has_bm_orco) { - float (*orco_coords)[3]; - int (*orco_tris)[3]; - int orco_tris_num; - int i; - - BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords); - - for (i = 0; i < orco_tris_num; i++) { - const float *co_tri[3] = { - orco_coords[orco_tris[i][0]], - orco_coords[orco_tris[i][1]], - orco_coords[orco_tris[i][2]], - }; - float co[3]; - - closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); - - if (sculpt_brush_test_sq_fn(&test, co)) { - float no[3]; - int flip_index; - - normal_tri_v3(no, UNPACK3(co_tri)); - - flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); - if (area_cos) - add_v3_v3(private_co[flip_index], co); - if (area_nos) - add_v3_v3(private_no[flip_index], no); - private_count[flip_index] += 1; - } - } - } - else { - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - const float *co; - const short *no_s; /* bm_vert only */ - - if (use_original) { - if (unode->bm_entry) { - BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); - } - else { - co = unode->co[vd.i]; - no_s = unode->no[vd.i]; - } - } - else { - co = vd.co; - } - - if (sculpt_brush_test_sq_fn(&test, co)) { - float no_buf[3]; - const float *no; - int flip_index; - - if (use_original) { - normal_short_to_float_v3(no_buf, no_s); - no = no_buf; - } - else { - if (vd.no) { - normal_short_to_float_v3(no_buf, vd.no); - no = no_buf; - } - else { - no = vd.fno; - } - } - - flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); - if (area_cos) - add_v3_v3(private_co[flip_index], co); - if (area_nos) - add_v3_v3(private_no[flip_index], no); - private_count[flip_index] += 1; - } - } - BKE_pbvh_vertex_iter_end; - } - - BLI_mutex_lock(&data->mutex); - - /* for flatten center */ - if (area_cos) { - add_v3_v3(area_cos[0], private_co[0]); - add_v3_v3(area_cos[1], private_co[1]); - } - - /* for area normal */ - if (area_nos) { - add_v3_v3(area_nos[0], private_no[0]); - add_v3_v3(area_nos[1], private_no[1]); - } - - /* weights */ - data->count[0] += private_count[0]; - data->count[1] += private_count[1]; - - BLI_mutex_unlock(&data->mutex); +static void calc_area_normal_and_center_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + float(*area_nos)[3] = data->area_nos; + float(*area_cos)[3] = data->area_cos; + + PBVHVertexIter vd; + SculptUndoNode *unode = NULL; + + float private_co[2][3] = {{0.0f}}; + float private_no[2][3] = {{0.0f}}; + int private_count[2] = {0}; + bool use_original = false; + + if (ss->cache->original) { + unode = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS); + use_original = (unode->co || unode->bm_entry); + } + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + /* when the mesh is edited we can't rely on original coords + * (original mesh may not even have verts in brush radius) */ + if (use_original && data->has_bm_orco) { + float(*orco_coords)[3]; + int(*orco_tris)[3]; + int orco_tris_num; + int i; + + BKE_pbvh_node_get_bm_orco_data(data->nodes[n], &orco_tris, &orco_tris_num, &orco_coords); + + for (i = 0; i < orco_tris_num; i++) { + const float *co_tri[3] = { + orco_coords[orco_tris[i][0]], + orco_coords[orco_tris[i][1]], + orco_coords[orco_tris[i][2]], + }; + float co[3]; + + closest_on_tri_to_point_v3(co, test.location, UNPACK3(co_tri)); + + if (sculpt_brush_test_sq_fn(&test, co)) { + float no[3]; + int flip_index; + + normal_tri_v3(no, UNPACK3(co_tri)); + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + if (area_cos) + add_v3_v3(private_co[flip_index], co); + if (area_nos) + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + } + else { + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + const float *co; + const short *no_s; /* bm_vert only */ + + if (use_original) { + if (unode->bm_entry) { + BM_log_original_vert_data(ss->bm_log, vd.bm_vert, &co, &no_s); + } + else { + co = unode->co[vd.i]; + no_s = unode->no[vd.i]; + } + } + else { + co = vd.co; + } + + if (sculpt_brush_test_sq_fn(&test, co)) { + float no_buf[3]; + const float *no; + int flip_index; + + if (use_original) { + normal_short_to_float_v3(no_buf, no_s); + no = no_buf; + } + else { + if (vd.no) { + normal_short_to_float_v3(no_buf, vd.no); + no = no_buf; + } + else { + no = vd.fno; + } + } + + flip_index = (dot_v3v3(ss->cache->view_normal, no) <= 0.0f); + if (area_cos) + add_v3_v3(private_co[flip_index], co); + if (area_nos) + add_v3_v3(private_no[flip_index], no); + private_count[flip_index] += 1; + } + } + BKE_pbvh_vertex_iter_end; + } + + BLI_mutex_lock(&data->mutex); + + /* for flatten center */ + if (area_cos) { + add_v3_v3(area_cos[0], private_co[0]); + add_v3_v3(area_cos[1], private_co[1]); + } + + /* for area normal */ + if (area_nos) { + add_v3_v3(area_nos[0], private_no[0]); + add_v3_v3(area_nos[1], private_no[1]); + } + + /* weights */ + data->count[0] += private_count[0]; + data->count[1] += private_count[1]; + + BLI_mutex_unlock(&data->mutex); } static void calc_area_center( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_co[3]) -{ - const Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); - int n; - - /* 0=towards view, 1=flipped */ - float area_cos[2][3] = {{0.0f}}; - - int count[2] = {0}; - - /* Intentionally set 'sd' to NULL since we share logic with vertex paint. */ - SculptThreadedTaskData data = { - .sd = NULL, .ob = ob, .brush = brush, .nodes = nodes, .totnode = totnode, - .has_bm_orco = has_bm_orco, .area_cos = area_cos, .area_nos = NULL, .count = count, - }; - BLI_mutex_init(&data.mutex); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - calc_area_normal_and_center_task_cb, - &settings); - - BLI_mutex_end(&data.mutex); - - /* for flatten center */ - for (n = 0; n < ARRAY_SIZE(area_cos); n++) { - if (count[n] != 0) { - mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); - break; - } - } - if (n == 2) { - zero_v3(r_area_co); - } + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_cos[2][3] = {{0.0f}}; + + int count[2] = {0}; + + /* Intentionally set 'sd' to NULL since we share logic with vertex paint. */ + SculptThreadedTaskData data = { + .sd = NULL, + .ob = ob, + .brush = brush, + .nodes = nodes, + .totnode = totnode, + .has_bm_orco = has_bm_orco, + .area_cos = area_cos, + .area_nos = NULL, + .count = count, + }; + BLI_mutex_init(&data.mutex); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + + BLI_mutex_end(&data.mutex); + + /* for flatten center */ + for (n = 0; n < ARRAY_SIZE(area_cos); n++) { + if (count[n] != 0) { + mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); + break; + } + } + if (n == 2) { + zero_v3(r_area_co); + } } static void calc_area_normal( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_no[3]) + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) { - const Brush *brush = BKE_paint_brush(&sd->paint); - bool use_threading = (sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT; - sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, use_threading, r_area_no); + const Brush *brush = BKE_paint_brush(&sd->paint); + bool use_threading = (sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT; + sculpt_pbvh_calc_area_normal(brush, ob, nodes, totnode, use_threading, r_area_no); } /* expose 'calc_area_normal' externally. */ -void sculpt_pbvh_calc_area_normal( - const Brush *brush, Object *ob, - PBVHNode **nodes, int totnode, - bool use_threading, - float r_area_no[3]) -{ - SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); - - /* 0=towards view, 1=flipped */ - float area_nos[2][3] = {{0.0f}}; - - int count[2] = {0}; - - /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ - SculptThreadedTaskData data = { - .sd = NULL, .ob = ob, .brush = brush, .nodes = nodes, .totnode = totnode, - .has_bm_orco = has_bm_orco, .area_cos = NULL, .area_nos = area_nos, .count = count, - }; - BLI_mutex_init(&data.mutex); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = use_threading; - BLI_task_parallel_range( - 0, totnode, - &data, - calc_area_normal_and_center_task_cb, - &settings); - - BLI_mutex_end(&data.mutex); - - /* for area normal */ - for (int i = 0; i < ARRAY_SIZE(area_nos); i++) { - if (normalize_v3_v3(r_area_no, area_nos[i]) != 0.0f) { - break; - } - } +void sculpt_pbvh_calc_area_normal(const Brush *brush, + Object *ob, + PBVHNode **nodes, + int totnode, + bool use_threading, + float r_area_no[3]) +{ + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + + /* 0=towards view, 1=flipped */ + float area_nos[2][3] = {{0.0f}}; + + int count[2] = {0}; + + /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ + SculptThreadedTaskData data = { + .sd = NULL, + .ob = ob, + .brush = brush, + .nodes = nodes, + .totnode = totnode, + .has_bm_orco = has_bm_orco, + .area_cos = NULL, + .area_nos = area_nos, + .count = count, + }; + BLI_mutex_init(&data.mutex); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = use_threading; + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + + BLI_mutex_end(&data.mutex); + + /* for area normal */ + for (int i = 0; i < ARRAY_SIZE(area_nos); i++) { + if (normalize_v3_v3(r_area_no, area_nos[i]) != 0.0f) { + break; + } + } } - /* this calculates flatten center and area normal together, * amortizing the memory bandwidth and loop overhead to calculate both at the same time */ static void calc_area_normal_and_center( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_no[3], float r_area_co[3]) -{ - const Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; - const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); - int n; - - /* 0=towards view, 1=flipped */ - float area_cos[2][3] = {{0.0f}}; - float area_nos[2][3] = {{0.0f}}; - - int count[2] = {0}; - - /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ - SculptThreadedTaskData data = { - .sd = NULL, .ob = ob, .brush = brush, .nodes = nodes, .totnode = totnode, - .has_bm_orco = has_bm_orco, .area_cos = area_cos, .area_nos = area_nos, .count = count, - }; - BLI_mutex_init(&data.mutex); - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - calc_area_normal_and_center_task_cb, - &settings); - - BLI_mutex_end(&data.mutex); - - /* for flatten center */ - for (n = 0; n < ARRAY_SIZE(area_cos); n++) { - if (count[n] != 0) { - mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); - break; - } - } - if (n == 2) { - zero_v3(r_area_co); - } - - /* for area normal */ - for (n = 0; n < ARRAY_SIZE(area_nos); n++) { - if (normalize_v3_v3(r_area_no, area_nos[n]) != 0.0f) { - break; - } - } + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + const bool has_bm_orco = ss->bm && sculpt_stroke_is_dynamic_topology(ss, brush); + int n; + + /* 0=towards view, 1=flipped */ + float area_cos[2][3] = {{0.0f}}; + float area_nos[2][3] = {{0.0f}}; + + int count[2] = {0}; + + /* Intentionally set 'sd' to NULL since this is used for vertex paint too. */ + SculptThreadedTaskData data = { + .sd = NULL, + .ob = ob, + .brush = brush, + .nodes = nodes, + .totnode = totnode, + .has_bm_orco = has_bm_orco, + .area_cos = area_cos, + .area_nos = area_nos, + .count = count, + }; + BLI_mutex_init(&data.mutex); + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, calc_area_normal_and_center_task_cb, &settings); + + BLI_mutex_end(&data.mutex); + + /* for flatten center */ + for (n = 0; n < ARRAY_SIZE(area_cos); n++) { + if (count[n] != 0) { + mul_v3_v3fl(r_area_co, area_cos[n], 1.0f / count[n]); + break; + } + } + if (n == 2) { + zero_v3(r_area_co); + } + + /* for area normal */ + for (n = 0; n < ARRAY_SIZE(area_nos); n++) { + if (normalize_v3_v3(r_area_no, area_nos[n]) != 0.0f) { + break; + } + } } /** \} */ - /* Return modified brush strength. Includes the direction of the brush, positive * values pull vertices, negative values push. Uses tablet pressure and a * special multiplier found experimentally to scale the strength factor. */ -static float brush_strength( - const Sculpt *sd, const StrokeCache *cache, - const float feather, const UnifiedPaintSettings *ups) -{ - const Scene *scene = cache->vc->scene; - const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); - - /* Primary strength input; square it to make lower values more sensitive */ - const float root_alpha = BKE_brush_alpha_get(scene, brush); - float alpha = root_alpha * root_alpha; - float dir = (brush->flag & BRUSH_DIR_IN) ? -1 : 1; - float pressure = BKE_brush_use_alpha_pressure(scene, brush) ? cache->pressure : 1; - float pen_flip = cache->pen_flip ? -1 : 1; - float invert = cache->invert ? -1 : 1; - float overlap = ups->overlap_factor; - /* spacing is integer percentage of radius, divide by 50 to get - * normalized diameter */ - - float flip = dir * invert * pen_flip; - - switch (brush->sculpt_tool) { - case SCULPT_TOOL_CLAY: - case SCULPT_TOOL_CLAY_STRIPS: - case SCULPT_TOOL_DRAW: - case SCULPT_TOOL_LAYER: - return alpha * flip * pressure * overlap * feather; - - case SCULPT_TOOL_MASK: - overlap = (1 + overlap) / 2; - switch ((BrushMaskTool)brush->mask_tool) { - case BRUSH_MASK_DRAW: - return alpha * flip * pressure * overlap * feather; - case BRUSH_MASK_SMOOTH: - return alpha * pressure * feather; - } - BLI_assert(!"Not supposed to happen"); - return 0.0f; - - case SCULPT_TOOL_CREASE: - case SCULPT_TOOL_BLOB: - return alpha * flip * pressure * overlap * feather; - - case SCULPT_TOOL_INFLATE: - if (flip > 0) { - return 0.250f * alpha * flip * pressure * overlap * feather; - } - else { - return 0.125f * alpha * flip * pressure * overlap * feather; - } - - case SCULPT_TOOL_FILL: - case SCULPT_TOOL_SCRAPE: - case SCULPT_TOOL_FLATTEN: - if (flip > 0) { - overlap = (1 + overlap) / 2; - return alpha * flip * pressure * overlap * feather; - } - else { - /* reduce strength for DEEPEN, PEAKS, and CONTRAST */ - return 0.5f * alpha * flip * pressure * overlap * feather; - } - - case SCULPT_TOOL_SMOOTH: - return alpha * pressure * feather; - - case SCULPT_TOOL_PINCH: - if (flip > 0) { - return alpha * flip * pressure * overlap * feather; - } - else { - return 0.25f * alpha * flip * pressure * overlap * feather; - } - - case SCULPT_TOOL_NUDGE: - overlap = (1 + overlap) / 2; - return alpha * pressure * overlap * feather; - - case SCULPT_TOOL_THUMB: - return alpha * pressure * feather; - - case SCULPT_TOOL_SNAKE_HOOK: - return root_alpha * feather; - - case SCULPT_TOOL_GRAB: - return root_alpha * feather; - - case SCULPT_TOOL_ROTATE: - return alpha * pressure * feather; - - default: - return 0; - } +static float brush_strength(const Sculpt *sd, + const StrokeCache *cache, + const float feather, + const UnifiedPaintSettings *ups) +{ + const Scene *scene = cache->vc->scene; + const Brush *brush = BKE_paint_brush((Paint *)&sd->paint); + + /* Primary strength input; square it to make lower values more sensitive */ + const float root_alpha = BKE_brush_alpha_get(scene, brush); + float alpha = root_alpha * root_alpha; + float dir = (brush->flag & BRUSH_DIR_IN) ? -1 : 1; + float pressure = BKE_brush_use_alpha_pressure(scene, brush) ? cache->pressure : 1; + float pen_flip = cache->pen_flip ? -1 : 1; + float invert = cache->invert ? -1 : 1; + float overlap = ups->overlap_factor; + /* spacing is integer percentage of radius, divide by 50 to get + * normalized diameter */ + + float flip = dir * invert * pen_flip; + + switch (brush->sculpt_tool) { + case SCULPT_TOOL_CLAY: + case SCULPT_TOOL_CLAY_STRIPS: + case SCULPT_TOOL_DRAW: + case SCULPT_TOOL_LAYER: + return alpha * flip * pressure * overlap * feather; + + case SCULPT_TOOL_MASK: + overlap = (1 + overlap) / 2; + switch ((BrushMaskTool)brush->mask_tool) { + case BRUSH_MASK_DRAW: + return alpha * flip * pressure * overlap * feather; + case BRUSH_MASK_SMOOTH: + return alpha * pressure * feather; + } + BLI_assert(!"Not supposed to happen"); + return 0.0f; + + case SCULPT_TOOL_CREASE: + case SCULPT_TOOL_BLOB: + return alpha * flip * pressure * overlap * feather; + + case SCULPT_TOOL_INFLATE: + if (flip > 0) { + return 0.250f * alpha * flip * pressure * overlap * feather; + } + else { + return 0.125f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_FILL: + case SCULPT_TOOL_SCRAPE: + case SCULPT_TOOL_FLATTEN: + if (flip > 0) { + overlap = (1 + overlap) / 2; + return alpha * flip * pressure * overlap * feather; + } + else { + /* reduce strength for DEEPEN, PEAKS, and CONTRAST */ + return 0.5f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_SMOOTH: + return alpha * pressure * feather; + + case SCULPT_TOOL_PINCH: + if (flip > 0) { + return alpha * flip * pressure * overlap * feather; + } + else { + return 0.25f * alpha * flip * pressure * overlap * feather; + } + + case SCULPT_TOOL_NUDGE: + overlap = (1 + overlap) / 2; + return alpha * pressure * overlap * feather; + + case SCULPT_TOOL_THUMB: + return alpha * pressure * feather; + + case SCULPT_TOOL_SNAKE_HOOK: + return root_alpha * feather; + + case SCULPT_TOOL_GRAB: + return root_alpha * feather; + + case SCULPT_TOOL_ROTATE: + return alpha * pressure * feather; + + default: + return 0; + } } /* Return a multiplier for brush strength on a particular vertex. */ -float tex_strength(SculptSession *ss, const Brush *br, +float tex_strength(SculptSession *ss, + const Brush *br, const float brush_point[3], const float len, const short vno[3], @@ -1199,299 +1180,295 @@ float tex_strength(SculptSession *ss, const Brush *br, const float mask, const int thread_id) { - StrokeCache *cache = ss->cache; - const Scene *scene = cache->vc->scene; - const MTex *mtex = &br->mtex; - float avg = 1; - float rgba[4]; - float point[3]; + StrokeCache *cache = ss->cache; + const Scene *scene = cache->vc->scene; + const MTex *mtex = &br->mtex; + float avg = 1; + float rgba[4]; + float point[3]; - sub_v3_v3v3(point, brush_point, cache->plane_offset); + sub_v3_v3v3(point, brush_point, cache->plane_offset); - if (!mtex->tex) { - avg = 1; - } - else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { - /* Get strength by feeding the vertex - * location directly into a texture */ - avg = BKE_brush_sample_tex_3d(scene, br, point, rgba, 0, ss->tex_pool); - } - else if (ss->texcache) { - float symm_point[3], point_2d[2]; - float x = 0.0f, y = 0.0f; /* Quite warnings */ + if (!mtex->tex) { + avg = 1; + } + else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { + /* Get strength by feeding the vertex + * location directly into a texture */ + avg = BKE_brush_sample_tex_3d(scene, br, point, rgba, 0, ss->tex_pool); + } + else if (ss->texcache) { + float symm_point[3], point_2d[2]; + float x = 0.0f, y = 0.0f; /* Quite warnings */ - /* if the active area is being applied for symmetry, flip it - * across the symmetry axis and rotate it back to the original - * position in order to project it. This insures that the - * brush texture will be oriented correctly. */ + /* if the active area is being applied for symmetry, flip it + * across the symmetry axis and rotate it back to the original + * position in order to project it. This insures that the + * brush texture will be oriented correctly. */ - flip_v3_v3(symm_point, point, cache->mirror_symmetry_pass); + flip_v3_v3(symm_point, point, cache->mirror_symmetry_pass); - if (cache->radial_symmetry_pass) - mul_m4_v3(cache->symm_rot_mat_inv, symm_point); + if (cache->radial_symmetry_pass) + mul_m4_v3(cache->symm_rot_mat_inv, symm_point); - ED_view3d_project_float_v2_m4(cache->vc->ar, symm_point, point_2d, cache->projection_mat); + ED_view3d_project_float_v2_m4(cache->vc->ar, symm_point, point_2d, cache->projection_mat); - /* still no symmetry supported for other paint modes. - * Sculpt does it DIY */ - if (mtex->brush_map_mode == MTEX_MAP_MODE_AREA) { - /* Similar to fixed mode, but projects from brush angle - * rather than view direction */ + /* still no symmetry supported for other paint modes. + * Sculpt does it DIY */ + if (mtex->brush_map_mode == MTEX_MAP_MODE_AREA) { + /* Similar to fixed mode, but projects from brush angle + * rather than view direction */ - mul_m4_v3(cache->brush_local_mat, symm_point); + mul_m4_v3(cache->brush_local_mat, symm_point); - x = symm_point[0]; - y = symm_point[1]; + x = symm_point[0]; + y = symm_point[1]; - x *= br->mtex.size[0]; - y *= br->mtex.size[1]; + x *= br->mtex.size[0]; + y *= br->mtex.size[1]; - x += br->mtex.ofs[0]; - y += br->mtex.ofs[1]; + x += br->mtex.ofs[0]; + y += br->mtex.ofs[1]; - avg = paint_get_tex_pixel(&br->mtex, x, y, ss->tex_pool, thread_id); + avg = paint_get_tex_pixel(&br->mtex, x, y, ss->tex_pool, thread_id); - avg += br->texture_sample_bias; - } - else { - const float point_3d[3] = {point_2d[0], point_2d[1], 0.0f}; - avg = BKE_brush_sample_tex_3d(scene, br, point_3d, rgba, 0, ss->tex_pool); - } - } + avg += br->texture_sample_bias; + } + else { + const float point_3d[3] = {point_2d[0], point_2d[1], 0.0f}; + avg = BKE_brush_sample_tex_3d(scene, br, point_3d, rgba, 0, ss->tex_pool); + } + } - /* Falloff curve */ - avg *= BKE_brush_curve_strength(br, len, cache->radius); + /* Falloff curve */ + avg *= BKE_brush_curve_strength(br, len, cache->radius); - avg *= frontface(br, cache->view_normal, vno, fno); + avg *= frontface(br, cache->view_normal, vno, fno); - /* Paint mask */ - avg *= 1.0f - mask; + /* Paint mask */ + avg *= 1.0f - mask; - return avg; + return avg; } /* Test AABB against sphere */ bool sculpt_search_sphere_cb(PBVHNode *node, void *data_v) { - SculptSearchSphereData *data = data_v; - float *center = data->ss->cache->location, nearest[3]; - float t[3], bb_min[3], bb_max[3]; - int i; + SculptSearchSphereData *data = data_v; + float *center = data->ss->cache->location, nearest[3]; + float t[3], bb_min[3], bb_max[3]; + int i; - if (data->original) - BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); - else - BKE_pbvh_node_get_BB(node, bb_min, bb_max); + if (data->original) + BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); + else + BKE_pbvh_node_get_BB(node, bb_min, bb_max); - for (i = 0; i < 3; ++i) { - if (bb_min[i] > center[i]) - nearest[i] = bb_min[i]; - else if (bb_max[i] < center[i]) - nearest[i] = bb_max[i]; - else - nearest[i] = center[i]; - } + for (i = 0; i < 3; ++i) { + if (bb_min[i] > center[i]) + nearest[i] = bb_min[i]; + else if (bb_max[i] < center[i]) + nearest[i] = bb_max[i]; + else + nearest[i] = center[i]; + } - sub_v3_v3v3(t, center, nearest); + sub_v3_v3v3(t, center, nearest); - return len_squared_v3(t) < data->radius_squared; + return len_squared_v3(t) < data->radius_squared; } /* 2D projection (distance to line). */ bool sculpt_search_circle_cb(PBVHNode *node, void *data_v) { - SculptSearchCircleData *data = data_v; - float bb_min[3], bb_max[3]; + SculptSearchCircleData *data = data_v; + float bb_min[3], bb_max[3]; - if (data->original) - BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); - else - BKE_pbvh_node_get_BB(node, bb_min, bb_min); + if (data->original) + BKE_pbvh_node_get_original_BB(node, bb_min, bb_max); + else + BKE_pbvh_node_get_BB(node, bb_min, bb_min); - float dummy_co[3], dummy_depth; - const float dist_sq = dist_squared_ray_to_aabb_v3( - data->dist_ray_to_aabb_precalc, bb_min, bb_max, dummy_co, &dummy_depth); + float dummy_co[3], dummy_depth; + const float dist_sq = dist_squared_ray_to_aabb_v3( + data->dist_ray_to_aabb_precalc, bb_min, bb_max, dummy_co, &dummy_depth); - return dist_sq < data->radius_squared || 1; + return dist_sq < data->radius_squared || 1; } /* Handles clipping against a mirror modifier and SCULPT_LOCK axis flags */ static void sculpt_clip(Sculpt *sd, SculptSession *ss, float co[3], const float val[3]) { - int i; - - for (i = 0; i < 3; ++i) { - if (sd->flags & (SCULPT_LOCK_X << i)) - continue; - - if ((ss->cache->flag & (CLIP_X << i)) && (fabsf(co[i]) <= ss->cache->clip_tolerance[i])) - co[i] = 0.0f; - else - co[i] = val[i]; - } -} - -static PBVHNode **sculpt_pbvh_gather_generic( - Object *ob, Sculpt *sd, const Brush *brush, bool use_original, float radius_scale, int *r_totnode) -{ - SculptSession *ss = ob->sculpt; - PBVHNode **nodes = NULL; - - /* Build a list of all nodes that are potentially within the brush's area of influence */ - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { - SculptSearchSphereData data = { - .ss = ss, - .sd = sd, - .radius_squared = SQUARE(ss->cache->radius * radius_scale), - .original = use_original, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); - } - else { - struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; - dist_squared_ray_to_aabb_v3_precalc(&dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); - SculptSearchCircleData data = { - .ss = ss, - .sd = sd, - .radius_squared = SQUARE(ss->cache->radius * radius_scale), - .original = use_original, - .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, - }; - BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); - } - return nodes; + int i; + + for (i = 0; i < 3; ++i) { + if (sd->flags & (SCULPT_LOCK_X << i)) + continue; + + if ((ss->cache->flag & (CLIP_X << i)) && (fabsf(co[i]) <= ss->cache->clip_tolerance[i])) + co[i] = 0.0f; + else + co[i] = val[i]; + } +} + +static PBVHNode **sculpt_pbvh_gather_generic(Object *ob, + Sculpt *sd, + const Brush *brush, + bool use_original, + float radius_scale, + int *r_totnode) +{ + SculptSession *ss = ob->sculpt; + PBVHNode **nodes = NULL; + + /* Build a list of all nodes that are potentially within the brush's area of influence */ + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) { + SculptSearchSphereData data = { + .ss = ss, + .sd = sd, + .radius_squared = SQUARE(ss->cache->radius * radius_scale), + .original = use_original, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_sphere_cb, &data, &nodes, r_totnode); + } + else { + struct DistRayAABB_Precalc dist_ray_to_aabb_precalc; + dist_squared_ray_to_aabb_v3_precalc( + &dist_ray_to_aabb_precalc, ss->cache->location, ss->cache->view_normal); + SculptSearchCircleData data = { + .ss = ss, + .sd = sd, + .radius_squared = SQUARE(ss->cache->radius * radius_scale), + .original = use_original, + .dist_ray_to_aabb_precalc = &dist_ray_to_aabb_precalc, + }; + BKE_pbvh_search_gather(ss->pbvh, sculpt_search_circle_cb, &data, &nodes, r_totnode); + } + return nodes; } /* Calculate primary direction of movement for many brushes */ static void calc_sculpt_normal( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_no[3]) -{ - const Brush *brush = BKE_paint_brush(&sd->paint); - const SculptSession *ss = ob->sculpt; - - switch (brush->sculpt_plane) { - case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(r_area_no, ss->cache->true_view_normal); - break; - - case SCULPT_DISP_DIR_X: - ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); - break; - - case SCULPT_DISP_DIR_Y: - ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); - break; - - case SCULPT_DISP_DIR_Z: - ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); - break; - - case SCULPT_DISP_DIR_AREA: - calc_area_normal(sd, ob, nodes, totnode, r_area_no); - break; - - default: - break; - } -} - -static void update_sculpt_normal(Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode) -{ - const Brush *brush = BKE_paint_brush(&sd->paint); - StrokeCache *cache = ob->sculpt->cache; - - if (cache->mirror_symmetry_pass == 0 && - cache->radial_symmetry_pass == 0 && - (cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) - { - calc_sculpt_normal(sd, ob, nodes, totnode, cache->sculpt_normal); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(cache->sculpt_normal, cache->sculpt_normal, cache->view_normal); - normalize_v3(cache->sculpt_normal); - } - copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal); - } - else { - copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal); - flip_v3(cache->sculpt_normal_symm, cache->mirror_symmetry_pass); - mul_m4_v3(cache->symm_rot_mat, cache->sculpt_normal_symm); - } + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + const SculptSession *ss = ob->sculpt; + + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + copy_v3_v3(r_area_no, ss->cache->true_view_normal); + break; + + case SCULPT_DISP_DIR_X: + ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); + break; + + case SCULPT_DISP_DIR_Y: + ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); + break; + + case SCULPT_DISP_DIR_Z: + ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); + break; + + case SCULPT_DISP_DIR_AREA: + calc_area_normal(sd, ob, nodes, totnode, r_area_no); + break; + + default: + break; + } +} + +static void update_sculpt_normal(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +{ + const Brush *brush = BKE_paint_brush(&sd->paint); + StrokeCache *cache = ob->sculpt->cache; + + if (cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0 && + (cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { + calc_sculpt_normal(sd, ob, nodes, totnode, cache->sculpt_normal); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(cache->sculpt_normal, cache->sculpt_normal, cache->view_normal); + normalize_v3(cache->sculpt_normal); + } + copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal); + } + else { + copy_v3_v3(cache->sculpt_normal_symm, cache->sculpt_normal); + flip_v3(cache->sculpt_normal_symm, cache->mirror_symmetry_pass); + mul_m4_v3(cache->symm_rot_mat, cache->sculpt_normal_symm); + } } static void calc_local_y(ViewContext *vc, const float center[3], float y[3]) { - Object *ob = vc->obact; - float loc[3], mval_f[2] = {0.0f, 1.0f}; - float zfac; + Object *ob = vc->obact; + float loc[3], mval_f[2] = {0.0f, 1.0f}; + float zfac; - mul_v3_m4v3(loc, ob->imat, center); - zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); + mul_v3_m4v3(loc, ob->imat, center); + zfac = ED_view3d_calc_zfac(vc->rv3d, loc, NULL); - ED_view3d_win_to_delta(vc->ar, mval_f, y, zfac); - normalize_v3(y); + ED_view3d_win_to_delta(vc->ar, mval_f, y, zfac); + normalize_v3(y); - add_v3_v3(y, ob->loc); - mul_m4_v3(ob->imat, y); + add_v3_v3(y, ob->loc); + mul_m4_v3(ob->imat, y); } -static void calc_brush_local_mat(const Brush *brush, Object *ob, - float local_mat[4][4]) +static void calc_brush_local_mat(const Brush *brush, Object *ob, float local_mat[4][4]) { - const StrokeCache *cache = ob->sculpt->cache; - float tmat[4][4]; - float mat[4][4]; - float scale[4][4]; - float angle, v[3]; - float up[3]; + const StrokeCache *cache = ob->sculpt->cache; + float tmat[4][4]; + float mat[4][4]; + float scale[4][4]; + float angle, v[3]; + float up[3]; - /* Ensure ob->imat is up to date */ - invert_m4_m4(ob->imat, ob->obmat); + /* Ensure ob->imat is up to date */ + invert_m4_m4(ob->imat, ob->obmat); - /* Initialize last column of matrix */ - mat[0][3] = 0; - mat[1][3] = 0; - mat[2][3] = 0; - mat[3][3] = 1; + /* Initialize last column of matrix */ + mat[0][3] = 0; + mat[1][3] = 0; + mat[2][3] = 0; + mat[3][3] = 1; - /* Get view's up vector in object-space */ - calc_local_y(cache->vc, cache->location, up); + /* Get view's up vector in object-space */ + calc_local_y(cache->vc, cache->location, up); - /* Calculate the X axis of the local matrix */ - cross_v3_v3v3(v, up, cache->sculpt_normal); - /* Apply rotation (user angle, rake, etc.) to X axis */ - angle = brush->mtex.rot - cache->special_rotation; - rotate_v3_v3v3fl(mat[0], v, cache->sculpt_normal, angle); + /* Calculate the X axis of the local matrix */ + cross_v3_v3v3(v, up, cache->sculpt_normal); + /* Apply rotation (user angle, rake, etc.) to X axis */ + angle = brush->mtex.rot - cache->special_rotation; + rotate_v3_v3v3fl(mat[0], v, cache->sculpt_normal, angle); - /* Get other axes */ - cross_v3_v3v3(mat[1], cache->sculpt_normal, mat[0]); - copy_v3_v3(mat[2], cache->sculpt_normal); + /* Get other axes */ + cross_v3_v3v3(mat[1], cache->sculpt_normal, mat[0]); + copy_v3_v3(mat[2], cache->sculpt_normal); - /* Set location */ - copy_v3_v3(mat[3], cache->location); + /* Set location */ + copy_v3_v3(mat[3], cache->location); - /* Scale by brush radius */ - normalize_m4(mat); - scale_m4_fl(scale, cache->radius); - mul_m4_m4m4(tmat, mat, scale); + /* Scale by brush radius */ + normalize_m4(mat); + scale_m4_fl(scale, cache->radius); + mul_m4_m4m4(tmat, mat, scale); - /* Return inverse (for converting from modelspace coords to local - * area coords) */ - invert_m4_m4(local_mat, tmat); + /* Return inverse (for converting from modelspace coords to local + * area coords) */ + invert_m4_m4(local_mat, tmat); } static void update_brush_local_mat(Sculpt *sd, Object *ob) { - StrokeCache *cache = ob->sculpt->cache; + StrokeCache *cache = ob->sculpt->cache; - if (cache->mirror_symmetry_pass == 0 && - cache->radial_symmetry_pass == 0) - { - calc_brush_local_mat(BKE_paint_brush(&sd->paint), ob, - cache->brush_local_mat); - } + if (cache->mirror_symmetry_pass == 0 && cache->radial_symmetry_pass == 0) { + calc_brush_local_mat(BKE_paint_brush(&sd->paint), ob, cache->brush_local_mat); + } } /* For the smooth brush, uses the neighboring vertices around vert to calculate @@ -1499,40 +1476,39 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob) * polygon.) */ static void neighbor_average(SculptSession *ss, float avg[3], unsigned vert) { - const MeshElemMap *vert_map = &ss->pmap[vert]; - const MVert *mvert = ss->mvert; - float (*deform_co)[3] = ss->deform_cos; + const MeshElemMap *vert_map = &ss->pmap[vert]; + const MVert *mvert = ss->mvert; + float(*deform_co)[3] = ss->deform_cos; - /* Don't modify corner vertices */ - if (vert_map->count > 1) { - int i, total = 0; + /* Don't modify corner vertices */ + if (vert_map->count > 1) { + int i, total = 0; - zero_v3(avg); + zero_v3(avg); - for (i = 0; i < vert_map->count; i++) { - const MPoly *p = &ss->mpoly[vert_map->indices[i]]; - unsigned f_adj_v[2]; + for (i = 0; i < vert_map->count; i++) { + const MPoly *p = &ss->mpoly[vert_map->indices[i]]; + unsigned f_adj_v[2]; - if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { - int j; - for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { - add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] : - mvert[f_adj_v[j]].co); + if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { + int j; + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { + if (vert_map->count != 2 || ss->pmap[f_adj_v[j]].count <= 2) { + add_v3_v3(avg, deform_co ? deform_co[f_adj_v[j]] : mvert[f_adj_v[j]].co); - total++; - } - } - } - } + total++; + } + } + } + } - if (total > 0) { - mul_v3_fl(avg, 1.0f / total); - return; - } - } + if (total > 0) { + mul_v3_fl(avg, 1.0f / total); + return; + } + } - copy_v3_v3(avg, deform_co ? deform_co[vert] : mvert[vert].co); + copy_v3_v3(avg, deform_co ? deform_co[vert] : mvert[vert].co); } /* Similar to neighbor_average(), but returns an averaged mask value @@ -1540,2664 +1516,2727 @@ static void neighbor_average(SculptSession *ss, float avg[3], unsigned vert) * corner vertices. */ static float neighbor_average_mask(SculptSession *ss, unsigned vert) { - const float *vmask = ss->vmask; - float avg = 0; - int i, total = 0; + const float *vmask = ss->vmask; + float avg = 0; + int i, total = 0; - for (i = 0; i < ss->pmap[vert].count; i++) { - const MPoly *p = &ss->mpoly[ss->pmap[vert].indices[i]]; - unsigned f_adj_v[2]; + for (i = 0; i < ss->pmap[vert].count; i++) { + const MPoly *p = &ss->mpoly[ss->pmap[vert].indices[i]]; + unsigned f_adj_v[2]; - if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { - int j; - for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { - avg += vmask[f_adj_v[j]]; - total++; - } - } - } + if (poly_get_adj_loops_from_vert(p, ss->mloop, vert, f_adj_v) != -1) { + int j; + for (j = 0; j < ARRAY_SIZE(f_adj_v); j += 1) { + avg += vmask[f_adj_v[j]]; + total++; + } + } + } - if (total > 0) - return avg / (float)total; - else - return vmask[vert]; + if (total > 0) + return avg / (float)total; + else + return vmask[vert]; } /* Same logic as neighbor_average(), but for bmesh rather than mesh */ static void bmesh_neighbor_average(float avg[3], BMVert *v) { - /* logic for 3 or more is identical */ - const int vfcount = BM_vert_face_count_at_most(v, 3); + /* logic for 3 or more is identical */ + const int vfcount = BM_vert_face_count_at_most(v, 3); - /* Don't modify corner vertices */ - if (vfcount > 1) { - BMIter liter; - BMLoop *l; - int i, total = 0; + /* Don't modify corner vertices */ + if (vfcount > 1) { + BMIter liter; + BMLoop *l; + int i, total = 0; - zero_v3(avg); + zero_v3(avg); - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - const BMVert *adj_v[2] = {l->prev->v, l->next->v}; + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (i = 0; i < ARRAY_SIZE(adj_v); i++) { - const BMVert *v_other = adj_v[i]; - if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { - add_v3_v3(avg, v_other->co); - total++; - } - } - } + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { + add_v3_v3(avg, v_other->co); + total++; + } + } + } - if (total > 0) { - mul_v3_fl(avg, 1.0f / total); - return; - } - } + if (total > 0) { + mul_v3_fl(avg, 1.0f / total); + return; + } + } - copy_v3_v3(avg, v->co); + copy_v3_v3(avg, v->co); } /* For bmesh: average only the four most aligned (parallel and perpendicular) edges * relative to a direction. Naturally converges to a quad-like tessellation. */ static void bmesh_four_neighbor_average(float avg[3], float direction[3], BMVert *v) { - /* Logic for 3 or more is identical. */ - const int vfcount = BM_vert_face_count_at_most(v, 3); - - /* Don't modify corner vertices. */ - if (vfcount < 2) { - copy_v3_v3(avg, v->co); - return; - } - - /* Project the direction to the vertex normal and create an additional - * parallel vector. */ - float dir_a[3], dir_b[3]; - cross_v3_v3v3(dir_a, direction, v->no); - cross_v3_v3v3(dir_b, dir_a, v->no); - - /* The four vectors which will be used for smoothing. - * Occasionally less than 4 verts match the requirements in that case - * use 'v' as fallback. */ - BMVert *pos_a = v; - BMVert *neg_a = v; - BMVert *pos_b = v; - BMVert *neg_b = v; - - float pos_score_a = 0.0f; - float neg_score_a = 0.0f; - float pos_score_b = 0.0f; - float neg_score_b = 0.0f; - - BMIter liter; - BMLoop *l; - - BM_ITER_ELEM(l, &liter, v, BM_LOOPS_OF_VERT) { - BMVert *adj_v[2] = { l->prev->v, l->next->v }; - - for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { - BMVert *v_other = adj_v[i]; - - if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { - float vec[3]; - sub_v3_v3v3(vec, v_other->co, v->co); - normalize_v3(vec); - - /* The score is a measure of how orthogonal the edge is. */ - float score = dot_v3v3(vec, dir_a); - - if (score >= pos_score_a) { - pos_a = v_other; - pos_score_a = score; - } - else if (score < neg_score_a) { - neg_a = v_other; - neg_score_a = score; - } - /* The same scoring but for the perpendicular direction. */ - score = dot_v3v3(vec, dir_b); - - if (score >= pos_score_b) { - pos_b = v_other; - pos_score_b = score; - } - else if (score < neg_score_b) { - neg_b = v_other; - neg_score_b = score; - } - } - } - } - - /* Average everything together. */ - zero_v3(avg); - add_v3_v3(avg, pos_a->co); - add_v3_v3(avg, neg_a->co); - add_v3_v3(avg, pos_b->co); - add_v3_v3(avg, neg_b->co); - mul_v3_fl(avg, 0.25f); - - /* Preserve volume. */ - float vec[3]; - sub_v3_v3(avg, v->co); - mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); - sub_v3_v3(avg, vec); - add_v3_v3(avg, v->co); + /* Logic for 3 or more is identical. */ + const int vfcount = BM_vert_face_count_at_most(v, 3); + + /* Don't modify corner vertices. */ + if (vfcount < 2) { + copy_v3_v3(avg, v->co); + return; + } + + /* Project the direction to the vertex normal and create an additional + * parallel vector. */ + float dir_a[3], dir_b[3]; + cross_v3_v3v3(dir_a, direction, v->no); + cross_v3_v3v3(dir_b, dir_a, v->no); + + /* The four vectors which will be used for smoothing. + * Occasionally less than 4 verts match the requirements in that case + * use 'v' as fallback. */ + BMVert *pos_a = v; + BMVert *neg_a = v; + BMVert *pos_b = v; + BMVert *neg_b = v; + + float pos_score_a = 0.0f; + float neg_score_a = 0.0f; + float pos_score_b = 0.0f; + float neg_score_b = 0.0f; + + BMIter liter; + BMLoop *l; + + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + BMVert *adj_v[2] = {l->prev->v, l->next->v}; + + for (int i = 0; i < ARRAY_SIZE(adj_v); i++) { + BMVert *v_other = adj_v[i]; + + if (vfcount != 2 || BM_vert_face_count_at_most(v_other, 2) <= 2) { + float vec[3]; + sub_v3_v3v3(vec, v_other->co, v->co); + normalize_v3(vec); + + /* The score is a measure of how orthogonal the edge is. */ + float score = dot_v3v3(vec, dir_a); + + if (score >= pos_score_a) { + pos_a = v_other; + pos_score_a = score; + } + else if (score < neg_score_a) { + neg_a = v_other; + neg_score_a = score; + } + /* The same scoring but for the perpendicular direction. */ + score = dot_v3v3(vec, dir_b); + + if (score >= pos_score_b) { + pos_b = v_other; + pos_score_b = score; + } + else if (score < neg_score_b) { + neg_b = v_other; + neg_score_b = score; + } + } + } + } + + /* Average everything together. */ + zero_v3(avg); + add_v3_v3(avg, pos_a->co); + add_v3_v3(avg, neg_a->co); + add_v3_v3(avg, pos_b->co); + add_v3_v3(avg, neg_b->co); + mul_v3_fl(avg, 0.25f); + + /* Preserve volume. */ + float vec[3]; + sub_v3_v3(avg, v->co); + mul_v3_v3fl(vec, v->no, dot_v3v3(avg, v->no)); + sub_v3_v3(avg, vec); + add_v3_v3(avg, v->co); } /* Same logic as neighbor_average_mask(), but for bmesh rather than mesh */ static float bmesh_neighbor_average_mask(BMVert *v, const int cd_vert_mask_offset) { - BMIter liter; - BMLoop *l; - float avg = 0; - int i, total = 0; + BMIter liter; + BMLoop *l; + float avg = 0; + int i, total = 0; - BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { - /* skip this vertex */ - const BMVert *adj_v[2] = {l->prev->v, l->next->v}; + BM_ITER_ELEM (l, &liter, v, BM_LOOPS_OF_VERT) { + /* skip this vertex */ + const BMVert *adj_v[2] = {l->prev->v, l->next->v}; - for (i = 0; i < ARRAY_SIZE(adj_v); i++) { - const BMVert *v_other = adj_v[i]; - const float *vmask = BM_ELEM_CD_GET_VOID_P(v_other, cd_vert_mask_offset); - avg += (*vmask); - total++; - } - } + for (i = 0; i < ARRAY_SIZE(adj_v); i++) { + const BMVert *v_other = adj_v[i]; + const float *vmask = BM_ELEM_CD_GET_VOID_P(v_other, cd_vert_mask_offset); + avg += (*vmask); + total++; + } + } - if (total > 0) { - return avg / (float)total; - } - else { - const float *vmask = BM_ELEM_CD_GET_VOID_P(v, cd_vert_mask_offset); - return (*vmask); - } + if (total > 0) { + return avg / (float)total; + } + else { + const float *vmask = BM_ELEM_CD_GET_VOID_P(v, cd_vert_mask_offset); + return (*vmask); + } } /* Note: uses after-struct allocated mem to store actual cache... */ typedef struct SculptDoBrushSmoothGridDataChunk { - size_t tmpgrid_size; + size_t tmpgrid_size; } SculptDoBrushSmoothGridDataChunk; typedef struct { - SculptSession *ss; - const float *ray_start, *ray_normal; - bool hit; - float depth; - bool original; + SculptSession *ss; + const float *ray_start, *ray_normal; + bool hit; + float depth; + bool original; } SculptRaycastData; typedef struct { - const float *ray_start, *ray_normal; - bool hit; - float depth; - float edge_length; + const float *ray_start, *ray_normal; + bool hit; + float depth; + float edge_length; } SculptDetailRaycastData; typedef struct { - SculptSession *ss; - const float *ray_start, *ray_normal; - bool hit; - float depth; - float dist_sq_to_ray; - bool original; + SculptSession *ss; + const float *ray_start, *ray_normal; + bool hit; + float depth; + float dist_sq_to_ray; + bool original; } SculptFindNearestToRayData; -static void do_smooth_brush_mesh_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - const bool smooth_mask = data->smooth_mask; - float bstrength = data->strength; - - PBVHVertexIter vd; - - CLAMP(bstrength, 0.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), tls->thread_id); - if (smooth_mask) { - float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask; - val *= fade * bstrength; - *vd.mask += val; - CLAMP(*vd.mask, 0.0f, 1.0f); - } - else { - float avg[3], val[3]; - - neighbor_average(ss, avg, vd.vert_indices[vd.i]); - sub_v3_v3v3(val, avg, vd.co); - - madd_v3_v3v3fl(val, vd.co, val, fade); - - sculpt_clip(sd, ss, vd.co, val); - } - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_smooth_brush_bmesh_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - const bool smooth_mask = data->smooth_mask; - float bstrength = data->strength; - - PBVHVertexIter vd; - - CLAMP(bstrength, 0.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, smooth_mask ? 0.0f : *vd.mask, tls->thread_id); - if (smooth_mask) { - float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; - val *= fade * bstrength; - *vd.mask += val; - CLAMP(*vd.mask, 0.0f, 1.0f); - } - else { - float avg[3], val[3]; - - bmesh_neighbor_average(avg, vd.bm_vert); - sub_v3_v3v3(val, avg, vd.co); - - madd_v3_v3v3fl(val, vd.co, val, fade); - - sculpt_clip(sd, ss, vd.co, val); - } - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_topology_rake_bmesh_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - - float direction[3]; - copy_v3_v3(direction, ss->cache->grab_delta_symmetry); - - float tmp[3]; - mul_v3_v3fl( - tmp, ss->cache->sculpt_normal_symm, - dot_v3v3(ss->cache->sculpt_normal_symm, direction)); - sub_v3_v3(direction, tmp); - - /* Cancel if there's no grab data. */ - if (is_zero_v3(direction)) { - return; - } - - float bstrength = data->strength; - CLAMP(bstrength, 0.0f, 1.0f); - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, *vd.mask, tls->thread_id) * ss->cache->pressure; - - float avg[3], val[3]; - - bmesh_four_neighbor_average(avg, direction, vd.bm_vert); - - sub_v3_v3v3(val, avg, vd.co); - - madd_v3_v3v3fl(val, vd.co, val, fade); - - sculpt_clip(sd, ss, vd.co, val); - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} - -static void do_smooth_brush_multires_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptDoBrushSmoothGridDataChunk *data_chunk = tls->userdata_chunk; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - const bool smooth_mask = data->smooth_mask; - float bstrength = data->strength; - - CCGElem **griddata, *gddata; - CCGKey key; - - float (*tmpgrid_co)[3] = NULL; - float tmprow_co[2][3]; - float *tmpgrid_mask = NULL; - float tmprow_mask[2]; - - BLI_bitmap * const *grid_hidden; - int *grid_indices, totgrid, gridsize; - int i, x, y; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - CLAMP(bstrength, 0.0f, 1.0f); - - BKE_pbvh_node_get_grids(ss->pbvh, data->nodes[n], &grid_indices, &totgrid, NULL, &gridsize, &griddata); - BKE_pbvh_get_grid_key(ss->pbvh, &key); - - grid_hidden = BKE_pbvh_grid_hidden(ss->pbvh); - - if (smooth_mask) - tmpgrid_mask = (void *)(data_chunk + 1); - else - tmpgrid_co = (void *)(data_chunk + 1); - - for (i = 0; i < totgrid; i++) { - int gi = grid_indices[i]; - const BLI_bitmap *gh = grid_hidden[gi]; - gddata = griddata[gi]; - - if (smooth_mask) - memset(tmpgrid_mask, 0, data_chunk->tmpgrid_size); - else - memset(tmpgrid_co, 0, data_chunk->tmpgrid_size); - - for (y = 0; y < gridsize - 1; y++) { - const int v = y * gridsize; - if (smooth_mask) { - tmprow_mask[0] = (*CCG_elem_offset_mask(&key, gddata, v) + - *CCG_elem_offset_mask(&key, gddata, v + gridsize)); - } - else { - add_v3_v3v3(tmprow_co[0], - CCG_elem_offset_co(&key, gddata, v), - CCG_elem_offset_co(&key, gddata, v + gridsize)); - } - - for (x = 0; x < gridsize - 1; x++) { - const int v1 = x + y * gridsize; - const int v2 = v1 + 1; - const int v3 = v1 + gridsize; - const int v4 = v3 + 1; - - if (smooth_mask) { - float tmp; - - tmprow_mask[(x + 1) % 2] = (*CCG_elem_offset_mask(&key, gddata, v2) + - *CCG_elem_offset_mask(&key, gddata, v4)); - tmp = tmprow_mask[(x + 1) % 2] + tmprow_mask[x % 2]; - - tmpgrid_mask[v1] += tmp; - tmpgrid_mask[v2] += tmp; - tmpgrid_mask[v3] += tmp; - tmpgrid_mask[v4] += tmp; - } - else { - float tmp[3]; - - add_v3_v3v3(tmprow_co[(x + 1) % 2], - CCG_elem_offset_co(&key, gddata, v2), - CCG_elem_offset_co(&key, gddata, v4)); - add_v3_v3v3(tmp, tmprow_co[(x + 1) % 2], tmprow_co[x % 2]); - - add_v3_v3(tmpgrid_co[v1], tmp); - add_v3_v3(tmpgrid_co[v2], tmp); - add_v3_v3(tmpgrid_co[v3], tmp); - add_v3_v3(tmpgrid_co[v4], tmp); - } - } - } - - /* blend with existing coordinates */ - for (y = 0; y < gridsize; y++) { - for (x = 0; x < gridsize; x++) { - float *co; - const float *fno; - float *mask; - const int index = y * gridsize + x; - - if (gh) { - if (BLI_BITMAP_TEST(gh, index)) - continue; - } - - co = CCG_elem_offset_co(&key, gddata, index); - fno = CCG_elem_offset_no(&key, gddata, index); - mask = CCG_elem_offset_mask(&key, gddata, index); - - if (sculpt_brush_test_sq_fn(&test, co)) { - const float strength_mask = (smooth_mask ? 0.0f : *mask); - const float fade = bstrength * tex_strength( - ss, brush, co, sqrtf(test.dist), - NULL, fno, strength_mask, tls->thread_id); - float f = 1.0f / 16.0f; - - if (x == 0 || x == gridsize - 1) - f *= 2.0f; - - if (y == 0 || y == gridsize - 1) - f *= 2.0f; - - if (smooth_mask) { - *mask += ((tmpgrid_mask[index] * f) - *mask) * fade; - } - else { - float *avg = tmpgrid_co[index]; - float val[3]; - - mul_v3_fl(avg, f); - sub_v3_v3v3(val, avg, co); - madd_v3_v3v3fl(val, co, val, fade); - - sculpt_clip(sd, ss, co, val); - } - } - } - } - } -} - -static void smooth( - Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength, const bool smooth_mask) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const int max_iterations = 4; - const float fract = 1.0f / max_iterations; - PBVHType type = BKE_pbvh_type(ss->pbvh); - int iteration, count; - float last; - - CLAMP(bstrength, 0.0f, 1.0f); - - count = (int)(bstrength * max_iterations); - last = max_iterations * (bstrength - count * fract); - - if (type == PBVH_FACES && !ss->pmap) { - BLI_assert(!"sculpt smooth: pmap missing"); - return; - } - - for (iteration = 0; iteration <= count; ++iteration) { - const float strength = (iteration != count) ? 1.0f : last; - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .smooth_mask = smooth_mask, .strength = strength, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - - switch (type) { - case PBVH_GRIDS: - { - int gridsize; - size_t size; - SculptDoBrushSmoothGridDataChunk *data_chunk; - - BKE_pbvh_node_get_grids(ss->pbvh, NULL, NULL, NULL, NULL, &gridsize, NULL); - size = (size_t)gridsize; - size = sizeof(float) * size * size * (smooth_mask ? 1 : 3); - data_chunk = MEM_mallocN(sizeof(*data_chunk) + size, __func__); - data_chunk->tmpgrid_size = size; - size += sizeof(*data_chunk); - - settings.userdata_chunk = data_chunk; - settings.userdata_chunk_size = size; - BLI_task_parallel_range( - 0, totnode, - &data, - do_smooth_brush_multires_task_cb_ex, - &settings); - - MEM_freeN(data_chunk); - break; - } - case PBVH_FACES: - BLI_task_parallel_range( - 0, totnode, - &data, - do_smooth_brush_mesh_task_cb_ex, - &settings); - break; - case PBVH_BMESH: - BLI_task_parallel_range( - 0, totnode, - &data, - do_smooth_brush_bmesh_task_cb_ex, - &settings); - break; - } - - if (ss->multires) - multires_stitch_grids(ob); - } +static void do_smooth_brush_mesh_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const bool smooth_mask = data->smooth_mask; + float bstrength = data->strength; + + PBVHVertexIter vd; + + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + smooth_mask ? 0.0f : (vd.mask ? *vd.mask : 0.0f), + tls->thread_id); + if (smooth_mask) { + float val = neighbor_average_mask(ss, vd.vert_indices[vd.i]) - *vd.mask; + val *= fade * bstrength; + *vd.mask += val; + CLAMP(*vd.mask, 0.0f, 1.0f); + } + else { + float avg[3], val[3]; + + neighbor_average(ss, avg, vd.vert_indices[vd.i]); + sub_v3_v3v3(val, avg, vd.co); + + madd_v3_v3v3fl(val, vd.co, val, fade); + + sculpt_clip(sd, ss, vd.co, val); + } + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_smooth_brush_bmesh_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const bool smooth_mask = data->smooth_mask; + float bstrength = data->strength; + + PBVHVertexIter vd; + + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + smooth_mask ? 0.0f : *vd.mask, + tls->thread_id); + if (smooth_mask) { + float val = bmesh_neighbor_average_mask(vd.bm_vert, vd.cd_vert_mask_offset) - *vd.mask; + val *= fade * bstrength; + *vd.mask += val; + CLAMP(*vd.mask, 0.0f, 1.0f); + } + else { + float avg[3], val[3]; + + bmesh_neighbor_average(avg, vd.bm_vert); + sub_v3_v3v3(val, avg, vd.co); + + madd_v3_v3v3fl(val, vd.co, val, fade); + + sculpt_clip(sd, ss, vd.co, val); + } + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + + float direction[3]; + copy_v3_v3(direction, ss->cache->grab_delta_symmetry); + + float tmp[3]; + mul_v3_v3fl( + tmp, ss->cache->sculpt_normal_symm, dot_v3v3(ss->cache->sculpt_normal_symm, direction)); + sub_v3_v3(direction, tmp); + + /* Cancel if there's no grab data. */ + if (is_zero_v3(direction)) { + return; + } + + float bstrength = data->strength; + CLAMP(bstrength, 0.0f, 1.0f); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = + bstrength * + tex_strength( + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, tls->thread_id) * + ss->cache->pressure; + + float avg[3], val[3]; + + bmesh_four_neighbor_average(avg, direction, vd.bm_vert); + + sub_v3_v3v3(val, avg, vd.co); + + madd_v3_v3v3fl(val, vd.co, val, fade); + + sculpt_clip(sd, ss, vd.co, val); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; +} + +static void do_smooth_brush_multires_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptDoBrushSmoothGridDataChunk *data_chunk = tls->userdata_chunk; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const bool smooth_mask = data->smooth_mask; + float bstrength = data->strength; + + CCGElem **griddata, *gddata; + CCGKey key; + + float(*tmpgrid_co)[3] = NULL; + float tmprow_co[2][3]; + float *tmpgrid_mask = NULL; + float tmprow_mask[2]; + + BLI_bitmap *const *grid_hidden; + int *grid_indices, totgrid, gridsize; + int i, x, y; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + CLAMP(bstrength, 0.0f, 1.0f); + + BKE_pbvh_node_get_grids( + ss->pbvh, data->nodes[n], &grid_indices, &totgrid, NULL, &gridsize, &griddata); + BKE_pbvh_get_grid_key(ss->pbvh, &key); + + grid_hidden = BKE_pbvh_grid_hidden(ss->pbvh); + + if (smooth_mask) + tmpgrid_mask = (void *)(data_chunk + 1); + else + tmpgrid_co = (void *)(data_chunk + 1); + + for (i = 0; i < totgrid; i++) { + int gi = grid_indices[i]; + const BLI_bitmap *gh = grid_hidden[gi]; + gddata = griddata[gi]; + + if (smooth_mask) + memset(tmpgrid_mask, 0, data_chunk->tmpgrid_size); + else + memset(tmpgrid_co, 0, data_chunk->tmpgrid_size); + + for (y = 0; y < gridsize - 1; y++) { + const int v = y * gridsize; + if (smooth_mask) { + tmprow_mask[0] = (*CCG_elem_offset_mask(&key, gddata, v) + + *CCG_elem_offset_mask(&key, gddata, v + gridsize)); + } + else { + add_v3_v3v3(tmprow_co[0], + CCG_elem_offset_co(&key, gddata, v), + CCG_elem_offset_co(&key, gddata, v + gridsize)); + } + + for (x = 0; x < gridsize - 1; x++) { + const int v1 = x + y * gridsize; + const int v2 = v1 + 1; + const int v3 = v1 + gridsize; + const int v4 = v3 + 1; + + if (smooth_mask) { + float tmp; + + tmprow_mask[(x + 1) % 2] = (*CCG_elem_offset_mask(&key, gddata, v2) + + *CCG_elem_offset_mask(&key, gddata, v4)); + tmp = tmprow_mask[(x + 1) % 2] + tmprow_mask[x % 2]; + + tmpgrid_mask[v1] += tmp; + tmpgrid_mask[v2] += tmp; + tmpgrid_mask[v3] += tmp; + tmpgrid_mask[v4] += tmp; + } + else { + float tmp[3]; + + add_v3_v3v3(tmprow_co[(x + 1) % 2], + CCG_elem_offset_co(&key, gddata, v2), + CCG_elem_offset_co(&key, gddata, v4)); + add_v3_v3v3(tmp, tmprow_co[(x + 1) % 2], tmprow_co[x % 2]); + + add_v3_v3(tmpgrid_co[v1], tmp); + add_v3_v3(tmpgrid_co[v2], tmp); + add_v3_v3(tmpgrid_co[v3], tmp); + add_v3_v3(tmpgrid_co[v4], tmp); + } + } + } + + /* blend with existing coordinates */ + for (y = 0; y < gridsize; y++) { + for (x = 0; x < gridsize; x++) { + float *co; + const float *fno; + float *mask; + const int index = y * gridsize + x; + + if (gh) { + if (BLI_BITMAP_TEST(gh, index)) + continue; + } + + co = CCG_elem_offset_co(&key, gddata, index); + fno = CCG_elem_offset_no(&key, gddata, index); + mask = CCG_elem_offset_mask(&key, gddata, index); + + if (sculpt_brush_test_sq_fn(&test, co)) { + const float strength_mask = (smooth_mask ? 0.0f : *mask); + const float fade = + bstrength * + tex_strength( + ss, brush, co, sqrtf(test.dist), NULL, fno, strength_mask, tls->thread_id); + float f = 1.0f / 16.0f; + + if (x == 0 || x == gridsize - 1) + f *= 2.0f; + + if (y == 0 || y == gridsize - 1) + f *= 2.0f; + + if (smooth_mask) { + *mask += ((tmpgrid_mask[index] * f) - *mask) * fade; + } + else { + float *avg = tmpgrid_co[index]; + float val[3]; + + mul_v3_fl(avg, f); + sub_v3_v3v3(val, avg, co); + madd_v3_v3v3fl(val, co, val, fade); + + sculpt_clip(sd, ss, co, val); + } + } + } + } + } +} + +static void smooth(Sculpt *sd, + Object *ob, + PBVHNode **nodes, + const int totnode, + float bstrength, + const bool smooth_mask) +{ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const int max_iterations = 4; + const float fract = 1.0f / max_iterations; + PBVHType type = BKE_pbvh_type(ss->pbvh); + int iteration, count; + float last; + + CLAMP(bstrength, 0.0f, 1.0f); + + count = (int)(bstrength * max_iterations); + last = max_iterations * (bstrength - count * fract); + + if (type == PBVH_FACES && !ss->pmap) { + BLI_assert(!"sculpt smooth: pmap missing"); + return; + } + + for (iteration = 0; iteration <= count; ++iteration) { + const float strength = (iteration != count) ? 1.0f : last; + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .smooth_mask = smooth_mask, + .strength = strength, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + + switch (type) { + case PBVH_GRIDS: { + int gridsize; + size_t size; + SculptDoBrushSmoothGridDataChunk *data_chunk; + + BKE_pbvh_node_get_grids(ss->pbvh, NULL, NULL, NULL, NULL, &gridsize, NULL); + size = (size_t)gridsize; + size = sizeof(float) * size * size * (smooth_mask ? 1 : 3); + data_chunk = MEM_mallocN(sizeof(*data_chunk) + size, __func__); + data_chunk->tmpgrid_size = size; + size += sizeof(*data_chunk); + + settings.userdata_chunk = data_chunk; + settings.userdata_chunk_size = size; + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_multires_task_cb_ex, &settings); + + MEM_freeN(data_chunk); + break; + } + case PBVH_FACES: + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_mesh_task_cb_ex, &settings); + break; + case PBVH_BMESH: + BLI_task_parallel_range(0, totnode, &data, do_smooth_brush_bmesh_task_cb_ex, &settings); + break; + } + + if (ss->multires) + multires_stitch_grids(ob); + } } static void bmesh_topology_rake( - Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) + Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength) { - Brush *brush = BKE_paint_brush(&sd->paint); - CLAMP(bstrength, 0.0f, 1.0f); + Brush *brush = BKE_paint_brush(&sd->paint); + CLAMP(bstrength, 0.0f, 1.0f); - /* Interactions increase both strength and quality. */ - const int iterations = 3; + /* Interactions increase both strength and quality. */ + const int iterations = 3; - int iteration; - const int count = iterations * bstrength + 1; - const float factor = iterations * bstrength / count; + int iteration; + const int count = iterations * bstrength + 1; + const float factor = iterations * bstrength / count; - for (iteration = 0; iteration <= count; ++iteration) { + for (iteration = 0; iteration <= count; ++iteration) { - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .strength = factor, - }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .strength = factor, + }; + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_topology_rake_bmesh_task_cb_ex, - &settings); - } + BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings); + } } static void do_smooth_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); + SculptSession *ss = ob->sculpt; + smooth(sd, ob, nodes, totnode, ss->cache->bstrength, false); } -static void do_mask_brush_draw_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float bstrength = ss->cache->bstrength; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float bstrength = ss->cache->bstrength; - PBVHVertexIter vd; + PBVHVertexIter vd; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, 0.0f, tls->thread_id); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = tex_strength( + ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, tls->thread_id); - (*vd.mask) += fade * bstrength; - CLAMP(*vd.mask, 0, 1); + (*vd.mask) += fade * bstrength; + CLAMP(*vd.mask, 0, 1); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - BKE_pbvh_vertex_iter_end; - } + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + BKE_pbvh_vertex_iter_end; + } } static void do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - Brush *brush = BKE_paint_brush(&sd->paint); + Brush *brush = BKE_paint_brush(&sd->paint); - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_mask_brush_draw_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings); } static void do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - switch ((BrushMaskTool)brush->mask_tool) { - case BRUSH_MASK_DRAW: - do_mask_brush_draw(sd, ob, nodes, totnode); - break; - case BRUSH_MASK_SMOOTH: - smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true); - break; - } + switch ((BrushMaskTool)brush->mask_tool) { + case BRUSH_MASK_DRAW: + do_mask_brush_draw(sd, ob, nodes, totnode); + break; + case BRUSH_MASK_SMOOTH: + smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true); + break; + } } -static void do_draw_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_draw_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *offset = data->offset; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *offset = data->offset; - PBVHVertexIter vd; - float (*proxy)[3]; + PBVHVertexIter vd; + float(*proxy)[3]; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* offset vertex */ - const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* offset vertex */ + const float fade = tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], offset, fade); + mul_v3_v3fl(proxy[vd.i], offset, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_draw_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - const float bstrength = ss->cache->bstrength; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + const float bstrength = ss->cache->bstrength; - /* offset with as much as possible factored in already */ - mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); - mul_v3_v3(offset, ss->cache->scale); - mul_v3_fl(offset, bstrength); + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); - /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise - * initialize before threads so they can do curve mapping */ - curvemapping_initialize(brush->curve); + /* XXX - this shouldn't be necessary, but sculpting crashes in blender2.8 otherwise + * initialize before threads so they can do curve mapping */ + curvemapping_initialize(brush->curve); - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .offset = offset, - }; + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_draw_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings); } /** * Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB' */ -static void do_crease_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - SculptProjectVector *spvc = data->spvc; - const float flippedbstrength = data->flippedbstrength; - const float *offset = data->offset; - - PBVHVertexIter vd; - float (*proxy)[3]; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - /* offset vertex */ - const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - float val1[3]; - float val2[3]; - - /* first we pinch */ - sub_v3_v3v3(val1, test.location, vd.co); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(val1, val1, ss->cache->view_normal); - } - - mul_v3_fl(val1, fade * flippedbstrength); - - sculpt_project_v3(spvc, val1, val1); - - /* then we draw */ - mul_v3_v3fl(val2, offset, fade); - - add_v3_v3v3(proxy[vd.i], val1, val2); - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; +static void do_crease_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + SculptProjectVector *spvc = data->spvc; + const float flippedbstrength = data->flippedbstrength; + const float *offset = data->offset; + + PBVHVertexIter vd; + float(*proxy)[3]; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + /* offset vertex */ + const float fade = tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + float val1[3]; + float val2[3]; + + /* first we pinch */ + sub_v3_v3v3(val1, test.location, vd.co); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(val1, val1, ss->cache->view_normal); + } + + mul_v3_fl(val1, fade * flippedbstrength); + + sculpt_project_v3(spvc, val1, val1); + + /* then we draw */ + mul_v3_v3fl(val2, offset, fade); + + add_v3_v3v3(proxy[vd.i], val1, val2); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_crease_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - const Scene *scene = ss->cache->vc->scene; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - float bstrength = ss->cache->bstrength; - float flippedbstrength, crease_correction; - float brush_alpha; - - SculptProjectVector spvc; - - /* offset with as much as possible factored in already */ - mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); - mul_v3_v3(offset, ss->cache->scale); - mul_v3_fl(offset, bstrength); - - /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */ - crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor; - brush_alpha = BKE_brush_alpha_get(scene, brush); - if (brush_alpha > 0.0f) - crease_correction /= brush_alpha * brush_alpha; - - /* we always want crease to pinch or blob to relax even when draw is negative */ - flippedbstrength = (bstrength < 0) ? -crease_correction * bstrength : crease_correction * bstrength; - - if (brush->sculpt_tool == SCULPT_TOOL_BLOB) flippedbstrength *= -1.0f; - - /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single point. - * Without this we get a 'flat' surface surrounding the pinch */ - sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); - - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .spvc = &spvc, .offset = offset, .flippedbstrength = flippedbstrength, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_crease_brush_task_cb_ex, - &settings); -} - -static void do_pinch_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - float val[3]; - - sub_v3_v3v3(val, test.location, vd.co); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(val, val, ss->cache->view_normal); - } - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + SculptSession *ss = ob->sculpt; + const Scene *scene = ss->cache->vc->scene; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; + float bstrength = ss->cache->bstrength; + float flippedbstrength, crease_correction; + float brush_alpha; + + SculptProjectVector spvc; + + /* offset with as much as possible factored in already */ + mul_v3_v3fl(offset, ss->cache->sculpt_normal_symm, ss->cache->radius); + mul_v3_v3(offset, ss->cache->scale); + mul_v3_fl(offset, bstrength); + + /* we divide out the squared alpha and multiply by the squared crease to give us the pinch strength */ + crease_correction = brush->crease_pinch_factor * brush->crease_pinch_factor; + brush_alpha = BKE_brush_alpha_get(scene, brush); + if (brush_alpha > 0.0f) + crease_correction /= brush_alpha * brush_alpha; + + /* we always want crease to pinch or blob to relax even when draw is negative */ + flippedbstrength = (bstrength < 0) ? -crease_correction * bstrength : + crease_correction * bstrength; + + if (brush->sculpt_tool == SCULPT_TOOL_BLOB) + flippedbstrength *= -1.0f; + + /* Use surface normal for 'spvc', so the vertices are pinched towards a line instead of a single point. + * Without this we get a 'flat' surface surrounding the pinch */ + sculpt_project_v3_cache_init(&spvc, ss->cache->sculpt_normal_symm); + + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .spvc = &spvc, + .offset = offset, + .flippedbstrength = flippedbstrength, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_crease_brush_task_cb_ex, &settings); +} + +static void do_pinch_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + float val[3]; + + sub_v3_v3v3(val, test.location, vd.co); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(val, val, ss->cache->view_normal); + } + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - Brush *brush = BKE_paint_brush(&sd->paint); + Brush *brush = BKE_paint_brush(&sd->paint); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_pinch_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_pinch_brush_task_cb_ex, &settings); } -static void do_grab_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_grab_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *grab_delta = data->grab_delta; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *grab_delta = data->grab_delta; - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); - if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - const float fade = bstrength * tex_strength( - ss, brush, orig_data.co, sqrtf(test.dist), - orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + mul_v3_v3fl(proxy[vd.i], grab_delta, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_grab_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float grab_delta[3]; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - if (ss->cache->normal_weight > 0.0f) { - sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); - } + if (ss->cache->normal_weight > 0.0f) { + sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); + } - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .grab_delta = grab_delta, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .grab_delta = grab_delta, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_grab_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings); } -static void do_nudge_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_nudge_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *cono = data->cono; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *cono = data->cono; - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], cono, fade); + mul_v3_v3fl(proxy[vd.i], cono, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_nudge_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 tmp[3], cono[3]; - - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - - cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); - cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .cono = cono, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_nudge_brush_task_cb_ex, - &settings); -} - -static void do_snake_hook_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - SculptProjectVector *spvc = data->spvc; - const float *grab_delta = data->grab_delta; - - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; - const bool do_rake_rotation = ss->cache->is_rake_rotation_valid; - const bool do_pinch = (brush->crease_pinch_factor != 0.5f); - const float pinch = do_pinch ? - (2.0f * (0.5f - brush->crease_pinch_factor) * (len_v3(grab_delta) / ss->cache->radius)) : 0.0f; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - - mul_v3_v3fl(proxy[vd.i], grab_delta, fade); - - /* negative pinch will inflate, helps maintain volume */ - if (do_pinch) { - float delta_pinch_init[3], delta_pinch[3]; - - sub_v3_v3v3(delta_pinch, vd.co, test.location); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(delta_pinch, delta_pinch, ss->cache->true_view_normal); - } - - /* important to calculate based on the grabbed location - * (intentionally ignore fade here). */ - add_v3_v3(delta_pinch, grab_delta); - - sculpt_project_v3(spvc, delta_pinch, delta_pinch); - - copy_v3_v3(delta_pinch_init, delta_pinch); - - float pinch_fade = pinch * fade; - /* when reducing, scale reduction back by how close to the center we are, - * so we don't pinch into nothingness */ - if (pinch > 0.0f) { - /* square to have even less impact for close vertices */ - pinch_fade *= pow2f(min_ff(1.0f, len_v3(delta_pinch) / ss->cache->radius)); - } - mul_v3_fl(delta_pinch, 1.0f + pinch_fade); - sub_v3_v3v3(delta_pinch, delta_pinch_init, delta_pinch); - add_v3_v3(proxy[vd.i], delta_pinch); - } - - if (do_rake_rotation) { - float delta_rotate[3]; - sculpt_rake_rotate(ss, test.location, vd.co, fade, delta_rotate); - add_v3_v3(proxy[vd.i], delta_rotate); - } - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + float tmp[3], cono[3]; + + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + + cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); + cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .cono = cono, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings); +} + +static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + SculptProjectVector *spvc = data->spvc; + const float *grab_delta = data->grab_delta; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + const bool do_rake_rotation = ss->cache->is_rake_rotation_valid; + const bool do_pinch = (brush->crease_pinch_factor != 0.5f); + const float pinch = do_pinch ? (2.0f * (0.5f - brush->crease_pinch_factor) * + (len_v3(grab_delta) / ss->cache->radius)) : + 0.0f; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + + mul_v3_v3fl(proxy[vd.i], grab_delta, fade); + + /* negative pinch will inflate, helps maintain volume */ + if (do_pinch) { + float delta_pinch_init[3], delta_pinch[3]; + + sub_v3_v3v3(delta_pinch, vd.co, test.location); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(delta_pinch, delta_pinch, ss->cache->true_view_normal); + } + + /* important to calculate based on the grabbed location + * (intentionally ignore fade here). */ + add_v3_v3(delta_pinch, grab_delta); + + sculpt_project_v3(spvc, delta_pinch, delta_pinch); + + copy_v3_v3(delta_pinch_init, delta_pinch); + + float pinch_fade = pinch * fade; + /* when reducing, scale reduction back by how close to the center we are, + * so we don't pinch into nothingness */ + if (pinch > 0.0f) { + /* square to have even less impact for close vertices */ + pinch_fade *= pow2f(min_ff(1.0f, len_v3(delta_pinch) / ss->cache->radius)); + } + mul_v3_fl(delta_pinch, 1.0f + pinch_fade); + sub_v3_v3v3(delta_pinch, delta_pinch_init, delta_pinch); + add_v3_v3(proxy[vd.i], delta_pinch); + } + + if (do_rake_rotation) { + float delta_rotate[3]; + sculpt_rake_rotate(ss, test.location, vd.co, fade, delta_rotate); + add_v3_v3(proxy[vd.i], delta_rotate); + } + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_snake_hook_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - const float bstrength = ss->cache->bstrength; - float grab_delta[3]; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + const float bstrength = ss->cache->bstrength; + float grab_delta[3]; - SculptProjectVector spvc; + SculptProjectVector spvc; - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - if (bstrength < 0) - negate_v3(grab_delta); + if (bstrength < 0) + negate_v3(grab_delta); - if (ss->cache->normal_weight > 0.0f) { - sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); - } + if (ss->cache->normal_weight > 0.0f) { + sculpt_project_v3_normal_align(ss, ss->cache->normal_weight, grab_delta); + } - /* optionally pinch while painting */ - if (brush->crease_pinch_factor != 0.5f) { - sculpt_project_v3_cache_init(&spvc, grab_delta); - } + /* optionally pinch while painting */ + if (brush->crease_pinch_factor != 0.5f) { + sculpt_project_v3_cache_init(&spvc, grab_delta); + } - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .spvc = &spvc, .grab_delta = grab_delta, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .spvc = &spvc, + .grab_delta = grab_delta, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_snake_hook_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_snake_hook_brush_task_cb_ex, &settings); } -static void do_thumb_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_thumb_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *cono = data->cono; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *cono = data->cono; - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); - if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - const float fade = bstrength * tex_strength( - ss, brush, orig_data.co, sqrtf(test.dist), - orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], cono, fade); + mul_v3_v3fl(proxy[vd.i], cono, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_thumb_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 tmp[3], cono[3]; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float grab_delta[3]; + float tmp[3], cono[3]; - copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); + copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry); - cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); - cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); + cross_v3_v3v3(tmp, ss->cache->sculpt_normal_symm, grab_delta); + cross_v3_v3v3(cono, tmp, ss->cache->sculpt_normal_symm); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .cono = cono, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .cono = cono, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_thumb_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_thumb_brush_task_cb_ex, &settings); } -static void do_rotate_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_rotate_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float angle = data->angle; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float angle = data->angle; - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); - if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - float vec[3], rot[3][3]; - const float fade = bstrength * tex_strength( - ss, brush, orig_data.co, sqrtf(test.dist), - orig_data.no, NULL, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + float vec[3], rot[3][3]; + const float fade = bstrength * tex_strength(ss, + brush, + orig_data.co, + sqrtf(test.dist), + orig_data.no, + NULL, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - sub_v3_v3v3(vec, orig_data.co, ss->cache->location); - axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); - mul_v3_m3v3(proxy[vd.i], rot, vec); - add_v3_v3(proxy[vd.i], ss->cache->location); - sub_v3_v3(proxy[vd.i], orig_data.co); + sub_v3_v3v3(vec, orig_data.co, ss->cache->location); + axis_angle_normalized_to_mat3(rot, ss->cache->sculpt_normal_symm, angle * fade); + mul_v3_m3v3(proxy[vd.i], rot, vec); + add_v3_v3(proxy[vd.i], ss->cache->location); + sub_v3_v3(proxy[vd.i], orig_data.co); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_rotate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - static const int flip[8] = { 1, -1, -1, 1, -1, 1, 1, -1 }; - const float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .angle = angle, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_rotate_brush_task_cb_ex, - &settings); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + static const int flip[8] = {1, -1, -1, 1, -1, 1, 1, -1}; + const float angle = ss->cache->vertex_rotation * flip[ss->cache->mirror_symmetry_pass]; + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .angle = angle, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_rotate_brush_task_cb_ex, &settings); +} + +static void do_layer_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + const Brush *brush = data->brush; + const float *offset = data->offset; + + PBVHVertexIter vd; + SculptOrigVertData orig_data; + float *layer_disp; + const float bstrength = ss->cache->bstrength; + const float lim = (bstrength < 0) ? -data->brush->height : data->brush->height; + /* XXX: layer brush needs conversion to proxy but its more complicated */ + /* proxy = BKE_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; */ + + sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + + /* Why does this have to be thread-protected? */ + BLI_mutex_lock(&data->mutex); + layer_disp = BKE_pbvh_node_layer_disp_get(ss->pbvh, data->nodes[n]); + BLI_mutex_unlock(&data->mutex); + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_orig_vert_data_update(&orig_data, &vd); + + if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + float *disp = &layer_disp[vd.i]; + float val[3]; + + *disp += fade; + + /* Don't let the displacement go past the limit */ + if ((lim < 0.0f && *disp < lim) || (lim >= 0.0f && *disp > lim)) + *disp = lim; + + mul_v3_v3fl(val, offset, *disp); + + if (!ss->multires && !ss->bm && ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { + int index = vd.vert_indices[vd.i]; + + /* persistent base */ + add_v3_v3(val, ss->layer_co[index]); + } + else { + add_v3_v3(val, orig_data.co); + } + + sculpt_clip(sd, ss, vd.co, val); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } -static void do_layer_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - const Brush *brush = data->brush; - const float *offset = data->offset; - - PBVHVertexIter vd; - SculptOrigVertData orig_data; - float *layer_disp; - const float bstrength = ss->cache->bstrength; - const float lim = (bstrength < 0) ? -data->brush->height : data->brush->height; - /* XXX: layer brush needs conversion to proxy but its more complicated */ - /* proxy = BKE_pbvh_node_add_proxy(ss->pbvh, nodes[n])->co; */ + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + float offset[3]; - sculpt_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]); + mul_v3_v3v3(offset, ss->cache->scale, ss->cache->sculpt_normal_symm); - /* Why does this have to be thread-protected? */ - BLI_mutex_lock(&data->mutex); - layer_disp = BKE_pbvh_node_layer_disp_get(ss->pbvh, data->nodes[n]); - BLI_mutex_unlock(&data->mutex); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; + BLI_mutex_init(&data.mutex); - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_orig_vert_data_update(&orig_data, &vd); + BLI_mutex_end(&data.mutex); +} - if (sculpt_brush_test_sq_fn(&test, orig_data.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - float *disp = &layer_disp[vd.i]; - float val[3]; +static void do_inflate_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; - *disp += fade; + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - /* Don't let the displacement go past the limit */ - if ((lim < 0.0f && *disp < lim) || (lim >= 0.0f && *disp > lim)) - *disp = lim; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - mul_v3_v3fl(val, offset, *disp); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - if (!ss->multires && !ss->bm && ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { - int index = vd.vert_indices[vd.i]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + float val[3]; - /* persistent base */ - add_v3_v3(val, ss->layer_co[index]); - } - else { - add_v3_v3(val, orig_data.co); - } + if (vd.fno) + copy_v3_v3(val, vd.fno); + else + normal_short_to_float_v3(val, vd.no); - sculpt_clip(sd, ss, vd.co, val); + mul_v3_fl(val, fade * ss->cache->radius); + mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } -static void do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) +static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]; - - mul_v3_v3v3(offset, ss->cache->scale, ss->cache->sculpt_normal_symm); - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .offset = offset, - }; - BLI_mutex_init(&data.mutex); + Brush *brush = BKE_paint_brush(&sd->paint); - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_layer_brush_task_cb_ex, - &settings); + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - BLI_mutex_end(&data.mutex); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings); } -static void do_inflate_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void calc_sculpt_plane( + Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + if (ss->cache->mirror_symmetry_pass == 0 && ss->cache->radial_symmetry_pass == 0 && + ss->cache->tile_pass == 0 && + (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) { + switch (brush->sculpt_plane) { + case SCULPT_DISP_DIR_VIEW: + copy_v3_v3(r_area_no, ss->cache->true_view_normal); + break; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + case SCULPT_DISP_DIR_X: + ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); + break; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + case SCULPT_DISP_DIR_Y: + ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); + break; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - float val[3]; + case SCULPT_DISP_DIR_Z: + ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); + break; - if (vd.fno) - copy_v3_v3(val, vd.fno); - else - normal_short_to_float_v3(val, vd.no); + case SCULPT_DISP_DIR_AREA: + calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal); + normalize_v3(r_area_no); + } + break; - mul_v3_fl(val, fade * ss->cache->radius); - mul_v3_v3v3(proxy[vd.i], val, ss->cache->scale); + default: + break; + } - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; -} + /* for flatten center */ + /* flatten center has not been calculated yet if we are not using the area normal */ + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) + calc_area_center(sd, ob, nodes, totnode, r_area_co); -static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) -{ - Brush *brush = BKE_paint_brush(&sd->paint); + /* for area normal */ + copy_v3_v3(ss->cache->sculpt_normal, r_area_no); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + /* for flatten center */ + copy_v3_v3(ss->cache->last_center, r_area_co); + } + else { + /* for area normal */ + copy_v3_v3(r_area_no, ss->cache->sculpt_normal); - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_inflate_brush_task_cb_ex, - &settings); -} + /* for flatten center */ + copy_v3_v3(r_area_co, ss->cache->last_center); -static void calc_sculpt_plane( - Sculpt *sd, Object *ob, - PBVHNode **nodes, int totnode, - float r_area_no[3], float r_area_co[3]) -{ - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (ss->cache->mirror_symmetry_pass == 0 && - ss->cache->radial_symmetry_pass == 0 && - ss->cache->tile_pass == 0 && - (ss->cache->first_time || !(brush->flag & BRUSH_ORIGINAL_NORMAL))) - { - switch (brush->sculpt_plane) { - case SCULPT_DISP_DIR_VIEW: - copy_v3_v3(r_area_no, ss->cache->true_view_normal); - break; - - case SCULPT_DISP_DIR_X: - ARRAY_SET_ITEMS(r_area_no, 1, 0, 0); - break; - - case SCULPT_DISP_DIR_Y: - ARRAY_SET_ITEMS(r_area_no, 0, 1, 0); - break; - - case SCULPT_DISP_DIR_Z: - ARRAY_SET_ITEMS(r_area_no, 0, 0, 1); - break; - - case SCULPT_DISP_DIR_AREA: - calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co); - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(r_area_no, r_area_no, ss->cache->view_normal); - normalize_v3(r_area_no); - } - break; - - default: - break; - } - - /* for flatten center */ - /* flatten center has not been calculated yet if we are not using the area normal */ - if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA) - calc_area_center(sd, ob, nodes, totnode, r_area_co); - - /* for area normal */ - copy_v3_v3(ss->cache->sculpt_normal, r_area_no); - - /* for flatten center */ - copy_v3_v3(ss->cache->last_center, r_area_co); - } - else { - /* for area normal */ - copy_v3_v3(r_area_no, ss->cache->sculpt_normal); - - /* for flatten center */ - copy_v3_v3(r_area_co, ss->cache->last_center); - - /* for area normal */ - flip_v3(r_area_no, ss->cache->mirror_symmetry_pass); - - /* for flatten center */ - flip_v3(r_area_co, ss->cache->mirror_symmetry_pass); - - /* for area normal */ - mul_m4_v3(ss->cache->symm_rot_mat, r_area_no); - - /* for flatten center */ - mul_m4_v3(ss->cache->symm_rot_mat, r_area_co); - - /* shift the plane for the current tile */ - add_v3_v3(r_area_co, ss->cache->plane_offset); - } + /* for area normal */ + flip_v3(r_area_no, ss->cache->mirror_symmetry_pass); + + /* for flatten center */ + flip_v3(r_area_co, ss->cache->mirror_symmetry_pass); + + /* for area normal */ + mul_m4_v3(ss->cache->symm_rot_mat, r_area_no); + + /* for flatten center */ + mul_m4_v3(ss->cache->symm_rot_mat, r_area_co); + + /* shift the plane for the current tile */ + add_v3_v3(r_area_co, ss->cache->plane_offset); + } } static int plane_trim(const StrokeCache *cache, const Brush *brush, const float val[3]) { - return (!(brush->flag & BRUSH_PLANE_TRIM) || - ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); + return (!(brush->flag & BRUSH_PLANE_TRIM) || + ((dot_v3v3(val, val) <= cache->radius_squared * cache->plane_trim_squared))); } -static bool plane_point_side_flip( - const float co[3], const float plane[4], - const bool flip) +static bool plane_point_side_flip(const float co[3], const float plane[4], const bool flip) { - float d = plane_point_side_v3(plane, co); - if (flip) d = -d; - return d <= 0.0f; + float d = plane_point_side_v3(plane, co); + if (flip) + d = -d; + return d <= 0.0f; } static int plane_point_side(const float co[3], const float plane[4]) { - float d = plane_point_side_v3(plane, co); - return d <= 0.0f; + float d = plane_point_side_v3(plane, co); + return d <= 0.0f; } static float get_offset(Sculpt *sd, SculptSession *ss) { - Brush *brush = BKE_paint_brush(&sd->paint); + Brush *brush = BKE_paint_brush(&sd->paint); - float rv = brush->plane_offset; + float rv = brush->plane_offset; - if (brush->flag & BRUSH_OFFSET_PRESSURE) { - rv *= ss->cache->pressure; - } + if (brush->flag & BRUSH_OFFSET_PRESSURE) { + rv *= ss->cache->pressure; + } - return rv; + return rv; } -static void do_flatten_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_flatten_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - float intr[3]; - float val[3]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + float intr[3]; + float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); + sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); + mul_v3_v3fl(proxy[vd.i], val, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - const float radius = ss->cache->radius; + const float radius = ss->cache->radius; - float area_no[3]; - float area_co[3]; + float area_no[3]; + float area_co[3]; - float offset = get_offset(sd, ss); - float displace; - float temp[3]; + float offset = get_offset(sd, ss); + float displace; + float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - displace = radius * offset; + displace = radius * offset; - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no = area_no, .area_co = area_co, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_flatten_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings); } -static void do_clay_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_clay_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; - PBVHVertexIter vd; - float (*proxy)[3]; - const bool flip = (ss->cache->bstrength < 0); - const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; + PBVHVertexIter vd; + float(*proxy)[3]; + const bool flip = (ss->cache->bstrength < 0); + const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - if (plane_point_side_flip(vd.co, test.plane_tool, flip)) { - float intr[3]; - float val[3]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + if (plane_point_side_flip(vd.co, test.plane_tool, flip)) { + float intr[3]; + float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); + sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - /* note, the normal from the vertices is ignored, - * causes glitch with planes, see: T44390 */ - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (plane_trim(ss->cache, brush, val)) { + /* note, the normal from the vertices is ignored, + * causes glitch with planes, see: T44390 */ + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); + mul_v3_v3fl(proxy[vd.i], val, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - const bool flip = (ss->cache->bstrength < 0); - const float radius = flip ? -ss->cache->radius : ss->cache->radius; + const bool flip = (ss->cache->bstrength < 0); + const float radius = flip ? -ss->cache->radius : ss->cache->radius; - float offset = get_offset(sd, ss); - float displace; + float offset = get_offset(sd, ss); + float displace; - float area_no[3]; - float area_co[3]; - float temp[3]; + float area_no[3]; + float area_co[3]; + float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - displace = radius * (0.25f + offset); + displace = radius * (0.25f + offset); - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); - /* add_v3_v3v3(p, ss->cache->location, area_no); */ + /* add_v3_v3v3(p, ss->cache->location, area_no); */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no = area_no, .area_co = area_co, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_clay_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_clay_brush_task_cb_ex, &settings); } -static void do_clay_strips_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_clay_strips_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - float (*mat)[4] = data->mat; - const float *area_no_sp = data->area_no_sp; - const float *area_co = data->area_co; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + float(*mat)[4] = data->mat; + const float *area_no_sp = data->area_no_sp; + const float *area_co = data->area_co; - PBVHVertexIter vd; - SculptBrushTest test; - float (*proxy)[3]; - const bool flip = (ss->cache->bstrength < 0); - const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; + PBVHVertexIter vd; + SculptBrushTest test; + float(*proxy)[3]; + const bool flip = (ss->cache->bstrength < 0); + const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - sculpt_brush_test_init(ss, &test); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); + sculpt_brush_test_init(ss, &test); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_cube(&test, vd.co, mat)) { - if (plane_point_side_flip(vd.co, test.plane_tool, flip)) { - float intr[3]; - float val[3]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_cube(&test, vd.co, mat)) { + if (plane_point_side_flip(vd.co, test.plane_tool, flip)) { + float intr[3]; + float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); + sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - /* note, the normal from the vertices is ignored, - * causes glitch with planes, see: T44390 */ - const float fade = bstrength * tex_strength( - ss, brush, vd.co, ss->cache->radius * test.dist, - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (plane_trim(ss->cache, brush, val)) { + /* note, the normal from the vertices is ignored, + * causes glitch with planes, see: T44390 */ + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + ss->cache->radius * test.dist, + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); + mul_v3_v3fl(proxy[vd.i], val, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_clay_strips_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - const bool flip = (ss->cache->bstrength < 0); - const float radius = flip ? -ss->cache->radius : ss->cache->radius; - const float offset = get_offset(sd, ss); - const float displace = radius * (0.25f + offset); - - float area_no_sp[3]; /* the sculpt-plane normal (whatever its set to) */ - float area_no[3]; /* geometry normal */ - float area_co[3]; - - float temp[3]; - float mat[4][4]; - float scale[4][4]; - float tmat[4][4]; - - calc_sculpt_plane(sd, ob, nodes, totnode, area_no_sp, area_co); - - if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) - calc_area_normal(sd, ob, nodes, totnode, area_no); - else - copy_v3_v3(area_no, area_no_sp); - - /* delay the first daub because grab delta is not setup */ - if (ss->cache->first_time) - return; - - mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); - - /* init mat */ - cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); - mat[0][3] = 0; - cross_v3_v3v3(mat[1], area_no, mat[0]); - mat[1][3] = 0; - copy_v3_v3(mat[2], area_no); - mat[2][3] = 0; - copy_v3_v3(mat[3], ss->cache->location); - mat[3][3] = 1; - normalize_m4(mat); - - /* scale mat */ - scale_m4_fl(scale, ss->cache->radius); - mul_m4_m4m4(tmat, mat, scale); - invert_m4_m4(mat, tmat); - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no_sp = area_no_sp, .area_co = area_co, .mat = mat, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_clay_strips_brush_task_cb_ex, - &settings); -} - -static void do_fill_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) -{ - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; - - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; - - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - if (plane_point_side(vd.co, test.plane_tool)) { - float intr[3]; - float val[3]; - - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - - sub_v3_v3v3(val, intr, vd.co); - - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); - - mul_v3_v3fl(proxy[vd.i], val, fade); - - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - BKE_pbvh_vertex_iter_end; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + const bool flip = (ss->cache->bstrength < 0); + const float radius = flip ? -ss->cache->radius : ss->cache->radius; + const float offset = get_offset(sd, ss); + const float displace = radius * (0.25f + offset); + + float area_no_sp[3]; /* the sculpt-plane normal (whatever its set to) */ + float area_no[3]; /* geometry normal */ + float area_co[3]; + + float temp[3]; + float mat[4][4]; + float scale[4][4]; + float tmat[4][4]; + + calc_sculpt_plane(sd, ob, nodes, totnode, area_no_sp, area_co); + + if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) + calc_area_normal(sd, ob, nodes, totnode, area_no); + else + copy_v3_v3(area_no, area_no_sp); + + /* delay the first daub because grab delta is not setup */ + if (ss->cache->first_time) + return; + + mul_v3_v3v3(temp, area_no_sp, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); + + /* init mat */ + cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry); + mat[0][3] = 0; + cross_v3_v3v3(mat[1], area_no, mat[0]); + mat[1][3] = 0; + copy_v3_v3(mat[2], area_no); + mat[2][3] = 0; + copy_v3_v3(mat[3], ss->cache->location); + mat[3][3] = 1; + normalize_m4(mat); + + /* scale mat */ + scale_m4_fl(scale, ss->cache->radius); + mul_m4_m4m4(tmat, mat, scale); + invert_m4_m4(mat, tmat); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no_sp = area_no_sp, + .area_co = area_co, + .mat = mat, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings); +} + +static void do_fill_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; + + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; + + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + if (plane_point_side(vd.co, test.plane_tool)) { + float intr[3]; + float val[3]; + + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + + sub_v3_v3v3(val, intr, vd.co); + + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); + + mul_v3_v3fl(proxy[vd.i], val, fade); + + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - const float radius = ss->cache->radius; + const float radius = ss->cache->radius; - float area_no[3]; - float area_co[3]; - float offset = get_offset(sd, ss); + float area_no[3]; + float area_co[3]; + float offset = get_offset(sd, ss); - float displace; + float displace; - float temp[3]; + float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - displace = radius * offset; + displace = radius * offset; - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no = area_no, .area_co = area_co, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_fill_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_fill_brush_task_cb_ex, &settings); } -static void do_scrape_brush_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_scrape_brush_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - const float *area_no = data->area_no; - const float *area_co = data->area_co; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + const float *area_no = data->area_no; + const float *area_co = data->area_co; - PBVHVertexIter vd; - float (*proxy)[3]; - const float bstrength = ss->cache->bstrength; + PBVHVertexIter vd; + float(*proxy)[3]; + const float bstrength = ss->cache->bstrength; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); - plane_from_point_normal_v3(test.plane_tool, area_co, area_no); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); + plane_from_point_normal_v3(test.plane_tool, area_co, area_no); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - if (!plane_point_side(vd.co, test.plane_tool)) { - float intr[3]; - float val[3]; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + if (!plane_point_side(vd.co, test.plane_tool)) { + float intr[3]; + float val[3]; - closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); + closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co); - sub_v3_v3v3(val, intr, vd.co); + sub_v3_v3v3(val, intr, vd.co); - if (plane_trim(ss->cache, brush, val)) { - const float fade = bstrength * tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + if (plane_trim(ss->cache, brush, val)) { + const float fade = bstrength * tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], val, fade); + mul_v3_v3fl(proxy[vd.i], val, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + } + } + BKE_pbvh_vertex_iter_end; } static void do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - const float radius = ss->cache->radius; + const float radius = ss->cache->radius; - float area_no[3]; - float area_co[3]; - float offset = get_offset(sd, ss); + float area_no[3]; + float area_co[3]; + float offset = get_offset(sd, ss); - float displace; + float displace; - float temp[3]; + float temp[3]; - calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); + calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co); - displace = -radius * offset; + displace = -radius * offset; - mul_v3_v3v3(temp, area_no, ss->cache->scale); - mul_v3_fl(temp, displace); - add_v3_v3(area_co, temp); + mul_v3_v3v3(temp, area_no, ss->cache->scale); + mul_v3_fl(temp, displace); + add_v3_v3(area_co, temp); - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .area_no = area_no, .area_co = area_co, - }; + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .area_no = area_no, + .area_co = area_co, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_scrape_brush_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings); } -static void do_gravity_task_cb_ex( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict tls) +static void do_gravity_task_cb_ex(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict tls) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - const Brush *brush = data->brush; - float *offset = data->offset; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + const Brush *brush = data->brush; + float *offset = data->offset; - PBVHVertexIter vd; - float (*proxy)[3]; + PBVHVertexIter vd; + float(*proxy)[3]; - proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; + proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co; - SculptBrushTest test; - SculptBrushTestFn sculpt_brush_test_sq_fn = - sculpt_brush_test_init_with_falloff_shape(ss, &test, data->brush->falloff_shape); + SculptBrushTest test; + SculptBrushTestFn sculpt_brush_test_sq_fn = sculpt_brush_test_init_with_falloff_shape( + ss, &test, data->brush->falloff_shape); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) { - if (sculpt_brush_test_sq_fn(&test, vd.co)) { - const float fade = tex_strength( - ss, brush, vd.co, sqrtf(test.dist), - vd.no, vd.fno, vd.mask ? *vd.mask : 0.0f, tls->thread_id); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + if (sculpt_brush_test_sq_fn(&test, vd.co)) { + const float fade = tex_strength(ss, + brush, + vd.co, + sqrtf(test.dist), + vd.no, + vd.fno, + vd.mask ? *vd.mask : 0.0f, + tls->thread_id); - mul_v3_v3fl(proxy[vd.i], offset, fade); + mul_v3_v3fl(proxy[vd.i], offset, fade); - if (vd.mvert) - vd.mvert->flag |= ME_VERT_PBVH_UPDATE; - } - } - BKE_pbvh_vertex_iter_end; + if (vd.mvert) + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; } static void do_gravity(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float bstrength) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - float offset[3]/*, area_no[3]*/; - float gravity_vector[3]; + float offset[3] /*, area_no[3]*/; + float gravity_vector[3]; - mul_v3_v3fl(gravity_vector, ss->cache->gravity_direction, -ss->cache->radius_squared); + mul_v3_v3fl(gravity_vector, ss->cache->gravity_direction, -ss->cache->radius_squared); - /* offset with as much as possible factored in already */ - mul_v3_v3v3(offset, gravity_vector, ss->cache->scale); - mul_v3_fl(offset, bstrength); + /* offset with as much as possible factored in already */ + mul_v3_v3v3(offset, gravity_vector, ss->cache->scale); + mul_v3_fl(offset, bstrength); - /* threaded loop over nodes */ - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .offset = offset, - }; + /* threaded loop over nodes */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .offset = offset, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - do_gravity_task_cb_ex, - &settings); + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, do_gravity_task_cb_ex, &settings); } - void sculpt_vertcos_to_key(Object *ob, KeyBlock *kb, float (*vertCos)[3]) { - Mesh *me = (Mesh *)ob->data; - float (*ofs)[3] = NULL; - int a; - const int kb_act_idx = ob->shapenr - 1; - KeyBlock *currkey; + Mesh *me = (Mesh *)ob->data; + float(*ofs)[3] = NULL; + int a; + const int kb_act_idx = ob->shapenr - 1; + KeyBlock *currkey; - /* for relative keys editing of base should update other keys */ - if (BKE_keyblock_is_basis(me->key, kb_act_idx)) { - ofs = BKE_keyblock_convert_to_vertcos(ob, kb); + /* for relative keys editing of base should update other keys */ + if (BKE_keyblock_is_basis(me->key, kb_act_idx)) { + ofs = BKE_keyblock_convert_to_vertcos(ob, kb); - /* calculate key coord offsets (from previous location) */ - for (a = 0; a < me->totvert; a++) { - sub_v3_v3v3(ofs[a], vertCos[a], ofs[a]); - } + /* calculate key coord offsets (from previous location) */ + for (a = 0; a < me->totvert; a++) { + sub_v3_v3v3(ofs[a], vertCos[a], ofs[a]); + } - /* apply offsets on other keys */ - for (currkey = me->key->block.first; currkey; currkey = currkey->next) { - if ((currkey != kb) && (currkey->relative == kb_act_idx)) { - BKE_keyblock_update_from_offset(ob, currkey, ofs); - } - } + /* apply offsets on other keys */ + for (currkey = me->key->block.first; currkey; currkey = currkey->next) { + if ((currkey != kb) && (currkey->relative == kb_act_idx)) { + BKE_keyblock_update_from_offset(ob, currkey, ofs); + } + } - MEM_freeN(ofs); - } + MEM_freeN(ofs); + } - /* modifying of basis key should update mesh */ - if (kb == me->key->refkey) { - MVert *mvert = me->mvert; + /* modifying of basis key should update mesh */ + if (kb == me->key->refkey) { + MVert *mvert = me->mvert; - for (a = 0; a < me->totvert; a++, mvert++) - copy_v3_v3(mvert->co, vertCos[a]); + for (a = 0; a < me->totvert; a++, mvert++) + copy_v3_v3(mvert->co, vertCos[a]); - BKE_mesh_calc_normals(me); - } + BKE_mesh_calc_normals(me); + } - /* apply new coords on active key block, no need to re-allocate kb->data here! */ - BKE_keyblock_update_from_vertcos(ob, kb, vertCos); + /* apply new coords on active key block, no need to re-allocate kb->data here! */ + BKE_keyblock_update_from_vertcos(ob, kb, vertCos); } /* Note: we do the topology update before any brush actions to avoid * issues with the proxies. The size of the proxy can't change, so * topology must be updated first. */ -static void sculpt_topology_update(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *UNUSED(ups)) -{ - SculptSession *ss = ob->sculpt; - - int n, totnode; - /* Build a list of all nodes that are potentially within the brush's area of influence */ - const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; - const float radius_scale = 1.25f; - PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); - - /* Only act if some verts are inside the brush area */ - if (totnode) { - PBVHTopologyUpdateMode mode = 0; - float location[3]; - - if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) { - if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { - mode |= PBVH_Subdivide; - } - - if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || - (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) - { - mode |= PBVH_Collapse; - } - } - - for (n = 0; n < totnode; n++) { - sculpt_undo_push_node(ob, nodes[n], - brush->sculpt_tool == SCULPT_TOOL_MASK ? - SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(nodes[n]); - - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_node_mark_topology_update(nodes[n]); - BKE_pbvh_bmesh_node_save_orig(nodes[n]); - } - } - - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { - BKE_pbvh_bmesh_update_topology( - ss->pbvh, mode, - ss->cache->location, - ss->cache->view_normal, - ss->cache->radius, - (brush->flag & BRUSH_FRONTFACE) != 0, - (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE)); - } - - MEM_freeN(nodes); - - /* update average stroke position */ - copy_v3_v3(location, ss->cache->true_location); - mul_m4_v3(ob->obmat, location); - } -} - -static void do_brush_action_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) -{ - SculptThreadedTaskData *data = userdata; - - sculpt_undo_push_node(data->ob, data->nodes[n], - data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : SCULPT_UNDO_COORDS); - BKE_pbvh_node_mark_update(data->nodes[n]); +static void sculpt_topology_update(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings *UNUSED(ups)) +{ + SculptSession *ss = ob->sculpt; + + int n, totnode; + /* Build a list of all nodes that are potentially within the brush's area of influence */ + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; + const float radius_scale = 1.25f; + PBVHNode **nodes = sculpt_pbvh_gather_generic( + ob, sd, brush, use_original, radius_scale, &totnode); + + /* Only act if some verts are inside the brush area */ + if (totnode) { + PBVHTopologyUpdateMode mode = 0; + float location[3]; + + if (!(sd->flags & SCULPT_DYNTOPO_DETAIL_MANUAL)) { + if (sd->flags & SCULPT_DYNTOPO_SUBDIVIDE) { + mode |= PBVH_Subdivide; + } + + if ((sd->flags & SCULPT_DYNTOPO_COLLAPSE) || (brush->sculpt_tool == SCULPT_TOOL_SIMPLIFY)) { + mode |= PBVH_Collapse; + } + } + + for (n = 0; n < totnode; n++) { + sculpt_undo_push_node(ob, + nodes[n], + brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : + SCULPT_UNDO_COORDS); + BKE_pbvh_node_mark_update(nodes[n]); + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BKE_pbvh_node_mark_topology_update(nodes[n]); + BKE_pbvh_bmesh_node_save_orig(nodes[n]); + } + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + BKE_pbvh_bmesh_update_topology(ss->pbvh, + mode, + ss->cache->location, + ss->cache->view_normal, + ss->cache->radius, + (brush->flag & BRUSH_FRONTFACE) != 0, + (brush->falloff_shape != PAINT_FALLOFF_SHAPE_SPHERE)); + } + + MEM_freeN(nodes); + + /* update average stroke position */ + copy_v3_v3(location, ss->cache->true_location); + mul_m4_v3(ob->obmat, location); + } +} + +static void do_brush_action_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + + sculpt_undo_push_node(data->ob, + data->nodes[n], + data->brush->sculpt_tool == SCULPT_TOOL_MASK ? SCULPT_UNDO_MASK : + SCULPT_UNDO_COORDS); + BKE_pbvh_node_mark_update(data->nodes[n]); } static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups) { - SculptSession *ss = ob->sculpt; - int totnode; - - /* Build a list of all nodes that are potentially within the brush's area of influence */ - const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : ss->cache->original; - const float radius_scale = 1.0f; - PBVHNode **nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); - - /* Only act if some verts are inside the brush area */ - if (totnode) { - float location[3]; - - SculptThreadedTaskData task_data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &task_data, - do_brush_action_task_cb, - &settings); - - if (sculpt_brush_needs_normal(ss, brush)) - update_sculpt_normal(sd, ob, nodes, totnode); - - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA) - update_brush_local_mat(sd, ob); - - /* Apply one type of brush action */ - switch (brush->sculpt_tool) { - case SCULPT_TOOL_DRAW: - do_draw_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_SMOOTH: - do_smooth_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_CREASE: - do_crease_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_BLOB: - do_crease_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_PINCH: - do_pinch_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_INFLATE: - do_inflate_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_GRAB: - do_grab_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_ROTATE: - do_rotate_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_SNAKE_HOOK: - do_snake_hook_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_NUDGE: - do_nudge_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_THUMB: - do_thumb_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_LAYER: - do_layer_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_FLATTEN: - do_flatten_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_CLAY: - do_clay_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_CLAY_STRIPS: - do_clay_strips_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_FILL: - do_fill_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_SCRAPE: - do_scrape_brush(sd, ob, nodes, totnode); - break; - case SCULPT_TOOL_MASK: - do_mask_brush(sd, ob, nodes, totnode); - break; - } - - if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && - brush->autosmooth_factor > 0) - { - if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) { - smooth(sd, ob, nodes, totnode, brush->autosmooth_factor * (1 - ss->cache->pressure), false); - } - else { - smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, false); - } - } - - if (sculpt_brush_use_topology_rake(ss, brush)) { - bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); - } - - if (ss->cache->supports_gravity) - do_gravity(sd, ob, nodes, totnode, sd->gravity_factor); - - MEM_freeN(nodes); - - /* update average stroke position */ - copy_v3_v3(location, ss->cache->true_location); - mul_m4_v3(ob->obmat, location); - - add_v3_v3(ups->average_stroke_accum, location); - ups->average_stroke_counter++; - /* update last stroke position */ - ups->last_stroke_valid = true; - } + SculptSession *ss = ob->sculpt; + int totnode; + + /* Build a list of all nodes that are potentially within the brush's area of influence */ + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; + const float radius_scale = 1.0f; + PBVHNode **nodes = sculpt_pbvh_gather_generic( + ob, sd, brush, use_original, radius_scale, &totnode); + + /* Only act if some verts are inside the brush area */ + if (totnode) { + float location[3]; + + SculptThreadedTaskData task_data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &task_data, do_brush_action_task_cb, &settings); + + if (sculpt_brush_needs_normal(ss, brush)) + update_sculpt_normal(sd, ob, nodes, totnode); + + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_AREA) + update_brush_local_mat(sd, ob); + + /* Apply one type of brush action */ + switch (brush->sculpt_tool) { + case SCULPT_TOOL_DRAW: + do_draw_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SMOOTH: + do_smooth_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CREASE: + do_crease_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_BLOB: + do_crease_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_PINCH: + do_pinch_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_INFLATE: + do_inflate_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_GRAB: + do_grab_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_ROTATE: + do_rotate_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SNAKE_HOOK: + do_snake_hook_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_NUDGE: + do_nudge_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_THUMB: + do_thumb_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_LAYER: + do_layer_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_FLATTEN: + do_flatten_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLAY: + do_clay_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLAY_STRIPS: + do_clay_strips_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_FILL: + do_fill_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SCRAPE: + do_scrape_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_MASK: + do_mask_brush(sd, ob, nodes, totnode); + break; + } + + if (!ELEM(brush->sculpt_tool, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK) && + brush->autosmooth_factor > 0) { + if (brush->flag & BRUSH_INVERSE_SMOOTH_PRESSURE) { + smooth( + sd, ob, nodes, totnode, brush->autosmooth_factor * (1 - ss->cache->pressure), false); + } + else { + smooth(sd, ob, nodes, totnode, brush->autosmooth_factor, false); + } + } + + if (sculpt_brush_use_topology_rake(ss, brush)) { + bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor); + } + + if (ss->cache->supports_gravity) + do_gravity(sd, ob, nodes, totnode, sd->gravity_factor); + + MEM_freeN(nodes); + + /* update average stroke position */ + copy_v3_v3(location, ss->cache->true_location); + mul_m4_v3(ob->obmat, location); + + add_v3_v3(ups->average_stroke_accum, location); + ups->average_stroke_counter++; + /* update last stroke position */ + ups->last_stroke_valid = true; + } } /* flush displacement from deformed PBVH vertex to original mesh */ static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd) { - SculptSession *ss = ob->sculpt; - Mesh *me = ob->data; - float disp[3], newco[3]; - int index = vd->vert_indices[vd->i]; + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + float disp[3], newco[3]; + int index = vd->vert_indices[vd->i]; - sub_v3_v3v3(disp, vd->co, ss->deform_cos[index]); - mul_m3_v3(ss->deform_imats[index], disp); - add_v3_v3v3(newco, disp, ss->orig_cos[index]); + sub_v3_v3v3(disp, vd->co, ss->deform_cos[index]); + mul_m3_v3(ss->deform_imats[index], disp); + add_v3_v3v3(newco, disp, ss->orig_cos[index]); - copy_v3_v3(ss->deform_cos[index], vd->co); - copy_v3_v3(ss->orig_cos[index], newco); + copy_v3_v3(ss->deform_cos[index], vd->co); + copy_v3_v3(ss->orig_cos[index], newco); - if (!ss->kb) - copy_v3_v3(me->mvert[index].co, newco); + if (!ss->kb) + copy_v3_v3(me->mvert[index].co, newco); } -static void sculpt_combine_proxies_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void sculpt_combine_proxies_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Sculpt *sd = data->sd; - Object *ob = data->ob; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Sculpt *sd = data->sd; + Object *ob = data->ob; - /* these brushes start from original coordinates */ - const bool use_orco = ELEM(data->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB); + /* these brushes start from original coordinates */ + const bool use_orco = ELEM( + data->brush->sculpt_tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_ROTATE, SCULPT_TOOL_THUMB); - PBVHVertexIter vd; - PBVHProxyNode *proxies; - int proxy_count; - float (*orco)[3] = NULL; + PBVHVertexIter vd; + PBVHProxyNode *proxies; + int proxy_count; + float(*orco)[3] = NULL; - if (use_orco && !ss->bm) - orco = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS)->co; + if (use_orco && !ss->bm) + orco = sculpt_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_COORDS)->co; - BKE_pbvh_node_get_proxies(data->nodes[n], &proxies, &proxy_count); + BKE_pbvh_node_get_proxies(data->nodes[n], &proxies, &proxy_count); - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - float val[3]; - int p; + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + float val[3]; + int p; - if (use_orco) { - if (ss->bm) { - copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert)); - } - else { - copy_v3_v3(val, orco[vd.i]); - } - } - else { - copy_v3_v3(val, vd.co); - } + if (use_orco) { + if (ss->bm) { + copy_v3_v3(val, BM_log_original_vert_co(ss->bm_log, vd.bm_vert)); + } + else { + copy_v3_v3(val, orco[vd.i]); + } + } + else { + copy_v3_v3(val, vd.co); + } - for (p = 0; p < proxy_count; p++) - add_v3_v3(val, proxies[p].co[vd.i]); + for (p = 0; p < proxy_count; p++) + add_v3_v3(val, proxies[p].co[vd.i]); - sculpt_clip(sd, ss, vd.co, val); + sculpt_clip(sd, ss, vd.co, val); - if (ss->modifiers_active) - sculpt_flush_pbvhvert_deform(ob, &vd); - } - BKE_pbvh_vertex_iter_end; + if (ss->modifiers_active) + sculpt_flush_pbvhvert_deform(ob, &vd); + } + BKE_pbvh_vertex_iter_end; - BKE_pbvh_node_free_proxies(data->nodes[n]); + BKE_pbvh_node_free_proxies(data->nodes[n]); } static void sculpt_combine_proxies(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - PBVHNode **nodes; - int totnode; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + PBVHNode **nodes; + int totnode; - BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); + BKE_pbvh_gather_proxies(ss->pbvh, &nodes, &totnode); - /* first line is tools that don't support proxies */ - if (ss->cache->supports_gravity || - (sculpt_tool_is_proxy_used(brush->sculpt_tool) == false)) - { - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - }; + /* first line is tools that don't support proxies */ + if (ss->cache->supports_gravity || (sculpt_tool_is_proxy_used(brush->sculpt_tool) == false)) { + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + }; - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - sculpt_combine_proxies_task_cb, - &settings); - } + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, sculpt_combine_proxies_task_cb, &settings); + } - if (nodes) - MEM_freeN(nodes); + if (nodes) + MEM_freeN(nodes); } /* copy the modified vertices from bvh to the active key */ static void sculpt_update_keyblock(Object *ob) { - SculptSession *ss = ob->sculpt; - float (*vertCos)[3]; + SculptSession *ss = ob->sculpt; + float(*vertCos)[3]; - /* Keyblock update happens after handling deformation caused by modifiers, - * so ss->orig_cos would be updated with new stroke */ - if (ss->orig_cos) vertCos = ss->orig_cos; - else vertCos = BKE_pbvh_get_vertCos(ss->pbvh); + /* Keyblock update happens after handling deformation caused by modifiers, + * so ss->orig_cos would be updated with new stroke */ + if (ss->orig_cos) + vertCos = ss->orig_cos; + else + vertCos = BKE_pbvh_get_vertCos(ss->pbvh); - if (vertCos) { - sculpt_vertcos_to_key(ob, ss->kb, vertCos); + if (vertCos) { + sculpt_vertcos_to_key(ob, ss->kb, vertCos); - if (vertCos != ss->orig_cos) - MEM_freeN(vertCos); - } + if (vertCos != ss->orig_cos) + MEM_freeN(vertCos); + } } -static void sculpt_flush_stroke_deform_task_cb( - void *__restrict userdata, - const int n, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void sculpt_flush_stroke_deform_task_cb(void *__restrict userdata, + const int n, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - SculptThreadedTaskData *data = userdata; - SculptSession *ss = data->ob->sculpt; - Object *ob = data->ob; - float (*vertCos)[3] = data->vertCos; + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + Object *ob = data->ob; + float(*vertCos)[3] = data->vertCos; - PBVHVertexIter vd; + PBVHVertexIter vd; - BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) - { - sculpt_flush_pbvhvert_deform(ob, &vd); + BKE_pbvh_vertex_iter_begin(ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) + { + sculpt_flush_pbvhvert_deform(ob, &vd); - if (vertCos) { - int index = vd.vert_indices[vd.i]; - copy_v3_v3(vertCos[index], ss->orig_cos[index]); - } - } - BKE_pbvh_vertex_iter_end; + if (vertCos) { + int index = vd.vert_indices[vd.i]; + copy_v3_v3(vertCos[index], ss->orig_cos[index]); + } + } + BKE_pbvh_vertex_iter_end; } /* flush displacement from deformed PBVH to original layer */ static void sculpt_flush_stroke_deform(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - - if (sculpt_tool_is_proxy_used(brush->sculpt_tool)) { - /* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't - * propagate needed deformation to original base */ - - int totnode; - Mesh *me = (Mesh *)ob->data; - PBVHNode **nodes; - float (*vertCos)[3] = NULL; - - if (ss->kb) { - vertCos = MEM_mallocN(sizeof(*vertCos) * me->totvert, "flushStrokeDeofrm keyVerts"); - - /* mesh could have isolated verts which wouldn't be in BVH, - * to deal with this we copy old coordinates over new ones - * and then update coordinates for all vertices from BVH - */ - memcpy(vertCos, ss->orig_cos, sizeof(*vertCos) * me->totvert); - } - - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); - - SculptThreadedTaskData data = { - .sd = sd, .ob = ob, .brush = brush, .nodes = nodes, - .vertCos = vertCos, - }; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); - BLI_task_parallel_range( - 0, totnode, - &data, - sculpt_flush_stroke_deform_task_cb, - &settings); - - if (vertCos) { - sculpt_vertcos_to_key(ob, ss->kb, vertCos); - MEM_freeN(vertCos); - } - - MEM_freeN(nodes); - - /* Modifiers could depend on mesh normals, so we should update them/ - * Note, then if sculpting happens on locked key, normals should be re-calculated - * after applying coords from keyblock on base mesh */ - BKE_mesh_calc_normals(me); - } - else if (ss->kb) { - sculpt_update_keyblock(ob); - } + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + + if (sculpt_tool_is_proxy_used(brush->sculpt_tool)) { + /* this brushes aren't using proxies, so sculpt_combine_proxies() wouldn't + * propagate needed deformation to original base */ + + int totnode; + Mesh *me = (Mesh *)ob->data; + PBVHNode **nodes; + float(*vertCos)[3] = NULL; + + if (ss->kb) { + vertCos = MEM_mallocN(sizeof(*vertCos) * me->totvert, "flushStrokeDeofrm keyVerts"); + + /* mesh could have isolated verts which wouldn't be in BVH, + * to deal with this we copy old coordinates over new ones + * and then update coordinates for all vertices from BVH + */ + memcpy(vertCos, ss->orig_cos, sizeof(*vertCos) * me->totvert); + } + + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .brush = brush, + .nodes = nodes, + .vertCos = vertCos, + }; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + settings.use_threading = ((sd->flags & SCULPT_USE_OPENMP) && totnode > SCULPT_THREADED_LIMIT); + BLI_task_parallel_range(0, totnode, &data, sculpt_flush_stroke_deform_task_cb, &settings); + + if (vertCos) { + sculpt_vertcos_to_key(ob, ss->kb, vertCos); + MEM_freeN(vertCos); + } + + MEM_freeN(nodes); + + /* Modifiers could depend on mesh normals, so we should update them/ + * Note, then if sculpting happens on locked key, normals should be re-calculated + * after applying coords from keyblock on base mesh */ + BKE_mesh_calc_normals(me); + } + else if (ss->kb) { + sculpt_update_keyblock(ob); + } } /* Flip all the editdata across the axis/axes specified by symm. Used to * calculate multiple modifications to the mesh when symmetry is enabled. */ -void sculpt_cache_calc_brushdata_symm( - StrokeCache *cache, const char symm, - const char axis, const float angle) -{ - flip_v3_v3(cache->location, cache->true_location, symm); - flip_v3_v3(cache->last_location, cache->true_last_location, symm); - flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm); - flip_v3_v3(cache->view_normal, cache->true_view_normal, symm); - - /* XXX This reduces the length of the grab delta if it approaches the line of symmetry - * XXX However, a different approach appears to be needed */ +void sculpt_cache_calc_brushdata_symm(StrokeCache *cache, + const char symm, + const char axis, + const float angle) +{ + flip_v3_v3(cache->location, cache->true_location, symm); + flip_v3_v3(cache->last_location, cache->true_last_location, symm); + flip_v3_v3(cache->grab_delta_symmetry, cache->grab_delta, symm); + flip_v3_v3(cache->view_normal, cache->true_view_normal, symm); + + /* XXX This reduces the length of the grab delta if it approaches the line of symmetry + * XXX However, a different approach appears to be needed */ #if 0 - if (sd->paint.symmetry_flags & PAINT_SYMMETRY_FEATHER) { - float frac = 1.0f / max_overlap_count(sd); - float reduce = (feather - frac) / (1 - frac); + if (sd->paint.symmetry_flags & PAINT_SYMMETRY_FEATHER) { + float frac = 1.0f / max_overlap_count(sd); + float reduce = (feather - frac) / (1 - frac); - printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); + printf("feather: %f frac: %f reduce: %f\n", feather, frac, reduce); - if (frac < 1) - mul_v3_fl(cache->grab_delta_symmetry, reduce); - } + if (frac < 1) + mul_v3_fl(cache->grab_delta_symmetry, reduce); + } #endif - unit_m4(cache->symm_rot_mat); - unit_m4(cache->symm_rot_mat_inv); - zero_v3(cache->plane_offset); + unit_m4(cache->symm_rot_mat); + unit_m4(cache->symm_rot_mat_inv); + zero_v3(cache->plane_offset); - if (axis) { /* expects XYZ */ - rotate_m4(cache->symm_rot_mat, axis, angle); - rotate_m4(cache->symm_rot_mat_inv, axis, -angle); - } + if (axis) { /* expects XYZ */ + rotate_m4(cache->symm_rot_mat, axis, angle); + rotate_m4(cache->symm_rot_mat_inv, axis, -angle); + } - mul_m4_v3(cache->symm_rot_mat, cache->location); - mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); + mul_m4_v3(cache->symm_rot_mat, cache->location); + mul_m4_v3(cache->symm_rot_mat, cache->grab_delta_symmetry); - if (cache->supports_gravity) { - flip_v3_v3(cache->gravity_direction, cache->true_gravity_direction, symm); - mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction); - } + if (cache->supports_gravity) { + flip_v3_v3(cache->gravity_direction, cache->true_gravity_direction, symm); + mul_m4_v3(cache->symm_rot_mat, cache->gravity_direction); + } - if (cache->is_rake_rotation_valid) { - flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm); - } + if (cache->is_rake_rotation_valid) { + flip_qt_qt(cache->rake_rotation_symmetry, cache->rake_rotation, symm); + } } typedef void (*BrushActionFunc)(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups); -static void do_tiled(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, BrushActionFunc action) -{ - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const float radius = cache->radius; - BoundBox *bb = BKE_object_boundbox_get(ob); - const float *bbMin = bb->vec[0]; - const float *bbMax = bb->vec[6]; - const float *step = sd->paint.tile_offset; - int dim; - - /* These are integer locations, for real location: multiply with step and add orgLoc. - * So 0,0,0 is at orgLoc. */ - int start[3]; - int end[3]; - int cur[3]; - - float orgLoc[3]; /* position of the "prototype" stroke for tiling */ - copy_v3_v3(orgLoc, cache->location); - - for (dim = 0; dim < 3; ++dim) { - if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) { - start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim]; - end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim]; - } - else - start[dim] = end[dim] = 0; - } - - /* first do the "untiled" position to initialize the stroke for this location */ - cache->tile_pass = 0; - action(sd, ob, brush, ups); - - /* now do it for all the tiles */ - copy_v3_v3_int(cur, start); - for (cur[0] = start[0]; cur[0] <= end[0]; ++cur[0]) { - for (cur[1] = start[1]; cur[1] <= end[1]; ++cur[1]) { - for (cur[2] = start[2]; cur[2] <= end[2]; ++cur[2]) { - if (!cur[0] && !cur[1] && !cur[2]) - continue; /* skip tile at orgLoc, this was already handled before all others */ - - ++cache->tile_pass; - - for (dim = 0; dim < 3; ++dim) { - cache->location[dim] = cur[dim] * step[dim] + orgLoc[dim]; - cache->plane_offset[dim] = cur[dim] * step[dim]; - } - action(sd, ob, brush, ups); - } - } - } -} - - -static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, +static void do_tiled( + Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, BrushActionFunc action) +{ + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const float radius = cache->radius; + BoundBox *bb = BKE_object_boundbox_get(ob); + const float *bbMin = bb->vec[0]; + const float *bbMax = bb->vec[6]; + const float *step = sd->paint.tile_offset; + int dim; + + /* These are integer locations, for real location: multiply with step and add orgLoc. + * So 0,0,0 is at orgLoc. */ + int start[3]; + int end[3]; + int cur[3]; + + float orgLoc[3]; /* position of the "prototype" stroke for tiling */ + copy_v3_v3(orgLoc, cache->location); + + for (dim = 0; dim < 3; ++dim) { + if ((sd->paint.symmetry_flags & (PAINT_TILE_X << dim)) && step[dim] > 0) { + start[dim] = (bbMin[dim] - orgLoc[dim] - radius) / step[dim]; + end[dim] = (bbMax[dim] - orgLoc[dim] + radius) / step[dim]; + } + else + start[dim] = end[dim] = 0; + } + + /* first do the "untiled" position to initialize the stroke for this location */ + cache->tile_pass = 0; + action(sd, ob, brush, ups); + + /* now do it for all the tiles */ + copy_v3_v3_int(cur, start); + for (cur[0] = start[0]; cur[0] <= end[0]; ++cur[0]) { + for (cur[1] = start[1]; cur[1] <= end[1]; ++cur[1]) { + for (cur[2] = start[2]; cur[2] <= end[2]; ++cur[2]) { + if (!cur[0] && !cur[1] && !cur[2]) + continue; /* skip tile at orgLoc, this was already handled before all others */ + + ++cache->tile_pass; + + for (dim = 0; dim < 3; ++dim) { + cache->location[dim] = cur[dim] * step[dim] + orgLoc[dim]; + cache->plane_offset[dim] = cur[dim] * step[dim]; + } + action(sd, ob, brush, ups); + } + } + } +} + +static void do_radial_symmetry(Sculpt *sd, + Object *ob, + Brush *brush, + UnifiedPaintSettings *ups, BrushActionFunc action, - const char symm, const int axis, + const char symm, + const int axis, const float UNUSED(feather)) { - SculptSession *ss = ob->sculpt; - int i; + SculptSession *ss = ob->sculpt; + int i; - for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { - const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; - ss->cache->radial_symmetry_pass = i; - sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); - do_tiled(sd, ob, brush, ups, action); - } + for (i = 1; i < sd->radial_symm[axis - 'X']; ++i) { + const float angle = 2 * M_PI * i / sd->radial_symm[axis - 'X']; + ss->cache->radial_symmetry_pass = i; + sculpt_cache_calc_brushdata_symm(ss->cache, symm, axis, angle); + do_tiled(sd, ob, brush, ups, action); + } } /* noise texture gives different values for the same input coord; this @@ -4205,140 +4244,137 @@ static void do_radial_symmetry(Sculpt *sd, Object *ob, Brush *brush, UnifiedPain * case */ static void sculpt_fix_noise_tear(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - MTex *mtex = &brush->mtex; + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + MTex *mtex = &brush->mtex; - if (ss->multires && mtex->tex && mtex->tex->type == TEX_NOISE) - multires_stitch_grids(ob); + if (ss->multires && mtex->tex && mtex->tex->type == TEX_NOISE) + multires_stitch_grids(ob); } -static void do_symmetrical_brush_actions( - Sculpt *sd, Object *ob, - BrushActionFunc action, UnifiedPaintSettings *ups) +static void do_symmetrical_brush_actions(Sculpt *sd, + Object *ob, + BrushActionFunc action, + UnifiedPaintSettings *ups) { - Brush *brush = BKE_paint_brush(&sd->paint); - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; - int i; + Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const char symm = sd->paint.symmetry_flags & PAINT_SYMM_AXIS_ALL; + int i; - float feather = calc_symmetry_feather(sd, ss->cache); + float feather = calc_symmetry_feather(sd, ss->cache); - cache->bstrength = brush_strength(sd, cache, feather, ups); - cache->symmetry = symm; + cache->bstrength = brush_strength(sd, cache, feather, ups); + cache->symmetry = symm; - /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ - for (i = 0; i <= symm; ++i) { - if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { - cache->mirror_symmetry_pass = i; - cache->radial_symmetry_pass = 0; + /* symm is a bit combination of XYZ - 1 is mirror X; 2 is Y; 3 is XY; 4 is Z; 5 is XZ; 6 is YZ; 7 is XYZ */ + for (i = 0; i <= symm; ++i) { + if (i == 0 || (symm & i && (symm != 5 || i != 3) && (symm != 6 || (i != 3 && i != 5)))) { + cache->mirror_symmetry_pass = i; + cache->radial_symmetry_pass = 0; - sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); - do_tiled(sd, ob, brush, ups, action); + sculpt_cache_calc_brushdata_symm(cache, i, 0, 0); + do_tiled(sd, ob, brush, ups, action); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'X', feather); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'Y', feather); - do_radial_symmetry(sd, ob, brush, ups, action, i, 'Z', feather); - } - } + do_radial_symmetry(sd, ob, brush, ups, action, i, 'X', feather); + do_radial_symmetry(sd, ob, brush, ups, action, i, 'Y', feather); + do_radial_symmetry(sd, ob, brush, ups, action, i, 'Z', feather); + } + } } static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) { - Brush *brush = BKE_paint_brush(&sd->paint); - const int radius = BKE_brush_size_get(scene, brush); + Brush *brush = BKE_paint_brush(&sd->paint); + const int radius = BKE_brush_size_get(scene, brush); - if (ss->texcache) { - MEM_freeN(ss->texcache); - ss->texcache = NULL; - } + if (ss->texcache) { + MEM_freeN(ss->texcache); + ss->texcache = NULL; + } - if (ss->tex_pool) { - BKE_image_pool_free(ss->tex_pool); - ss->tex_pool = NULL; - } + if (ss->tex_pool) { + BKE_image_pool_free(ss->tex_pool); + ss->tex_pool = NULL; + } - /* Need to allocate a bigger buffer for bigger brush size */ - ss->texcache_side = 2 * radius; - if (!ss->texcache || ss->texcache_side > ss->texcache_actual) { - ss->texcache = BKE_brush_gen_texture_cache(brush, radius, false); - ss->texcache_actual = ss->texcache_side; - ss->tex_pool = BKE_image_pool_new(); - } + /* Need to allocate a bigger buffer for bigger brush size */ + ss->texcache_side = 2 * radius; + if (!ss->texcache || ss->texcache_side > ss->texcache_actual) { + ss->texcache = BKE_brush_gen_texture_cache(brush, radius, false); + ss->texcache_actual = ss->texcache_side; + ss->tex_pool = BKE_image_pool_new(); + } } - - bool sculpt_mode_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - return ob && ob->mode & OB_MODE_SCULPT; + Object *ob = CTX_data_active_object(C); + return ob && ob->mode & OB_MODE_SCULPT; } bool sculpt_mode_poll_view3d(bContext *C) { - return (sculpt_mode_poll(C) && - CTX_wm_region_view3d(C)); + return (sculpt_mode_poll(C) && CTX_wm_region_view3d(C)); } bool sculpt_poll_view3d(bContext *C) { - return (sculpt_poll(C) && - CTX_wm_region_view3d(C)); + return (sculpt_poll(C) && CTX_wm_region_view3d(C)); } bool sculpt_poll(bContext *C) { - return sculpt_mode_poll(C) && paint_poll(C); + return sculpt_mode_poll(C) && paint_poll(C); } static const char *sculpt_tool_name(Sculpt *sd) { - Brush *brush = BKE_paint_brush(&sd->paint); - - switch ((eBrushSculptTool)brush->sculpt_tool) { - case SCULPT_TOOL_DRAW: - return "Draw Brush"; - case SCULPT_TOOL_SMOOTH: - return "Smooth Brush"; - case SCULPT_TOOL_CREASE: - return "Crease Brush"; - case SCULPT_TOOL_BLOB: - return "Blob Brush"; - case SCULPT_TOOL_PINCH: - return "Pinch Brush"; - case SCULPT_TOOL_INFLATE: - return "Inflate Brush"; - case SCULPT_TOOL_GRAB: - return "Grab Brush"; - case SCULPT_TOOL_NUDGE: - return "Nudge Brush"; - case SCULPT_TOOL_THUMB: - return "Thumb Brush"; - case SCULPT_TOOL_LAYER: - return "Layer Brush"; - case SCULPT_TOOL_FLATTEN: - return "Flatten Brush"; - case SCULPT_TOOL_CLAY: - return "Clay Brush"; - case SCULPT_TOOL_CLAY_STRIPS: - return "Clay Strips Brush"; - case SCULPT_TOOL_FILL: - return "Fill Brush"; - case SCULPT_TOOL_SCRAPE: - return "Scrape Brush"; - case SCULPT_TOOL_SNAKE_HOOK: - return "Snake Hook Brush"; - case SCULPT_TOOL_ROTATE: - return "Rotate Brush"; - case SCULPT_TOOL_MASK: - return "Mask Brush"; - case SCULPT_TOOL_SIMPLIFY: - return "Simplify Brush"; - } - - return "Sculpting"; + Brush *brush = BKE_paint_brush(&sd->paint); + + switch ((eBrushSculptTool)brush->sculpt_tool) { + case SCULPT_TOOL_DRAW: + return "Draw Brush"; + case SCULPT_TOOL_SMOOTH: + return "Smooth Brush"; + case SCULPT_TOOL_CREASE: + return "Crease Brush"; + case SCULPT_TOOL_BLOB: + return "Blob Brush"; + case SCULPT_TOOL_PINCH: + return "Pinch Brush"; + case SCULPT_TOOL_INFLATE: + return "Inflate Brush"; + case SCULPT_TOOL_GRAB: + return "Grab Brush"; + case SCULPT_TOOL_NUDGE: + return "Nudge Brush"; + case SCULPT_TOOL_THUMB: + return "Thumb Brush"; + case SCULPT_TOOL_LAYER: + return "Layer Brush"; + case SCULPT_TOOL_FLATTEN: + return "Flatten Brush"; + case SCULPT_TOOL_CLAY: + return "Clay Brush"; + case SCULPT_TOOL_CLAY_STRIPS: + return "Clay Strips Brush"; + case SCULPT_TOOL_FILL: + return "Fill Brush"; + case SCULPT_TOOL_SCRAPE: + return "Scrape Brush"; + case SCULPT_TOOL_SNAKE_HOOK: + return "Snake Hook Brush"; + case SCULPT_TOOL_ROTATE: + return "Rotate Brush"; + case SCULPT_TOOL_MASK: + return "Mask Brush"; + case SCULPT_TOOL_SIMPLIFY: + return "Simplify Brush"; + } + + return "Sculpting"; } /** @@ -4347,567 +4383,557 @@ static const char *sculpt_tool_name(Sculpt *sd) void sculpt_cache_free(StrokeCache *cache) { - if (cache->dial) - MEM_freeN(cache->dial); - MEM_freeN(cache); + if (cache->dial) + MEM_freeN(cache->dial); + MEM_freeN(cache); } /* Initialize mirror modifier clipping */ static void sculpt_init_mirror_clipping(Object *ob, SculptSession *ss) { - ModifierData *md; - int i; - - for (md = ob->modifiers.first; md; md = md->next) { - if (md->type == eModifierType_Mirror && - (md->mode & eModifierMode_Realtime)) - { - MirrorModifierData *mmd = (MirrorModifierData *)md; - - if (mmd->flag & MOD_MIR_CLIPPING) { - /* check each axis for mirroring */ - for (i = 0; i < 3; ++i) { - if (mmd->flag & (MOD_MIR_AXIS_X << i)) { - /* enable sculpt clipping */ - ss->cache->flag |= CLIP_X << i; - - /* update the clip tolerance */ - if (mmd->tolerance > - ss->cache->clip_tolerance[i]) - { - ss->cache->clip_tolerance[i] = - mmd->tolerance; - } - } - } - } - } - } + ModifierData *md; + int i; + + for (md = ob->modifiers.first; md; md = md->next) { + if (md->type == eModifierType_Mirror && (md->mode & eModifierMode_Realtime)) { + MirrorModifierData *mmd = (MirrorModifierData *)md; + + if (mmd->flag & MOD_MIR_CLIPPING) { + /* check each axis for mirroring */ + for (i = 0; i < 3; ++i) { + if (mmd->flag & (MOD_MIR_AXIS_X << i)) { + /* enable sculpt clipping */ + ss->cache->flag |= CLIP_X << i; + + /* update the clip tolerance */ + if (mmd->tolerance > ss->cache->clip_tolerance[i]) { + ss->cache->clip_tolerance[i] = mmd->tolerance; + } + } + } + } + } + } } /* Initialize the stroke cache invariants from operator properties */ static void sculpt_update_cache_invariants( - bContext *C, Sculpt *sd, SculptSession *ss, - wmOperator *op, const float mouse[2]) -{ - StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - Brush *brush = BKE_paint_brush(&sd->paint); - ViewContext *vc = paint_stroke_view_context(op->customdata); - Object *ob = CTX_data_active_object(C); - float mat[3][3]; - float viewDir[3] = {0.0f, 0.0f, 1.0f}; - float max_scale; - int i; - int mode; - - ss->cache = cache; - - /* Set scaling adjustment */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { - max_scale = 1.0f; - } - else { - max_scale = 0.0f; - for (i = 0; i < 3; i ++) { - max_scale = max_ff(max_scale, fabsf(ob->scale[i])); - } - } - cache->scale[0] = max_scale / ob->scale[0]; - cache->scale[1] = max_scale / ob->scale[1]; - cache->scale[2] = max_scale / ob->scale[2]; - - cache->plane_trim_squared = brush->plane_trim * brush->plane_trim; - - cache->flag = 0; - - sculpt_init_mirror_clipping(ob, ss); - - /* Initial mouse location */ - if (mouse) - copy_v2_v2(cache->initial_mouse, mouse); - else - zero_v2(cache->initial_mouse); - - mode = RNA_enum_get(op->ptr, "mode"); - cache->invert = mode == BRUSH_STROKE_INVERT; - cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH; - cache->normal_weight = brush->normal_weight; - - /* interpret invert as following normal, for grab brushes */ - if (SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool)) { - if (cache->invert) { - cache->invert = false; - cache->normal_weight = (cache->normal_weight == 0.0f); - } - } - - /* not very nice, but with current events system implementation - * we can't handle brush appearance inversion hotkey separately (sergey) */ - if (cache->invert) ups->draw_inverted = true; - else ups->draw_inverted = false; - - /* Alt-Smooth */ - if (cache->alt_smooth) { - if (brush->sculpt_tool == SCULPT_TOOL_MASK) { - cache->saved_mask_brush_tool = brush->mask_tool; - brush->mask_tool = BRUSH_MASK_SMOOTH; - } - else { - Paint *p = &sd->paint; - Brush *br; - int size = BKE_brush_size_get(scene, brush); - - BLI_strncpy(cache->saved_active_brush_name, brush->id.name + 2, - sizeof(cache->saved_active_brush_name)); - - br = (Brush *)BKE_libblock_find_name(bmain, ID_BR, "Smooth"); - if (br) { - BKE_paint_brush_set(p, br); - brush = br; - cache->saved_smooth_size = BKE_brush_size_get(scene, brush); - BKE_brush_size_set(scene, brush, size); - curvemapping_initialize(brush->curve); - } - } - } - - copy_v2_v2(cache->mouse, cache->initial_mouse); - copy_v2_v2(ups->tex_mouse, cache->initial_mouse); - - /* Truly temporary data that isn't stored in properties */ - - cache->vc = vc; - - cache->brush = brush; - - /* cache projection matrix */ - ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); - - invert_m4_m4(ob->imat, ob->obmat); - copy_m3_m4(mat, cache->vc->rv3d->viewinv); - mul_m3_v3(mat, viewDir); - copy_m3_m4(mat, ob->imat); - mul_m3_v3(mat, viewDir); - normalize_v3_v3(cache->true_view_normal, viewDir); - - cache->supports_gravity = (!ELEM(brush->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SIMPLIFY) && - (sd->gravity_factor > 0.0f)); - /* get gravity vector in world space */ - if (cache->supports_gravity) { - if (sd->gravity_object) { - Object *gravity_object = sd->gravity_object; - - copy_v3_v3(cache->true_gravity_direction, gravity_object->obmat[2]); - } - else { - cache->true_gravity_direction[0] = cache->true_gravity_direction[1] = 0.0; - cache->true_gravity_direction[2] = 1.0; - } - - /* transform to sculpted object space */ - mul_m3_v3(mat, cache->true_gravity_direction); - normalize_v3(cache->true_gravity_direction); - } - - /* Initialize layer brush displacements and persistent coords */ - if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { - /* not supported yet for multires or dynamic topology */ - if (!ss->multires && !ss->bm && !ss->layer_co && - (brush->flag & BRUSH_PERSISTENT)) - { - if (!ss->layer_co) - ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert, - "sculpt mesh vertices copy"); - - if (ss->deform_cos) { - memcpy(ss->layer_co, ss->deform_cos, ss->totvert); - } - else { - for (i = 0; i < ss->totvert; ++i) { - copy_v3_v3(ss->layer_co[i], ss->mvert[i].co); - } - } - } - - if (ss->bm) { - /* Free any remaining layer displacements from nodes. If not and topology changes - * from using another tool, then next layer toolstroke - * can access past disp array bounds */ - BKE_pbvh_free_layer_disp(ss->pbvh); - } - } - - /* Make copies of the mesh vertex locations and normals for some tools */ - if (brush->flag & BRUSH_ANCHORED) { - cache->original = 1; - } - - if (SCULPT_TOOL_HAS_ACCUMULATE(brush->sculpt_tool)) { - if (!(brush->flag & BRUSH_ACCUMULATE)) { - cache->original = 1; - } - } - - cache->first_time = 1; + bContext *C, Sculpt *sd, SculptSession *ss, wmOperator *op, const float mouse[2]) +{ + StrokeCache *cache = MEM_callocN(sizeof(StrokeCache), "stroke cache"); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Brush *brush = BKE_paint_brush(&sd->paint); + ViewContext *vc = paint_stroke_view_context(op->customdata); + Object *ob = CTX_data_active_object(C); + float mat[3][3]; + float viewDir[3] = {0.0f, 0.0f, 1.0f}; + float max_scale; + int i; + int mode; + + ss->cache = cache; + + /* Set scaling adjustment */ + if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { + max_scale = 1.0f; + } + else { + max_scale = 0.0f; + for (i = 0; i < 3; i++) { + max_scale = max_ff(max_scale, fabsf(ob->scale[i])); + } + } + cache->scale[0] = max_scale / ob->scale[0]; + cache->scale[1] = max_scale / ob->scale[1]; + cache->scale[2] = max_scale / ob->scale[2]; + + cache->plane_trim_squared = brush->plane_trim * brush->plane_trim; + + cache->flag = 0; + + sculpt_init_mirror_clipping(ob, ss); + + /* Initial mouse location */ + if (mouse) + copy_v2_v2(cache->initial_mouse, mouse); + else + zero_v2(cache->initial_mouse); + + mode = RNA_enum_get(op->ptr, "mode"); + cache->invert = mode == BRUSH_STROKE_INVERT; + cache->alt_smooth = mode == BRUSH_STROKE_SMOOTH; + cache->normal_weight = brush->normal_weight; + + /* interpret invert as following normal, for grab brushes */ + if (SCULPT_TOOL_HAS_NORMAL_WEIGHT(brush->sculpt_tool)) { + if (cache->invert) { + cache->invert = false; + cache->normal_weight = (cache->normal_weight == 0.0f); + } + } + + /* not very nice, but with current events system implementation + * we can't handle brush appearance inversion hotkey separately (sergey) */ + if (cache->invert) + ups->draw_inverted = true; + else + ups->draw_inverted = false; + + /* Alt-Smooth */ + if (cache->alt_smooth) { + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + cache->saved_mask_brush_tool = brush->mask_tool; + brush->mask_tool = BRUSH_MASK_SMOOTH; + } + else { + Paint *p = &sd->paint; + Brush *br; + int size = BKE_brush_size_get(scene, brush); + + BLI_strncpy(cache->saved_active_brush_name, + brush->id.name + 2, + sizeof(cache->saved_active_brush_name)); + + br = (Brush *)BKE_libblock_find_name(bmain, ID_BR, "Smooth"); + if (br) { + BKE_paint_brush_set(p, br); + brush = br; + cache->saved_smooth_size = BKE_brush_size_get(scene, brush); + BKE_brush_size_set(scene, brush, size); + curvemapping_initialize(brush->curve); + } + } + } + + copy_v2_v2(cache->mouse, cache->initial_mouse); + copy_v2_v2(ups->tex_mouse, cache->initial_mouse); + + /* Truly temporary data that isn't stored in properties */ + + cache->vc = vc; + + cache->brush = brush; + + /* cache projection matrix */ + ED_view3d_ob_project_mat_get(cache->vc->rv3d, ob, cache->projection_mat); + + invert_m4_m4(ob->imat, ob->obmat); + copy_m3_m4(mat, cache->vc->rv3d->viewinv); + mul_m3_v3(mat, viewDir); + copy_m3_m4(mat, ob->imat); + mul_m3_v3(mat, viewDir); + normalize_v3_v3(cache->true_view_normal, viewDir); + + cache->supports_gravity = + (!ELEM(brush->sculpt_tool, SCULPT_TOOL_MASK, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_SIMPLIFY) && + (sd->gravity_factor > 0.0f)); + /* get gravity vector in world space */ + if (cache->supports_gravity) { + if (sd->gravity_object) { + Object *gravity_object = sd->gravity_object; + + copy_v3_v3(cache->true_gravity_direction, gravity_object->obmat[2]); + } + else { + cache->true_gravity_direction[0] = cache->true_gravity_direction[1] = 0.0; + cache->true_gravity_direction[2] = 1.0; + } + + /* transform to sculpted object space */ + mul_m3_v3(mat, cache->true_gravity_direction); + normalize_v3(cache->true_gravity_direction); + } + + /* Initialize layer brush displacements and persistent coords */ + if (brush->sculpt_tool == SCULPT_TOOL_LAYER) { + /* not supported yet for multires or dynamic topology */ + if (!ss->multires && !ss->bm && !ss->layer_co && (brush->flag & BRUSH_PERSISTENT)) { + if (!ss->layer_co) + ss->layer_co = MEM_mallocN(sizeof(float) * 3 * ss->totvert, "sculpt mesh vertices copy"); + + if (ss->deform_cos) { + memcpy(ss->layer_co, ss->deform_cos, ss->totvert); + } + else { + for (i = 0; i < ss->totvert; ++i) { + copy_v3_v3(ss->layer_co[i], ss->mvert[i].co); + } + } + } + + if (ss->bm) { + /* Free any remaining layer displacements from nodes. If not and topology changes + * from using another tool, then next layer toolstroke + * can access past disp array bounds */ + BKE_pbvh_free_layer_disp(ss->pbvh); + } + } + + /* Make copies of the mesh vertex locations and normals for some tools */ + if (brush->flag & BRUSH_ANCHORED) { + cache->original = 1; + } + + if (SCULPT_TOOL_HAS_ACCUMULATE(brush->sculpt_tool)) { + if (!(brush->flag & BRUSH_ACCUMULATE)) { + cache->original = 1; + } + } + + cache->first_time = 1; #define PIXEL_INPUT_THRESHHOLD 5 - if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) - cache->dial = BLI_dial_initialize(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); + if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) + cache->dial = BLI_dial_initialize(cache->initial_mouse, PIXEL_INPUT_THRESHHOLD); #undef PIXEL_INPUT_THRESHHOLD } static void sculpt_update_brush_delta(UnifiedPaintSettings *ups, Object *ob, Brush *brush) { - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - const float mouse[2] = { - cache->mouse[0], - cache->mouse[1], - }; - int tool = brush->sculpt_tool; - - if (ELEM(tool, - SCULPT_TOOL_GRAB, SCULPT_TOOL_NUDGE, - SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_SNAKE_HOOK, - SCULPT_TOOL_THUMB) || - sculpt_brush_use_topology_rake(ss, brush)) - { - float grab_location[3], imat[4][4], delta[3], loc[3]; - - if (cache->first_time) { - copy_v3_v3(cache->orig_grab_location, - cache->true_location); - } - else if (tool == SCULPT_TOOL_SNAKE_HOOK) - add_v3_v3(cache->true_location, cache->grab_delta); - - /* compute 3d coordinate at same z from original location + mouse */ - mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location); - ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->ar, loc, mouse, grab_location); - - /* compute delta to move verts by */ - if (!cache->first_time) { - switch (tool) { - case SCULPT_TOOL_GRAB: - case SCULPT_TOOL_THUMB: - sub_v3_v3v3(delta, grab_location, cache->old_grab_location); - invert_m4_m4(imat, ob->obmat); - mul_mat3_m4_v3(imat, delta); - add_v3_v3(cache->grab_delta, delta); - break; - case SCULPT_TOOL_CLAY_STRIPS: - case SCULPT_TOOL_NUDGE: - case SCULPT_TOOL_SNAKE_HOOK: - if (brush->flag & BRUSH_ANCHORED) { - float orig[3]; - mul_v3_m4v3(orig, ob->obmat, cache->orig_grab_location); - sub_v3_v3v3(cache->grab_delta, grab_location, orig); - } - else { - sub_v3_v3v3(cache->grab_delta, grab_location, - cache->old_grab_location); - } - invert_m4_m4(imat, ob->obmat); - mul_mat3_m4_v3(imat, cache->grab_delta); - break; - default: - /* Use for 'Brush.topology_rake_factor'. */ - sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); - break; - - } - } - else { - zero_v3(cache->grab_delta); - } - - if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { - project_plane_v3_v3v3(cache->grab_delta, cache->grab_delta, ss->cache->true_view_normal); - } - - copy_v3_v3(cache->old_grab_location, grab_location); - - if (tool == SCULPT_TOOL_GRAB) - copy_v3_v3(cache->anchored_location, cache->true_location); - else if (tool == SCULPT_TOOL_THUMB) - copy_v3_v3(cache->anchored_location, cache->orig_grab_location); - - if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { - /* location stays the same for finding vertices in brush radius */ - copy_v3_v3(cache->true_location, cache->orig_grab_location); - - ups->draw_anchored = true; - copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); - ups->anchored_size = ups->pixel_radius; - } - - - /* handle 'rake' */ - cache->is_rake_rotation_valid = false; - - if (cache->first_time) { - copy_v3_v3(cache->rake_data.follow_co, grab_location); - } - - if (sculpt_brush_needs_rake_rotation(brush)) { - cache->rake_data.follow_dist = cache->radius * SCULPT_RAKE_BRUSH_FACTOR; - - if (!is_zero_v3(cache->grab_delta)) { - const float eps = 0.00001f; - - float v1[3], v2[3]; - - copy_v3_v3(v1, cache->rake_data.follow_co); - copy_v3_v3(v2, cache->rake_data.follow_co); - sub_v3_v3(v2, cache->grab_delta); - - sub_v3_v3(v1, grab_location); - sub_v3_v3(v2, grab_location); - - if ((normalize_v3(v2) > eps) && - (normalize_v3(v1) > eps) && - (len_squared_v3v3(v1, v2) > eps)) - { - const float rake_dist_sq = len_squared_v3v3(cache->rake_data.follow_co, grab_location); - const float rake_fade = (rake_dist_sq > SQUARE(cache->rake_data.follow_dist)) ? - 1.0f : sqrtf(rake_dist_sq) / cache->rake_data.follow_dist; - - float axis[3], angle; - float tquat[4]; - - rotation_between_vecs_to_quat(tquat, v1, v2); - - /* use axis-angle to scale rotation since the factor may be above 1 */ - quat_to_axis_angle(axis, &angle, tquat); - normalize_v3(axis); - - angle *= brush->rake_factor * rake_fade; - axis_angle_normalized_to_quat(cache->rake_rotation, axis, angle); - cache->is_rake_rotation_valid = true; - } - } - sculpt_rake_data_update(&cache->rake_data, grab_location); - } - } + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + const float mouse[2] = { + cache->mouse[0], + cache->mouse[1], + }; + int tool = brush->sculpt_tool; + + if (ELEM(tool, + SCULPT_TOOL_GRAB, + SCULPT_TOOL_NUDGE, + SCULPT_TOOL_CLAY_STRIPS, + SCULPT_TOOL_SNAKE_HOOK, + SCULPT_TOOL_THUMB) || + sculpt_brush_use_topology_rake(ss, brush)) { + float grab_location[3], imat[4][4], delta[3], loc[3]; + + if (cache->first_time) { + copy_v3_v3(cache->orig_grab_location, cache->true_location); + } + else if (tool == SCULPT_TOOL_SNAKE_HOOK) + add_v3_v3(cache->true_location, cache->grab_delta); + + /* compute 3d coordinate at same z from original location + mouse */ + mul_v3_m4v3(loc, ob->obmat, cache->orig_grab_location); + ED_view3d_win_to_3d(cache->vc->v3d, cache->vc->ar, loc, mouse, grab_location); + + /* compute delta to move verts by */ + if (!cache->first_time) { + switch (tool) { + case SCULPT_TOOL_GRAB: + case SCULPT_TOOL_THUMB: + sub_v3_v3v3(delta, grab_location, cache->old_grab_location); + invert_m4_m4(imat, ob->obmat); + mul_mat3_m4_v3(imat, delta); + add_v3_v3(cache->grab_delta, delta); + break; + case SCULPT_TOOL_CLAY_STRIPS: + case SCULPT_TOOL_NUDGE: + case SCULPT_TOOL_SNAKE_HOOK: + if (brush->flag & BRUSH_ANCHORED) { + float orig[3]; + mul_v3_m4v3(orig, ob->obmat, cache->orig_grab_location); + sub_v3_v3v3(cache->grab_delta, grab_location, orig); + } + else { + sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); + } + invert_m4_m4(imat, ob->obmat); + mul_mat3_m4_v3(imat, cache->grab_delta); + break; + default: + /* Use for 'Brush.topology_rake_factor'. */ + sub_v3_v3v3(cache->grab_delta, grab_location, cache->old_grab_location); + break; + } + } + else { + zero_v3(cache->grab_delta); + } + + if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) { + project_plane_v3_v3v3(cache->grab_delta, cache->grab_delta, ss->cache->true_view_normal); + } + + copy_v3_v3(cache->old_grab_location, grab_location); + + if (tool == SCULPT_TOOL_GRAB) + copy_v3_v3(cache->anchored_location, cache->true_location); + else if (tool == SCULPT_TOOL_THUMB) + copy_v3_v3(cache->anchored_location, cache->orig_grab_location); + + if (ELEM(tool, SCULPT_TOOL_GRAB, SCULPT_TOOL_THUMB)) { + /* location stays the same for finding vertices in brush radius */ + copy_v3_v3(cache->true_location, cache->orig_grab_location); + + ups->draw_anchored = true; + copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); + ups->anchored_size = ups->pixel_radius; + } + + /* handle 'rake' */ + cache->is_rake_rotation_valid = false; + + if (cache->first_time) { + copy_v3_v3(cache->rake_data.follow_co, grab_location); + } + + if (sculpt_brush_needs_rake_rotation(brush)) { + cache->rake_data.follow_dist = cache->radius * SCULPT_RAKE_BRUSH_FACTOR; + + if (!is_zero_v3(cache->grab_delta)) { + const float eps = 0.00001f; + + float v1[3], v2[3]; + + copy_v3_v3(v1, cache->rake_data.follow_co); + copy_v3_v3(v2, cache->rake_data.follow_co); + sub_v3_v3(v2, cache->grab_delta); + + sub_v3_v3(v1, grab_location); + sub_v3_v3(v2, grab_location); + + if ((normalize_v3(v2) > eps) && (normalize_v3(v1) > eps) && + (len_squared_v3v3(v1, v2) > eps)) { + const float rake_dist_sq = len_squared_v3v3(cache->rake_data.follow_co, grab_location); + const float rake_fade = (rake_dist_sq > SQUARE(cache->rake_data.follow_dist)) ? + 1.0f : + sqrtf(rake_dist_sq) / cache->rake_data.follow_dist; + + float axis[3], angle; + float tquat[4]; + + rotation_between_vecs_to_quat(tquat, v1, v2); + + /* use axis-angle to scale rotation since the factor may be above 1 */ + quat_to_axis_angle(axis, &angle, tquat); + normalize_v3(axis); + + angle *= brush->rake_factor * rake_fade; + axis_angle_normalized_to_quat(cache->rake_rotation, axis, angle); + cache->is_rake_rotation_valid = true; + } + } + sculpt_rake_data_update(&cache->rake_data, grab_location); + } + } } /* Initialize the stroke cache variants from operator properties */ -static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, - PointerRNA *ptr) -{ - Scene *scene = CTX_data_scene(C); - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - SculptSession *ss = ob->sculpt; - StrokeCache *cache = ss->cache; - Brush *brush = BKE_paint_brush(&sd->paint); - - /* RNA_float_get_array(ptr, "location", cache->traced_location); */ - - if (cache->first_time || - !((brush->flag & BRUSH_ANCHORED) || - (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) || - (brush->sculpt_tool == SCULPT_TOOL_ROTATE)) - ) - { - RNA_float_get_array(ptr, "location", cache->true_location); - } - - cache->pen_flip = RNA_boolean_get(ptr, "pen_flip"); - RNA_float_get_array(ptr, "mouse", cache->mouse); - - /* XXX: Use pressure value from first brush step for brushes which don't - * support strokes (grab, thumb). They depends on initial state and - * brush coord/pressure/etc. - * It's more an events design issue, which doesn't split coordinate/pressure/angle - * changing events. We should avoid this after events system re-design */ - if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { - cache->pressure = RNA_float_get(ptr, "pressure"); - } - - /* Truly temporary data that isn't stored in properties */ - if (cache->first_time) { - if (!BKE_brush_use_locked_size(scene, brush)) { - cache->initial_radius = paint_calc_object_space_radius(cache->vc, - cache->true_location, - BKE_brush_size_get(scene, brush)); - BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); - } - else { - cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); - } - } - - if (BKE_brush_use_size_pressure(scene, brush) && paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) { - cache->radius = cache->initial_radius * cache->pressure; - } - else { - cache->radius = cache->initial_radius; - } - - cache->radius_squared = cache->radius * cache->radius; - - if (brush->flag & BRUSH_ANCHORED) { - /* true location has been calculated as part of the stroke system already here */ - if (brush->flag & BRUSH_EDGE_TO_EDGE) { - RNA_float_get_array(ptr, "location", cache->true_location); - } - - cache->radius = paint_calc_object_space_radius(cache->vc, - cache->true_location, - ups->pixel_radius); - cache->radius_squared = cache->radius * cache->radius; - - copy_v3_v3(cache->anchored_location, cache->true_location); - } - - sculpt_update_brush_delta(ups, ob, brush); - - if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { - cache->vertex_rotation = -BLI_dial_angle(cache->dial, cache->mouse) * cache->bstrength; - - ups->draw_anchored = true; - copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); - copy_v3_v3(cache->anchored_location, cache->true_location); - ups->anchored_size = ups->pixel_radius; - } - - cache->special_rotation = ups->brush_rotation; +static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, PointerRNA *ptr) +{ + Scene *scene = CTX_data_scene(C); + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + SculptSession *ss = ob->sculpt; + StrokeCache *cache = ss->cache; + Brush *brush = BKE_paint_brush(&sd->paint); + + /* RNA_float_get_array(ptr, "location", cache->traced_location); */ + + if (cache->first_time || + !((brush->flag & BRUSH_ANCHORED) || (brush->sculpt_tool == SCULPT_TOOL_SNAKE_HOOK) || + (brush->sculpt_tool == SCULPT_TOOL_ROTATE))) { + RNA_float_get_array(ptr, "location", cache->true_location); + } + + cache->pen_flip = RNA_boolean_get(ptr, "pen_flip"); + RNA_float_get_array(ptr, "mouse", cache->mouse); + + /* XXX: Use pressure value from first brush step for brushes which don't + * support strokes (grab, thumb). They depends on initial state and + * brush coord/pressure/etc. + * It's more an events design issue, which doesn't split coordinate/pressure/angle + * changing events. We should avoid this after events system re-design */ + if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { + cache->pressure = RNA_float_get(ptr, "pressure"); + } + + /* Truly temporary data that isn't stored in properties */ + if (cache->first_time) { + if (!BKE_brush_use_locked_size(scene, brush)) { + cache->initial_radius = paint_calc_object_space_radius( + cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); + BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); + } + else { + cache->initial_radius = BKE_brush_unprojected_radius_get(scene, brush); + } + } + + if (BKE_brush_use_size_pressure(scene, brush) && + paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT)) { + cache->radius = cache->initial_radius * cache->pressure; + } + else { + cache->radius = cache->initial_radius; + } + + cache->radius_squared = cache->radius * cache->radius; + + if (brush->flag & BRUSH_ANCHORED) { + /* true location has been calculated as part of the stroke system already here */ + if (brush->flag & BRUSH_EDGE_TO_EDGE) { + RNA_float_get_array(ptr, "location", cache->true_location); + } + + cache->radius = paint_calc_object_space_radius( + cache->vc, cache->true_location, ups->pixel_radius); + cache->radius_squared = cache->radius * cache->radius; + + copy_v3_v3(cache->anchored_location, cache->true_location); + } + + sculpt_update_brush_delta(ups, ob, brush); + + if (brush->sculpt_tool == SCULPT_TOOL_ROTATE) { + cache->vertex_rotation = -BLI_dial_angle(cache->dial, cache->mouse) * cache->bstrength; + + ups->draw_anchored = true; + copy_v2_v2(ups->anchored_initial_mouse, cache->initial_mouse); + copy_v3_v3(cache->anchored_location, cache->true_location); + ups->anchored_size = ups->pixel_radius; + } + + cache->special_rotation = ups->brush_rotation; } /* Returns true if any of the smoothing modes are active (currently * one of smooth brush, autosmooth, mask smooth, or shift-key * smooth) */ -static bool sculpt_any_smooth_mode(const Brush *brush, - StrokeCache *cache, - int stroke_mode) +static bool sculpt_any_smooth_mode(const Brush *brush, StrokeCache *cache, int stroke_mode) { - return ((stroke_mode == BRUSH_STROKE_SMOOTH) || - (cache && cache->alt_smooth) || - (brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || - (brush->autosmooth_factor > 0) || - ((brush->sculpt_tool == SCULPT_TOOL_MASK) && - (brush->mask_tool == BRUSH_MASK_SMOOTH))); + return ((stroke_mode == BRUSH_STROKE_SMOOTH) || (cache && cache->alt_smooth) || + (brush->sculpt_tool == SCULPT_TOOL_SMOOTH) || (brush->autosmooth_factor > 0) || + ((brush->sculpt_tool == SCULPT_TOOL_MASK) && (brush->mask_tool == BRUSH_MASK_SMOOTH))); } static void sculpt_stroke_modifiers_check(const bContext *C, Object *ob, const Brush *brush) { - SculptSession *ss = ob->sculpt; + SculptSession *ss = ob->sculpt; - if (ss->kb || ss->modifiers_active) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Sculpt *sd = scene->toolsettings->sculpt; - bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0); - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, need_pmap, false); - } + if (ss->kb || ss->modifiers_active) { + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Sculpt *sd = scene->toolsettings->sculpt; + bool need_pmap = sculpt_any_smooth_mode(brush, ss->cache, 0); + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, need_pmap, false); + } } static void sculpt_raycast_cb(PBVHNode *node, void *data_v, float *tmin) { - if (BKE_pbvh_node_get_tmin(node) < *tmin) { - SculptRaycastData *srd = data_v; - float (*origco)[3] = NULL; - bool use_origco = false; - - if (srd->original && srd->ss->cache) { - if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { - use_origco = true; - } - else { - /* intersect with coordinates from before we started stroke */ - SculptUndoNode *unode = sculpt_undo_get_node(node); - origco = (unode) ? unode->co : NULL; - use_origco = origco ? true : false; - } - } - - if (BKE_pbvh_node_raycast(srd->ss->pbvh, node, origco, use_origco, - srd->ray_start, srd->ray_normal, &srd->depth)) - { - srd->hit = 1; - *tmin = srd->depth; - } - } + if (BKE_pbvh_node_get_tmin(node) < *tmin) { + SculptRaycastData *srd = data_v; + float(*origco)[3] = NULL; + bool use_origco = false; + + if (srd->original && srd->ss->cache) { + if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { + use_origco = true; + } + else { + /* intersect with coordinates from before we started stroke */ + SculptUndoNode *unode = sculpt_undo_get_node(node); + origco = (unode) ? unode->co : NULL; + use_origco = origco ? true : false; + } + } + + if (BKE_pbvh_node_raycast(srd->ss->pbvh, + node, + origco, + use_origco, + srd->ray_start, + srd->ray_normal, + &srd->depth)) { + srd->hit = 1; + *tmin = srd->depth; + } + } } static void sculpt_find_nearest_to_ray_cb(PBVHNode *node, void *data_v, float *tmin) { - if (BKE_pbvh_node_get_tmin(node) < *tmin) { - SculptFindNearestToRayData *srd = data_v; - float (*origco)[3] = NULL; - bool use_origco = false; - - if (srd->original && srd->ss->cache) { - if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { - use_origco = true; - } - else { - /* intersect with coordinates from before we started stroke */ - SculptUndoNode *unode = sculpt_undo_get_node(node); - origco = (unode) ? unode->co : NULL; - use_origco = origco ? true : false; - } - } - - if (BKE_pbvh_node_find_nearest_to_ray( - srd->ss->pbvh, node, origco, use_origco, - srd->ray_start, srd->ray_normal, - &srd->depth, &srd->dist_sq_to_ray)) - { - srd->hit = 1; - *tmin = srd->dist_sq_to_ray; - } - } + if (BKE_pbvh_node_get_tmin(node) < *tmin) { + SculptFindNearestToRayData *srd = data_v; + float(*origco)[3] = NULL; + bool use_origco = false; + + if (srd->original && srd->ss->cache) { + if (BKE_pbvh_type(srd->ss->pbvh) == PBVH_BMESH) { + use_origco = true; + } + else { + /* intersect with coordinates from before we started stroke */ + SculptUndoNode *unode = sculpt_undo_get_node(node); + origco = (unode) ? unode->co : NULL; + use_origco = origco ? true : false; + } + } + + if (BKE_pbvh_node_find_nearest_to_ray(srd->ss->pbvh, + node, + origco, + use_origco, + srd->ray_start, + srd->ray_normal, + &srd->depth, + &srd->dist_sq_to_ray)) { + srd->hit = 1; + *tmin = srd->dist_sq_to_ray; + } + } } static void sculpt_raycast_detail_cb(PBVHNode *node, void *data_v, float *tmin) { - if (BKE_pbvh_node_get_tmin(node) < *tmin) { - SculptDetailRaycastData *srd = data_v; - if (BKE_pbvh_bmesh_node_raycast_detail(node, srd->ray_start, srd->ray_normal, - &srd->depth, &srd->edge_length)) - { - srd->hit = 1; - *tmin = srd->depth; - } - } + if (BKE_pbvh_node_get_tmin(node) < *tmin) { + SculptDetailRaycastData *srd = data_v; + if (BKE_pbvh_bmesh_node_raycast_detail( + node, srd->ray_start, srd->ray_normal, &srd->depth, &srd->edge_length)) { + srd->hit = 1; + *tmin = srd->depth; + } + } } -static float sculpt_raycast_init( - ViewContext *vc, const float mouse[2], - float ray_start[3], float ray_end[3], float ray_normal[3], bool original) +static float sculpt_raycast_init(ViewContext *vc, + const float mouse[2], + float ray_start[3], + float ray_end[3], + float ray_normal[3], + bool original) { - float obimat[4][4]; - float dist; - Object *ob = vc->obact; - RegionView3D *rv3d = vc->ar->regiondata; + float obimat[4][4]; + float dist; + Object *ob = vc->obact; + RegionView3D *rv3d = vc->ar->regiondata; - /* TODO: what if the segment is totally clipped? (return == 0) */ - ED_view3d_win_to_segment_clipped(vc->depsgraph, vc->ar, vc->v3d, mouse, ray_start, ray_end, true); + /* TODO: what if the segment is totally clipped? (return == 0) */ + ED_view3d_win_to_segment_clipped( + vc->depsgraph, vc->ar, vc->v3d, mouse, ray_start, ray_end, true); - invert_m4_m4(obimat, ob->obmat); - mul_m4_v3(obimat, ray_start); - mul_m4_v3(obimat, ray_end); + invert_m4_m4(obimat, ob->obmat); + mul_m4_v3(obimat, ray_start); + mul_m4_v3(obimat, ray_end); - sub_v3_v3v3(ray_normal, ray_end, ray_start); - dist = normalize_v3(ray_normal); + sub_v3_v3v3(ray_normal, ray_end, ray_start); + dist = normalize_v3(ray_normal); - if ((rv3d->is_persp == false) && - /* if the ray is clipped, don't adjust its start/end */ - ((rv3d->rflag & RV3D_CLIPPING) == 0)) - { - BKE_pbvh_raycast_project_ray_root(ob->sculpt->pbvh, original, ray_start, ray_end, ray_normal); + if ((rv3d->is_persp == false) && + /* if the ray is clipped, don't adjust its start/end */ + ((rv3d->rflag & RV3D_CLIPPING) == 0)) { + BKE_pbvh_raycast_project_ray_root(ob->sculpt->pbvh, original, ray_start, ray_end, ray_normal); - /* recalculate the normal */ - sub_v3_v3v3(ray_normal, ray_end, ray_start); - dist = normalize_v3(ray_normal); - } + /* recalculate the normal */ + sub_v3_v3v3(ray_normal, ray_end, ray_start); + dist = normalize_v3(ray_normal); + } - return dist; + return dist; } /* Do a raycast in the tree to find the 3d brush location @@ -4916,859 +4942,887 @@ static float sculpt_raycast_init( */ bool sculpt_stroke_get_location(bContext *C, float out[3], const float mouse[2]) { - Object *ob; - SculptSession *ss; - StrokeCache *cache; - float ray_start[3], ray_end[3], ray_normal[3], depth; - bool original; - ViewContext vc; - - ED_view3d_viewcontext_init(C, &vc); - - ob = vc.obact; - - ss = ob->sculpt; - cache = ss->cache; - original = (cache) ? cache->original : 0; - - const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); - - sculpt_stroke_modifiers_check(C, ob, brush); - - depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); - - bool hit = false; - { - SculptRaycastData srd = { - .original = original, - .ss = ob->sculpt, - .hit = 0, - .ray_start = ray_start, - .ray_normal = ray_normal, - .depth = depth, - }; - BKE_pbvh_raycast( - ss->pbvh, sculpt_raycast_cb, &srd, - ray_start, ray_normal, srd.original); - if (srd.hit) { - hit = true; - copy_v3_v3(out, ray_normal); - mul_v3_fl(out, srd.depth); - add_v3_v3(out, ray_start); - } - } - - if (hit == false) { - if (ELEM(brush->falloff_shape, PAINT_FALLOFF_SHAPE_TUBE)) { - SculptFindNearestToRayData srd = { - .original = original, - .ss = ob->sculpt, - .hit = 0, - .ray_start = ray_start, - .ray_normal = ray_normal, - .depth = FLT_MAX, - .dist_sq_to_ray = FLT_MAX, - }; - BKE_pbvh_find_nearest_to_ray( - ss->pbvh, sculpt_find_nearest_to_ray_cb, &srd, - ray_start, ray_normal, srd.original); - if (srd.hit) { - hit = true; - copy_v3_v3(out, ray_normal); - mul_v3_fl(out, srd.depth); - add_v3_v3(out, ray_start); - } - } - } - - if (cache && hit) { - copy_v3_v3(cache->true_location, out); - } - - return hit; + Object *ob; + SculptSession *ss; + StrokeCache *cache; + float ray_start[3], ray_end[3], ray_normal[3], depth; + bool original; + ViewContext vc; + + ED_view3d_viewcontext_init(C, &vc); + + ob = vc.obact; + + ss = ob->sculpt; + cache = ss->cache; + original = (cache) ? cache->original : 0; + + const Brush *brush = BKE_paint_brush(BKE_paint_get_active_from_context(C)); + + sculpt_stroke_modifiers_check(C, ob, brush); + + depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, original); + + bool hit = false; + { + SculptRaycastData srd = { + .original = original, + .ss = ob->sculpt, + .hit = 0, + .ray_start = ray_start, + .ray_normal = ray_normal, + .depth = depth, + }; + BKE_pbvh_raycast(ss->pbvh, sculpt_raycast_cb, &srd, ray_start, ray_normal, srd.original); + if (srd.hit) { + hit = true; + copy_v3_v3(out, ray_normal); + mul_v3_fl(out, srd.depth); + add_v3_v3(out, ray_start); + } + } + + if (hit == false) { + if (ELEM(brush->falloff_shape, PAINT_FALLOFF_SHAPE_TUBE)) { + SculptFindNearestToRayData srd = { + .original = original, + .ss = ob->sculpt, + .hit = 0, + .ray_start = ray_start, + .ray_normal = ray_normal, + .depth = FLT_MAX, + .dist_sq_to_ray = FLT_MAX, + }; + BKE_pbvh_find_nearest_to_ray( + ss->pbvh, sculpt_find_nearest_to_ray_cb, &srd, ray_start, ray_normal, srd.original); + if (srd.hit) { + hit = true; + copy_v3_v3(out, ray_normal); + mul_v3_fl(out, srd.depth); + add_v3_v3(out, ray_start); + } + } + } + + if (cache && hit) { + copy_v3_v3(cache->true_location, out); + } + + return hit; } static void sculpt_brush_init_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) { - Brush *brush = BKE_paint_brush(&sd->paint); - MTex *mtex = &brush->mtex; + Brush *brush = BKE_paint_brush(&sd->paint); + MTex *mtex = &brush->mtex; - /* init mtex nodes */ - if (mtex->tex && mtex->tex->nodetree) { - /* has internal flag to detect it only does it once */ - ntreeTexBeginExecTree(mtex->tex->nodetree); - } + /* init mtex nodes */ + if (mtex->tex && mtex->tex->nodetree) { + /* has internal flag to detect it only does it once */ + ntreeTexBeginExecTree(mtex->tex->nodetree); + } - /* TODO: Shouldn't really have to do this at the start of every - * stroke, but sculpt would need some sort of notification when - * changes are made to the texture. */ - sculpt_update_tex(scene, sd, ss); + /* TODO: Shouldn't really have to do this at the start of every + * stroke, but sculpt would need some sort of notification when + * changes are made to the texture. */ + sculpt_update_tex(scene, sd, ss); } static void sculpt_brush_stroke_init(bContext *C, wmOperator *op) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - SculptSession *ss = CTX_data_active_object(C)->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); - int mode = RNA_enum_get(op->ptr, "mode"); - bool is_smooth; - bool need_mask = false; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = CTX_data_active_object(C)->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); + int mode = RNA_enum_get(op->ptr, "mode"); + bool is_smooth; + bool need_mask = false; - if (brush->sculpt_tool == SCULPT_TOOL_MASK) { - need_mask = true; - } + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + need_mask = true; + } - view3d_operator_needs_opengl(C); - sculpt_brush_init_tex(scene, sd, ss); + view3d_operator_needs_opengl(C); + sculpt_brush_init_tex(scene, sd, ss); - is_smooth = sculpt_any_smooth_mode(brush, NULL, mode); - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, is_smooth, need_mask); + is_smooth = sculpt_any_smooth_mode(brush, NULL, mode); + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, is_smooth, need_mask); } static void sculpt_restore_mesh(Sculpt *sd, Object *ob) { - SculptSession *ss = ob->sculpt; - Brush *brush = BKE_paint_brush(&sd->paint); + SculptSession *ss = ob->sculpt; + Brush *brush = BKE_paint_brush(&sd->paint); - /* Restore the mesh before continuing with anchored stroke */ - if ((brush->flag & BRUSH_ANCHORED) || - (brush->sculpt_tool == SCULPT_TOOL_GRAB && - BKE_brush_use_size_pressure(ss->cache->vc->scene, brush)) || - (brush->flag & BRUSH_DRAG_DOT)) - { - paint_mesh_restore_co(sd, ob); - } + /* Restore the mesh before continuing with anchored stroke */ + if ((brush->flag & BRUSH_ANCHORED) || + (brush->sculpt_tool == SCULPT_TOOL_GRAB && + BKE_brush_use_size_pressure(ss->cache->vc->scene, brush)) || + (brush->flag & BRUSH_DRAG_DOT)) { + paint_mesh_restore_co(sd, ob); + } } /* Copy the PBVH bounding box into the object's bounding box */ void sculpt_update_object_bounding_box(Object *ob) { - if (ob->runtime.bb) { - float bb_min[3], bb_max[3]; + if (ob->runtime.bb) { + float bb_min[3], bb_max[3]; - BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); - BKE_boundbox_init_from_minmax(ob->runtime.bb, bb_min, bb_max); - } + BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); + BKE_boundbox_init_from_minmax(ob->runtime.bb, bb_min, bb_max); + } } static void sculpt_flush_update(bContext *C) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Object *ob = CTX_data_active_object(C); - Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); - SculptSession *ss = ob->sculpt; - ARegion *ar = CTX_wm_region(C); - bScreen *screen = CTX_wm_screen(C); - MultiresModifierData *mmd = ss->multires; - - if (mmd != NULL) { - /* NOTE: SubdivCCG is living in the evaluated object. */ - multires_mark_as_modified(ob_eval, MULTIRES_COORDS_MODIFIED); - } - - DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); - - bool use_shaded_mode = false; - if (mmd || (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH)) { - /* Multres or dyntopo are drawn directly by EEVEE, - * no need for hacks in this case. */ - } - else { - /* We search if an area of the current window is in lookdev/rendered - * display mode. In this case, for changes to show up, we need to - * tag for ID_RECALC_GEOMETRY. */ - for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { - for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { - if (sl->spacetype == SPACE_VIEW3D) { - View3D *v3d = (View3D *)sl; - if (v3d->shading.type > OB_SOLID) { - use_shaded_mode = true; - } - } - } - } - } - - if (ss->kb || ss->modifiers_active || use_shaded_mode) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - ED_region_tag_redraw(ar); - } - else { - rcti r; - - BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); - /* Update the object's bounding box too so that the object - * doesn't get incorrectly clipped during drawing in - * draw_mesh_object(). [#33790] */ - sculpt_update_object_bounding_box(ob); - - if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { - if (ss->cache) { - ss->cache->current_r = r; - } - - /* previous is not set in the current cache else - * the partial rect will always grow */ - sculpt_extend_redraw_rect_previous(ob, &r); - - r.xmin += ar->winrct.xmin - 2; - r.xmax += ar->winrct.xmin + 2; - r.ymin += ar->winrct.ymin - 2; - r.ymax += ar->winrct.ymin + 2; - - ss->partial_redraw = 1; - ED_region_tag_redraw_partial(ar, &r); - } - } + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Object *ob = CTX_data_active_object(C); + Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob); + SculptSession *ss = ob->sculpt; + ARegion *ar = CTX_wm_region(C); + bScreen *screen = CTX_wm_screen(C); + MultiresModifierData *mmd = ss->multires; + + if (mmd != NULL) { + /* NOTE: SubdivCCG is living in the evaluated object. */ + multires_mark_as_modified(ob_eval, MULTIRES_COORDS_MODIFIED); + } + + DEG_id_tag_update(&ob->id, ID_RECALC_SHADING); + + bool use_shaded_mode = false; + if (mmd || (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH)) { + /* Multres or dyntopo are drawn directly by EEVEE, + * no need for hacks in this case. */ + } + else { + /* We search if an area of the current window is in lookdev/rendered + * display mode. In this case, for changes to show up, we need to + * tag for ID_RECALC_GEOMETRY. */ + for (ScrArea *sa = screen->areabase.first; sa; sa = sa->next) { + for (SpaceLink *sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_VIEW3D) { + View3D *v3d = (View3D *)sl; + if (v3d->shading.type > OB_SOLID) { + use_shaded_mode = true; + } + } + } + } + } + + if (ss->kb || ss->modifiers_active || use_shaded_mode) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + ED_region_tag_redraw(ar); + } + else { + rcti r; + + BKE_pbvh_update(ss->pbvh, PBVH_UpdateBB, NULL); + /* Update the object's bounding box too so that the object + * doesn't get incorrectly clipped during drawing in + * draw_mesh_object(). [#33790] */ + sculpt_update_object_bounding_box(ob); + + if (sculpt_get_redraw_rect(ar, CTX_wm_region_view3d(C), ob, &r)) { + if (ss->cache) { + ss->cache->current_r = r; + } + + /* previous is not set in the current cache else + * the partial rect will always grow */ + sculpt_extend_redraw_rect_previous(ob, &r); + + r.xmin += ar->winrct.xmin - 2; + r.xmax += ar->winrct.xmin + 2; + r.ymin += ar->winrct.ymin - 2; + r.ymax += ar->winrct.ymin + 2; + + ss->partial_redraw = 1; + ED_region_tag_redraw_partial(ar, &r); + } + } } /* Returns whether the mouse/stylus is over the mesh (1) * or over the background (0) */ static bool over_mesh(bContext *C, struct wmOperator *UNUSED(op), float x, float y) { - float mouse[2], co[3]; + float mouse[2], co[3]; - mouse[0] = x; - mouse[1] = y; + mouse[0] = x; + mouse[1] = y; - return sculpt_stroke_get_location(C, co, mouse); + return sculpt_stroke_get_location(C, co, mouse); } -static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, - const float mouse[2]) +static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const float mouse[2]) { - /* Don't start the stroke until mouse goes over the mesh. - * note: mouse will only be null when re-executing the saved stroke. - * We have exception for 'exec' strokes since they may not set 'mouse', - * only 'location', see: T52195. */ - if (((op->flag & OP_IS_INVOKE) == 0) || - (mouse == NULL) || over_mesh(C, op, mouse[0], mouse[1])) - { - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + /* Don't start the stroke until mouse goes over the mesh. + * note: mouse will only be null when re-executing the saved stroke. + * We have exception for 'exec' strokes since they may not set 'mouse', + * only 'location', see: T52195. */ + if (((op->flag & OP_IS_INVOKE) == 0) || (mouse == NULL) || + over_mesh(C, op, mouse[0], mouse[1])) { + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); + ED_view3d_init_mats_rv3d(ob, CTX_wm_region_view3d(C)); - sculpt_update_cache_invariants(C, sd, ss, op, mouse); + sculpt_update_cache_invariants(C, sd, ss, op, mouse); - sculpt_undo_push_begin(sculpt_tool_name(sd)); + sculpt_undo_push_begin(sculpt_tool_name(sd)); - return 1; - } - else - return 0; + return 1; + } + else + return 0; } -static void sculpt_stroke_update_step(bContext *C, struct PaintStroke *UNUSED(stroke), PointerRNA *itemptr) +static void sculpt_stroke_update_step(bContext *C, + struct PaintStroke *UNUSED(stroke), + PointerRNA *itemptr) { - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - const Brush *brush = BKE_paint_brush(&sd->paint); + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + const Brush *brush = BKE_paint_brush(&sd->paint); - sculpt_stroke_modifiers_check(C, ob, brush); - sculpt_update_cache_variants(C, sd, ob, itemptr); - sculpt_restore_mesh(sd, ob); + sculpt_stroke_modifiers_check(C, ob, brush); + sculpt_update_cache_variants(C, sd, ob, itemptr); + sculpt_restore_mesh(sd, ob); - if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); - } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); - } - else { - BKE_pbvh_bmesh_detail_size_set( - ss->pbvh, - (ss->cache->radius / - (float)ups->pixel_radius) * - (float)(sd->detail_size * U.pixelsize) / 0.4f); - } + if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { + float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, ss->cache->radius * sd->detail_percent / 100.0f); + } + else { + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, + (ss->cache->radius / (float)ups->pixel_radius) * + (float)(sd->detail_size * U.pixelsize) / 0.4f); + } - if (sculpt_stroke_is_dynamic_topology(ss, brush)) { - do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); - } + if (sculpt_stroke_is_dynamic_topology(ss, brush)) { + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); + } - do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); + do_symmetrical_brush_actions(sd, ob, do_brush_action, ups); - sculpt_combine_proxies(sd, ob); + sculpt_combine_proxies(sd, ob); - /* hack to fix noise texture tearing mesh */ - sculpt_fix_noise_tear(sd, ob); + /* hack to fix noise texture tearing mesh */ + sculpt_fix_noise_tear(sd, ob); - /* TODO(sergey): This is not really needed for the solid shading, - * which does use pBVH drawing anyway, but texture and wireframe - * requires this. - * - * Could be optimized later, but currently don't think it's so - * much common scenario. - * - * Same applies to the DEG_id_tag_update() invoked from - * sculpt_flush_update(). - */ - if (ss->modifiers_active) { - sculpt_flush_stroke_deform(sd, ob); - } - else if (ss->kb) { - sculpt_update_keyblock(ob); - } + /* TODO(sergey): This is not really needed for the solid shading, + * which does use pBVH drawing anyway, but texture and wireframe + * requires this. + * + * Could be optimized later, but currently don't think it's so + * much common scenario. + * + * Same applies to the DEG_id_tag_update() invoked from + * sculpt_flush_update(). + */ + if (ss->modifiers_active) { + sculpt_flush_stroke_deform(sd, ob); + } + else if (ss->kb) { + sculpt_update_keyblock(ob); + } - ss->cache->first_time = false; + ss->cache->first_time = false; - /* Cleanup */ - sculpt_flush_update(C); + /* Cleanup */ + sculpt_flush_update(C); } static void sculpt_brush_exit_tex(Sculpt *sd) { - Brush *brush = BKE_paint_brush(&sd->paint); - MTex *mtex = &brush->mtex; + Brush *brush = BKE_paint_brush(&sd->paint); + MTex *mtex = &brush->mtex; - if (mtex->tex && mtex->tex->nodetree) - ntreeTexEndExecTree(mtex->tex->nodetree->execdata); + if (mtex->tex && mtex->tex->nodetree) + ntreeTexEndExecTree(mtex->tex->nodetree->execdata); } static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(stroke)) { - Main *bmain = CTX_data_main(C); - Object *ob = CTX_data_active_object(C); - Scene *scene = CTX_data_scene(C); - SculptSession *ss = ob->sculpt; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Main *bmain = CTX_data_main(C); + Object *ob = CTX_data_active_object(C); + Scene *scene = CTX_data_scene(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - /* Finished */ - if (ss->cache) { - UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - Brush *brush = BKE_paint_brush(&sd->paint); - BLI_assert(brush == ss->cache->brush); /* const, so we shouldn't change. */ - ups->draw_inverted = false; + /* Finished */ + if (ss->cache) { + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Brush *brush = BKE_paint_brush(&sd->paint); + BLI_assert(brush == ss->cache->brush); /* const, so we shouldn't change. */ + ups->draw_inverted = false; - sculpt_stroke_modifiers_check(C, ob, brush); + sculpt_stroke_modifiers_check(C, ob, brush); - /* Alt-Smooth */ - if (ss->cache->alt_smooth) { - if (brush->sculpt_tool == SCULPT_TOOL_MASK) { - brush->mask_tool = ss->cache->saved_mask_brush_tool; - } - else { - BKE_brush_size_set(scene, brush, ss->cache->saved_smooth_size); - brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, ss->cache->saved_active_brush_name); - if (brush) { - BKE_paint_brush_set(&sd->paint, brush); - } - } - } + /* Alt-Smooth */ + if (ss->cache->alt_smooth) { + if (brush->sculpt_tool == SCULPT_TOOL_MASK) { + brush->mask_tool = ss->cache->saved_mask_brush_tool; + } + else { + BKE_brush_size_set(scene, brush, ss->cache->saved_smooth_size); + brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, ss->cache->saved_active_brush_name); + if (brush) { + BKE_paint_brush_set(&sd->paint, brush); + } + } + } - sculpt_cache_free(ss->cache); - ss->cache = NULL; + sculpt_cache_free(ss->cache); + ss->cache = NULL; - sculpt_undo_push_end(); + sculpt_undo_push_end(); - BKE_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL); + BKE_pbvh_update(ss->pbvh, PBVH_UpdateOriginalBB, NULL); - if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) - BKE_pbvh_bmesh_after_stroke(ss->pbvh); + if (BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) + BKE_pbvh_bmesh_after_stroke(ss->pbvh); - /* optimization: if there is locked key and active modifiers present in */ - /* the stack, keyblock is updating at each step. otherwise we could update */ - /* keyblock only when stroke is finished */ - if (ss->kb && !ss->modifiers_active) sculpt_update_keyblock(ob); + /* optimization: if there is locked key and active modifiers present in */ + /* the stack, keyblock is updating at each step. otherwise we could update */ + /* keyblock only when stroke is finished */ + if (ss->kb && !ss->modifiers_active) + sculpt_update_keyblock(ob); - ss->partial_redraw = 0; + ss->partial_redraw = 0; - /* try to avoid calling this, only for e.g. linked duplicates now */ - if (((Mesh *)ob->data)->id.us > 1) - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + /* try to avoid calling this, only for e.g. linked duplicates now */ + if (((Mesh *)ob->data)->id.us > 1) + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - } + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + } - sculpt_brush_exit_tex(sd); + sculpt_brush_exit_tex(sd); } static int sculpt_brush_stroke_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - struct PaintStroke *stroke; - int ignore_background_click; - int retval; + struct PaintStroke *stroke; + int ignore_background_click; + int retval; - sculpt_brush_stroke_init(C, op); + sculpt_brush_stroke_init(C, op); - stroke = paint_stroke_new(C, op, sculpt_stroke_get_location, - sculpt_stroke_test_start, - sculpt_stroke_update_step, NULL, - sculpt_stroke_done, event->type); + stroke = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + sculpt_stroke_test_start, + sculpt_stroke_update_step, + NULL, + sculpt_stroke_done, + event->type); - op->customdata = stroke; + op->customdata = stroke; - /* For tablet rotation */ - ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); + /* For tablet rotation */ + ignore_background_click = RNA_boolean_get(op->ptr, "ignore_background_click"); - if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) { - paint_stroke_data_free(op); - return OPERATOR_PASS_THROUGH; - } + if (ignore_background_click && !over_mesh(C, op, event->x, event->y)) { + paint_stroke_data_free(op); + return OPERATOR_PASS_THROUGH; + } - if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { - paint_stroke_data_free(op); - return OPERATOR_FINISHED; - } - /* add modal handler */ - WM_event_add_modal_handler(C, op); + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } + /* add modal handler */ + WM_event_add_modal_handler(C, op); - OPERATOR_RETVAL_CHECK(retval); - BLI_assert(retval == OPERATOR_RUNNING_MODAL); + OPERATOR_RETVAL_CHECK(retval); + BLI_assert(retval == OPERATOR_RUNNING_MODAL); - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } static int sculpt_brush_stroke_exec(bContext *C, wmOperator *op) { - sculpt_brush_stroke_init(C, op); + sculpt_brush_stroke_init(C, op); - op->customdata = paint_stroke_new(C, op, sculpt_stroke_get_location, sculpt_stroke_test_start, - sculpt_stroke_update_step, NULL, sculpt_stroke_done, 0); + op->customdata = paint_stroke_new(C, + op, + sculpt_stroke_get_location, + sculpt_stroke_test_start, + sculpt_stroke_update_step, + NULL, + sculpt_stroke_done, + 0); - /* frees op->customdata */ - paint_stroke_exec(C, op); + /* frees op->customdata */ + paint_stroke_exec(C, op); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op) { - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - const Brush *brush = BKE_paint_brush(&sd->paint); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + const Brush *brush = BKE_paint_brush(&sd->paint); - /* XXX Canceling strokes that way does not work with dynamic topology, user will have to do real undo for now. - * See T46456. */ - if (ss->cache && !sculpt_stroke_is_dynamic_topology(ss, brush)) { - paint_mesh_restore_co(sd, ob); - } + /* XXX Canceling strokes that way does not work with dynamic topology, user will have to do real undo for now. + * See T46456. */ + if (ss->cache && !sculpt_stroke_is_dynamic_topology(ss, brush)) { + paint_mesh_restore_co(sd, ob); + } - paint_stroke_cancel(C, op); + paint_stroke_cancel(C, op); - if (ss->cache) { - sculpt_cache_free(ss->cache); - ss->cache = NULL; - } + if (ss->cache) { + sculpt_cache_free(ss->cache); + ss->cache = NULL; + } - sculpt_brush_exit_tex(sd); + sculpt_brush_exit_tex(sd); } static void SCULPT_OT_brush_stroke(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sculpt"; - ot->idname = "SCULPT_OT_brush_stroke"; - ot->description = "Sculpt a stroke into the geometry"; + /* identifiers */ + ot->name = "Sculpt"; + ot->idname = "SCULPT_OT_brush_stroke"; + ot->description = "Sculpt a stroke into the geometry"; - /* api callbacks */ - ot->invoke = sculpt_brush_stroke_invoke; - ot->modal = paint_stroke_modal; - ot->exec = sculpt_brush_stroke_exec; - ot->poll = sculpt_poll; - ot->cancel = sculpt_brush_stroke_cancel; + /* api callbacks */ + ot->invoke = sculpt_brush_stroke_invoke; + ot->modal = paint_stroke_modal; + ot->exec = sculpt_brush_stroke_exec; + ot->poll = sculpt_poll; + ot->cancel = sculpt_brush_stroke_cancel; - /* flags (sculpt does own undo? (ton) */ - ot->flag = OPTYPE_BLOCKING; + /* flags (sculpt does own undo? (ton) */ + ot->flag = OPTYPE_BLOCKING; - /* properties */ + /* properties */ - paint_stroke_operator_properties(ot); + paint_stroke_operator_properties(ot); - RNA_def_boolean(ot->srna, "ignore_background_click", 0, - "Ignore Background Click", - "Clicks on the background do not start the stroke"); + RNA_def_boolean(ot->srna, + "ignore_background_click", + 0, + "Ignore Background Click", + "Clicks on the background do not start the stroke"); } /* Reset the copy of the mesh that is being sculpted on (currently just for the layer brush) */ static int sculpt_set_persistent_base_exec(bContext *C, wmOperator *UNUSED(op)) { - SculptSession *ss = CTX_data_active_object(C)->sculpt; + SculptSession *ss = CTX_data_active_object(C)->sculpt; - if (ss) { - if (ss->layer_co) - MEM_freeN(ss->layer_co); - ss->layer_co = NULL; - } + if (ss) { + if (ss->layer_co) + MEM_freeN(ss->layer_co); + ss->layer_co = NULL; + } - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_set_persistent_base(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Set Persistent Base"; - ot->idname = "SCULPT_OT_set_persistent_base"; - ot->description = "Reset the copy of the mesh that is being sculpted on"; + /* identifiers */ + ot->name = "Set Persistent Base"; + ot->idname = "SCULPT_OT_set_persistent_base"; + ot->description = "Reset the copy of the mesh that is being sculpted on"; - /* api callbacks */ - ot->exec = sculpt_set_persistent_base_exec; - ot->poll = sculpt_mode_poll; + /* api callbacks */ + ot->exec = sculpt_set_persistent_base_exec; + ot->poll = sculpt_mode_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /************************** Dynamic Topology **************************/ static void sculpt_dynamic_topology_triangulate(BMesh *bm) { - if (bm->totloop != bm->totface * 3) { - BM_mesh_triangulate(bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL); - } + if (bm->totloop != bm->totface * 3) { + BM_mesh_triangulate( + bm, MOD_TRIANGULATE_QUAD_BEAUTY, MOD_TRIANGULATE_NGON_EARCLIP, 4, false, NULL, NULL, NULL); + } } void sculpt_pbvh_clear(Object *ob) { - SculptSession *ss = ob->sculpt; + SculptSession *ss = ob->sculpt; - /* Clear out any existing DM and PBVH */ - if (ss->pbvh) { - BKE_pbvh_free(ss->pbvh); - } - ss->pbvh = NULL; - BKE_object_free_derived_caches(ob); + /* Clear out any existing DM and PBVH */ + if (ss->pbvh) { + BKE_pbvh_free(ss->pbvh); + } + ss->pbvh = NULL; + BKE_object_free_derived_caches(ob); } void sculpt_dyntopo_node_layers_add(SculptSession *ss) { - int cd_node_layer_index; + int cd_node_layer_index; - char layer_id[] = "_dyntopo_node_id"; + char layer_id[] = "_dyntopo_node_id"; - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT, layer_id); - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); - } + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); + if (cd_node_layer_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->vdata, CD_PROP_INT, layer_id); + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->vdata, CD_PROP_INT, layer_id); + } - ss->cd_vert_node_offset = CustomData_get_n_offset( - &ss->bm->vdata, CD_PROP_INT, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT)); + ss->cd_vert_node_offset = CustomData_get_n_offset( + &ss->bm->vdata, + CD_PROP_INT, + cd_node_layer_index - CustomData_get_layer_index(&ss->bm->vdata, CD_PROP_INT)); - ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; + ss->bm->vdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); - if (cd_node_layer_index == -1) { - BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT, layer_id); - cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); - } + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); + if (cd_node_layer_index == -1) { + BM_data_layer_add_named(ss->bm, &ss->bm->pdata, CD_PROP_INT, layer_id); + cd_node_layer_index = CustomData_get_named_layer_index(&ss->bm->pdata, CD_PROP_INT, layer_id); + } - ss->cd_face_node_offset = CustomData_get_n_offset( - &ss->bm->pdata, CD_PROP_INT, - cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT)); + ss->cd_face_node_offset = CustomData_get_n_offset( + &ss->bm->pdata, + CD_PROP_INT, + cd_node_layer_index - CustomData_get_layer_index(&ss->bm->pdata, CD_PROP_INT)); - ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; + ss->bm->pdata.layers[cd_node_layer_index].flag |= CD_FLAG_TEMPORARY; } - -void sculpt_update_after_dynamic_topology_toggle( - Depsgraph *depsgraph, - Scene *scene, Object *ob) +void sculpt_update_after_dynamic_topology_toggle(Depsgraph *depsgraph, Scene *scene, Object *ob) { - Sculpt *sd = scene->toolsettings->sculpt; + Sculpt *sd = scene->toolsettings->sculpt; - /* Create the PBVH */ - BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, false); - WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); + /* Create the PBVH */ + BKE_sculpt_update_mesh_elements(depsgraph, scene, sd, ob, false, false); + WM_main_add_notifier(NC_OBJECT | ND_DRAW, ob); } -void sculpt_dynamic_topology_enable_ex( - Depsgraph *depsgraph, - Scene *scene, Object *ob) +void sculpt_dynamic_topology_enable_ex(Depsgraph *depsgraph, Scene *scene, Object *ob) { - SculptSession *ss = ob->sculpt; - Mesh *me = ob->data; - const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + const BMAllocTemplate allocsize = BMALLOC_TEMPLATE_FROM_ME(me); - sculpt_pbvh_clear(ob); + sculpt_pbvh_clear(ob); - ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != 0; + ss->bm_smooth_shading = (scene->toolsettings->sculpt->flags & SCULPT_DYNTOPO_SMOOTH_SHADING) != + 0; - /* Dynamic topology doesn't ensure selection state is valid, so remove [#36280] */ - BKE_mesh_mselect_clear(me); + /* Dynamic topology doesn't ensure selection state is valid, so remove [#36280] */ + BKE_mesh_mselect_clear(me); - /* Create triangles-only BMesh */ - ss->bm = BM_mesh_create( - &allocsize, - &((struct BMeshCreateParams){.use_toolflags = false,})); + /* Create triangles-only BMesh */ + ss->bm = BM_mesh_create(&allocsize, + &((struct BMeshCreateParams){ + .use_toolflags = false, + })); - BM_mesh_bm_from_me( - ss->bm, me, (&(struct BMeshFromMeshParams){ - .calc_face_normal = true, .use_shapekey = true, .active_shapekey = ob->shapenr, - })); - sculpt_dynamic_topology_triangulate(ss->bm); - BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); - sculpt_dyntopo_node_layers_add(ss); - /* make sure the data for existing faces are initialized */ - if (me->totpoly != ss->bm->totface) { - BM_mesh_normals_update(ss->bm); - } + BM_mesh_bm_from_me(ss->bm, + me, + (&(struct BMeshFromMeshParams){ + .calc_face_normal = true, + .use_shapekey = true, + .active_shapekey = ob->shapenr, + })); + sculpt_dynamic_topology_triangulate(ss->bm); + BM_data_layer_add(ss->bm, &ss->bm->vdata, CD_PAINT_MASK); + sculpt_dyntopo_node_layers_add(ss); + /* make sure the data for existing faces are initialized */ + if (me->totpoly != ss->bm->totface) { + BM_mesh_normals_update(ss->bm); + } - /* Enable dynamic topology */ - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + /* Enable dynamic topology */ + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - /* Enable logging for undo/redo */ - ss->bm_log = BM_log_create(ss->bm); + /* Enable logging for undo/redo */ + ss->bm_log = BM_log_create(ss->bm); - /* Refresh */ - sculpt_update_after_dynamic_topology_toggle(depsgraph, scene, ob); + /* Refresh */ + sculpt_update_after_dynamic_topology_toggle(depsgraph, scene, ob); } /* Free the sculpt BMesh and BMLog * * If 'unode' is given, the BMesh's data is copied out to the unode * before the BMesh is deleted so that it can be restored from */ -void sculpt_dynamic_topology_disable_ex( - Depsgraph *depsgraph, - Scene *scene, Object *ob, SculptUndoNode *unode) -{ - SculptSession *ss = ob->sculpt; - Mesh *me = ob->data; - - sculpt_pbvh_clear(ob); - - if (unode) { - /* Free all existing custom data */ - CustomData_free(&me->vdata, me->totvert); - CustomData_free(&me->edata, me->totedge); - CustomData_free(&me->fdata, me->totface); - CustomData_free(&me->ldata, me->totloop); - CustomData_free(&me->pdata, me->totpoly); - - /* Copy over stored custom data */ - me->totvert = unode->bm_enter_totvert; - me->totloop = unode->bm_enter_totloop; - me->totpoly = unode->bm_enter_totpoly; - me->totedge = unode->bm_enter_totedge; - me->totface = 0; - CustomData_copy(&unode->bm_enter_vdata, &me->vdata, CD_MASK_MESH.vmask, - CD_DUPLICATE, unode->bm_enter_totvert); - CustomData_copy(&unode->bm_enter_edata, &me->edata, CD_MASK_MESH.emask, - CD_DUPLICATE, unode->bm_enter_totedge); - CustomData_copy(&unode->bm_enter_ldata, &me->ldata, CD_MASK_MESH.lmask, - CD_DUPLICATE, unode->bm_enter_totloop); - CustomData_copy(&unode->bm_enter_pdata, &me->pdata, CD_MASK_MESH.pmask, - CD_DUPLICATE, unode->bm_enter_totpoly); - - BKE_mesh_update_customdata_pointers(me, false); - } - else { - BKE_sculptsession_bm_to_me(ob, true); - } - - /* Clear data */ - me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - - /* typically valid but with global-undo they can be NULL, [#36234] */ - if (ss->bm) { - BM_mesh_free(ss->bm); - ss->bm = NULL; - } - if (ss->bm_log) { - BM_log_free(ss->bm_log); - ss->bm_log = NULL; - } - - BKE_particlesystem_reset_all(ob); - BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED); - - /* Refresh */ - sculpt_update_after_dynamic_topology_toggle(depsgraph, scene, ob); +void sculpt_dynamic_topology_disable_ex(Depsgraph *depsgraph, + Scene *scene, + Object *ob, + SculptUndoNode *unode) +{ + SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + + sculpt_pbvh_clear(ob); + + if (unode) { + /* Free all existing custom data */ + CustomData_free(&me->vdata, me->totvert); + CustomData_free(&me->edata, me->totedge); + CustomData_free(&me->fdata, me->totface); + CustomData_free(&me->ldata, me->totloop); + CustomData_free(&me->pdata, me->totpoly); + + /* Copy over stored custom data */ + me->totvert = unode->bm_enter_totvert; + me->totloop = unode->bm_enter_totloop; + me->totpoly = unode->bm_enter_totpoly; + me->totedge = unode->bm_enter_totedge; + me->totface = 0; + CustomData_copy(&unode->bm_enter_vdata, + &me->vdata, + CD_MASK_MESH.vmask, + CD_DUPLICATE, + unode->bm_enter_totvert); + CustomData_copy(&unode->bm_enter_edata, + &me->edata, + CD_MASK_MESH.emask, + CD_DUPLICATE, + unode->bm_enter_totedge); + CustomData_copy(&unode->bm_enter_ldata, + &me->ldata, + CD_MASK_MESH.lmask, + CD_DUPLICATE, + unode->bm_enter_totloop); + CustomData_copy(&unode->bm_enter_pdata, + &me->pdata, + CD_MASK_MESH.pmask, + CD_DUPLICATE, + unode->bm_enter_totpoly); + + BKE_mesh_update_customdata_pointers(me, false); + } + else { + BKE_sculptsession_bm_to_me(ob, true); + } + + /* Clear data */ + me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + + /* typically valid but with global-undo they can be NULL, [#36234] */ + if (ss->bm) { + BM_mesh_free(ss->bm); + ss->bm = NULL; + } + if (ss->bm_log) { + BM_log_free(ss->bm_log); + ss->bm_log = NULL; + } + + BKE_particlesystem_reset_all(ob); + BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_OUTDATED); + + /* Refresh */ + sculpt_update_after_dynamic_topology_toggle(depsgraph, scene, ob); } void sculpt_dynamic_topology_disable(bContext *C, SculptUndoNode *unode) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - sculpt_dynamic_topology_disable_ex(depsgraph, scene, ob, unode); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + sculpt_dynamic_topology_disable_ex(depsgraph, scene, ob, unode); } -static void sculpt_dynamic_topology_disable_with_undo( - Depsgraph *depsgraph, Scene *scene, Object *ob) +static void sculpt_dynamic_topology_disable_with_undo(Depsgraph *depsgraph, + Scene *scene, + Object *ob) { - SculptSession *ss = ob->sculpt; - if (ss->bm) { - sculpt_undo_push_begin("Dynamic topology disable"); - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_END); - sculpt_dynamic_topology_disable_ex(depsgraph, scene, ob, NULL); - sculpt_undo_push_end(); - } + SculptSession *ss = ob->sculpt; + if (ss->bm) { + sculpt_undo_push_begin("Dynamic topology disable"); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_END); + sculpt_dynamic_topology_disable_ex(depsgraph, scene, ob, NULL); + sculpt_undo_push_end(); + } } -static void sculpt_dynamic_topology_enable_with_undo( - Depsgraph *depsgraph, - Scene *scene, Object *ob) +static void sculpt_dynamic_topology_enable_with_undo(Depsgraph *depsgraph, + Scene *scene, + Object *ob) { - SculptSession *ss = ob->sculpt; - if (ss->bm == NULL) { - sculpt_undo_push_begin("Dynamic topology enable"); - sculpt_dynamic_topology_enable_ex(depsgraph, scene, ob); - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - sculpt_undo_push_end(); - } + SculptSession *ss = ob->sculpt; + if (ss->bm == NULL) { + sculpt_undo_push_begin("Dynamic topology enable"); + sculpt_dynamic_topology_enable_ex(depsgraph, scene, ob); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + sculpt_undo_push_end(); + } } static int sculpt_dynamic_topology_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; - WM_cursor_wait(1); + WM_cursor_wait(1); - if (ss->bm) { - sculpt_dynamic_topology_disable_with_undo(depsgraph, scene, ob); - } - else { - sculpt_dynamic_topology_enable_with_undo(depsgraph, scene, ob); - } + if (ss->bm) { + sculpt_dynamic_topology_disable_with_undo(depsgraph, scene, ob); + } + else { + sculpt_dynamic_topology_enable_with_undo(depsgraph, scene, ob); + } - WM_cursor_wait(0); + WM_cursor_wait(0); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } enum eDynTopoWarnFlag { - DYNTOPO_WARN_VDATA = (1 << 0), - DYNTOPO_WARN_EDATA = (1 << 1), - DYNTOPO_WARN_LDATA = (1 << 2), - DYNTOPO_WARN_MODIFIER = (1 << 3), + DYNTOPO_WARN_VDATA = (1 << 0), + DYNTOPO_WARN_EDATA = (1 << 1), + DYNTOPO_WARN_LDATA = (1 << 2), + DYNTOPO_WARN_MODIFIER = (1 << 3), }; static int dyntopo_warning_popup(bContext *C, wmOperatorType *ot, enum eDynTopoWarnFlag flag) { - uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); - uiLayout *layout = UI_popup_menu_layout(pup); + uiPopupMenu *pup = UI_popup_menu_begin(C, IFACE_("Warning!"), ICON_ERROR); + uiLayout *layout = UI_popup_menu_layout(pup); - if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { - const char *msg_error = TIP_("Vertex Data Detected!"); - const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); - uiItemL(layout, msg_error, ICON_INFO); - uiItemL(layout, msg, ICON_NONE); - uiItemS(layout); - } + if (flag & (DYNTOPO_WARN_VDATA | DYNTOPO_WARN_EDATA | DYNTOPO_WARN_LDATA)) { + const char *msg_error = TIP_("Vertex Data Detected!"); + const char *msg = TIP_("Dyntopo will not preserve vertex colors, UVs, or other customdata"); + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } - if (flag & DYNTOPO_WARN_MODIFIER) { - const char *msg_error = TIP_("Generative Modifiers Detected!"); - const char *msg = TIP_("Keeping the modifiers will increase polycount when returning to object mode"); + if (flag & DYNTOPO_WARN_MODIFIER) { + const char *msg_error = TIP_("Generative Modifiers Detected!"); + const char *msg = TIP_( + "Keeping the modifiers will increase polycount when returning to object mode"); - uiItemL(layout, msg_error, ICON_INFO); - uiItemL(layout, msg, ICON_NONE); - uiItemS(layout); - } + uiItemL(layout, msg_error, ICON_INFO); + uiItemL(layout, msg, ICON_NONE); + uiItemS(layout); + } - uiItemFullO_ptr(layout, ot, IFACE_("OK"), ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0, NULL); + uiItemFullO_ptr(layout, ot, IFACE_("OK"), ICON_NONE, NULL, WM_OP_EXEC_DEFAULT, 0, NULL); - UI_popup_menu_end(C, pup); + UI_popup_menu_end(C, pup); - return OPERATOR_INTERFACE; + return OPERATOR_INTERFACE; } static enum eDynTopoWarnFlag sculpt_dynamic_topology_check(Scene *scene, Object *ob) { - Mesh *me = ob->data; - SculptSession *ss = ob->sculpt; + Mesh *me = ob->data; + SculptSession *ss = ob->sculpt; - enum eDynTopoWarnFlag flag = 0; + enum eDynTopoWarnFlag flag = 0; - BLI_assert(ss->bm == NULL); - UNUSED_VARS_NDEBUG(ss); + BLI_assert(ss->bm == NULL); + UNUSED_VARS_NDEBUG(ss); - for (int i = 0; i < CD_NUMTYPES; i++) { - if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) { - if (CustomData_has_layer(&me->vdata, i)) { - flag |= DYNTOPO_WARN_VDATA; - } - if (CustomData_has_layer(&me->edata, i)) { - flag |= DYNTOPO_WARN_EDATA; - } - if (CustomData_has_layer(&me->ldata, i)) { - flag |= DYNTOPO_WARN_LDATA; - } - } - } + for (int i = 0; i < CD_NUMTYPES; i++) { + if (!ELEM(i, CD_MVERT, CD_MEDGE, CD_MFACE, CD_MLOOP, CD_MPOLY, CD_PAINT_MASK, CD_ORIGINDEX)) { + if (CustomData_has_layer(&me->vdata, i)) { + flag |= DYNTOPO_WARN_VDATA; + } + if (CustomData_has_layer(&me->edata, i)) { + flag |= DYNTOPO_WARN_EDATA; + } + if (CustomData_has_layer(&me->ldata, i)) { + flag |= DYNTOPO_WARN_LDATA; + } + } + } - { - VirtualModifierData virtualModifierData; - ModifierData *md = modifiers_getVirtualModifierList(ob, &virtualModifierData); + { + VirtualModifierData virtualModifierData; + ModifierData *md = modifiers_getVirtualModifierList(ob, &virtualModifierData); - /* exception for shape keys because we can edit those */ - for (; md; md = md->next) { - const ModifierTypeInfo *mti = modifierType_getInfo(md->type); - if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) continue; + /* exception for shape keys because we can edit those */ + for (; md; md = md->next) { + const ModifierTypeInfo *mti = modifierType_getInfo(md->type); + if (!modifier_isEnabled(scene, md, eModifierMode_Realtime)) + continue; - if (mti->type == eModifierTypeType_Constructive) { - flag |= DYNTOPO_WARN_MODIFIER; - break; - } - } - } + if (mti->type == eModifierTypeType_Constructive) { + flag |= DYNTOPO_WARN_MODIFIER; + break; + } + } + } - return flag; + return flag; } -static int sculpt_dynamic_topology_toggle_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +static int sculpt_dynamic_topology_toggle_invoke(bContext *C, + wmOperator *op, + const wmEvent *UNUSED(event)) { - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; - if (!ss->bm) { - Scene *scene = CTX_data_scene(C); - enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); + if (!ss->bm) { + Scene *scene = CTX_data_scene(C); + enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); - if (flag) { - /* The mesh has customdata that will be lost, let the user confirm this is OK */ - return dyntopo_warning_popup(C, op->type, flag); - } - } + if (flag) { + /* The mesh has customdata that will be lost, let the user confirm this is OK */ + return dyntopo_warning_popup(C, op->type, flag); + } + } - return sculpt_dynamic_topology_toggle_exec(C, op); + return sculpt_dynamic_topology_toggle_exec(C, op); } static void SCULPT_OT_dynamic_topology_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Dynamic Topology Toggle"; - ot->idname = "SCULPT_OT_dynamic_topology_toggle"; - ot->description = "Dynamic topology alters the mesh topology while sculpting"; + /* identifiers */ + ot->name = "Dynamic Topology Toggle"; + ot->idname = "SCULPT_OT_dynamic_topology_toggle"; + ot->description = "Dynamic topology alters the mesh topology while sculpting"; - /* api callbacks */ - ot->invoke = sculpt_dynamic_topology_toggle_invoke; - ot->exec = sculpt_dynamic_topology_toggle_exec; - ot->poll = sculpt_mode_poll; + /* api callbacks */ + ot->invoke = sculpt_dynamic_topology_toggle_invoke; + ot->exec = sculpt_dynamic_topology_toggle_exec; + ot->poll = sculpt_mode_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /************************* SCULPT_OT_optimize *************************/ static int sculpt_optimize_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - sculpt_pbvh_clear(ob); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + sculpt_pbvh_clear(ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static bool sculpt_and_dynamic_topology_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); + Object *ob = CTX_data_active_object(C); - return sculpt_mode_poll(C) && ob->sculpt->bm; + return sculpt_mode_poll(C) && ob->sculpt->bm; } /* The BVH gets less optimal more quickly with dynamic topology than @@ -5777,572 +5831,578 @@ static bool sculpt_and_dynamic_topology_poll(bContext *C) * to recalculate it than toggling modes. */ static void SCULPT_OT_optimize(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Optimize"; - ot->idname = "SCULPT_OT_optimize"; - ot->description = "Recalculate the sculpt BVH to improve performance"; + /* identifiers */ + ot->name = "Optimize"; + ot->idname = "SCULPT_OT_optimize"; + ot->description = "Recalculate the sculpt BVH to improve performance"; - /* api callbacks */ - ot->exec = sculpt_optimize_exec; - ot->poll = sculpt_and_dynamic_topology_poll; + /* api callbacks */ + ot->exec = sculpt_optimize_exec; + ot->poll = sculpt_and_dynamic_topology_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } /********************* Dynamic topology symmetrize ********************/ static int sculpt_symmetrize_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = CTX_data_active_object(C); - const Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - SculptSession *ss = ob->sculpt; + Object *ob = CTX_data_active_object(C); + const Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = ob->sculpt; - /* To simplify undo for symmetrize, all BMesh elements are logged - * as deleted, then after symmetrize operation all BMesh elements - * are logged as added (as opposed to attempting to store just the - * parts that symmetrize modifies) */ - sculpt_undo_push_begin("Dynamic topology symmetrize"); - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); - BM_log_before_all_removed(ss->bm, ss->bm_log); + /* To simplify undo for symmetrize, all BMesh elements are logged + * as deleted, then after symmetrize operation all BMesh elements + * are logged as added (as opposed to attempting to store just the + * parts that symmetrize modifies) */ + sculpt_undo_push_begin("Dynamic topology symmetrize"); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_SYMMETRIZE); + BM_log_before_all_removed(ss->bm, ss->bm_log); - BM_mesh_toolflags_set(ss->bm, true); + BM_mesh_toolflags_set(ss->bm, true); - /* Symmetrize and re-triangulate */ - BMO_op_callf(ss->bm, (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), - "symmetrize input=%avef direction=%i dist=%f", - sd->symmetrize_direction, 0.00001f); - sculpt_dynamic_topology_triangulate(ss->bm); + /* Symmetrize and re-triangulate */ + BMO_op_callf(ss->bm, + (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE), + "symmetrize input=%avef direction=%i dist=%f", + sd->symmetrize_direction, + 0.00001f); + sculpt_dynamic_topology_triangulate(ss->bm); - /* bisect operator flags edges (keep tags clean for edge queue) */ - BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); + /* bisect operator flags edges (keep tags clean for edge queue) */ + BM_mesh_elem_hflag_disable_all(ss->bm, BM_EDGE, BM_ELEM_TAG, false); - BM_mesh_toolflags_set(ss->bm, false); + BM_mesh_toolflags_set(ss->bm, false); - /* Finish undo */ - BM_log_all_added(ss->bm, ss->bm_log); - sculpt_undo_push_end(); + /* Finish undo */ + BM_log_all_added(ss->bm, ss->bm_log); + sculpt_undo_push_end(); - /* Redraw */ - sculpt_pbvh_clear(ob); - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + /* Redraw */ + sculpt_pbvh_clear(ob); + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_symmetrize(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Symmetrize"; - ot->idname = "SCULPT_OT_symmetrize"; - ot->description = "Symmetrize the topology modifications"; + /* identifiers */ + ot->name = "Symmetrize"; + ot->idname = "SCULPT_OT_symmetrize"; + ot->description = "Symmetrize the topology modifications"; - /* api callbacks */ - ot->exec = sculpt_symmetrize_exec; - ot->poll = sculpt_and_dynamic_topology_poll; + /* api callbacks */ + ot->exec = sculpt_symmetrize_exec; + ot->poll = sculpt_and_dynamic_topology_poll; } /**** Toggle operator for turning sculpt mode on or off ****/ static void sculpt_init_session(Depsgraph *depsgraph, Scene *scene, Object *ob) { - /* Create persistent sculpt mode data */ - BKE_sculpt_toolsettings_data_ensure(scene); - - ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); - ob->sculpt->mode_type = OB_MODE_SCULPT; - BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); -} - -static int ed_object_sculptmode_flush_recalc_flag(Scene *scene, Object *ob, MultiresModifierData *mmd) -{ - int flush_recalc = 0; - /* multires in sculpt mode could have different from object mode subdivision level */ - flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl; - /* if object has got active modifiers, it's dm could be different in sculpt mode */ - flush_recalc |= sculpt_has_active_modifiers(scene, ob); - return flush_recalc; -} - -void ED_object_sculptmode_enter_ex( - Main *bmain, Depsgraph *depsgraph, - Scene *scene, Object *ob, const bool force_dyntopo, - ReportList *reports) -{ - const int mode_flag = OB_MODE_SCULPT; - Mesh *me = BKE_mesh_from_object(ob); - - /* Enter sculptmode */ - ob->mode |= mode_flag; - - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - - const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); - - if (flush_recalc) - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - - /* Create sculpt mode session data */ - if (ob->sculpt) { - BKE_sculptsession_free(ob); - } - - /* Make sure derived final from original object does not reference possibly - * freed memory. - */ - BKE_object_free_derived_caches(ob); - - sculpt_init_session(depsgraph, scene, ob); - - /* Mask layer is required */ - if (mmd) { - /* XXX, we could attempt to support adding mask data mid-sculpt mode (with multi-res) - * but this ends up being quite tricky (and slow) */ - BKE_sculpt_mask_layers_ensure(ob, mmd); - } - - if (!(fabsf(ob->scale[0] - ob->scale[1]) < 1e-4f && fabsf(ob->scale[1] - ob->scale[2]) < 1e-4f)) { - BKE_report(reports, RPT_WARNING, - "Object has non-uniform scale, sculpting may be unpredictable"); - } - else if (is_negative_m4(ob->obmat)) { - BKE_report(reports, RPT_WARNING, - "Object has negative scale, sculpting may be unpredictable"); - } - - Paint *paint = BKE_paint_get_active_from_paintmode(scene, PAINT_MODE_SCULPT); - BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT, PAINT_CURSOR_SCULPT); - - paint_cursor_start_explicit(paint, bmain->wm.first, sculpt_poll_view3d); - - /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, - * As long as no data was added that is not supported. */ - if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { - const char *message_unsupported = NULL; - if (me->totloop != me->totpoly * 3) { - message_unsupported = TIP_("non-triangle face"); - } - else if (mmd != NULL) { - message_unsupported = TIP_("multi-res modifier"); - } - else { - enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); - if (flag == 0) { - /* pass */ - } - else if (flag & DYNTOPO_WARN_VDATA) { - message_unsupported = TIP_("vertex data"); - } - else if (flag & DYNTOPO_WARN_EDATA) { - message_unsupported = TIP_("edge data"); - } - else if (flag & DYNTOPO_WARN_LDATA) { - message_unsupported = TIP_("face data"); - } - else if (flag & DYNTOPO_WARN_MODIFIER) { - message_unsupported = TIP_("constructive modifier"); - } - else { - BLI_assert(0); - } - } - - if ((message_unsupported == NULL) || force_dyntopo) { - /* Needed because we may be entering this mode before the undo system loads. */ - wmWindowManager *wm = bmain->wm.first; - bool has_undo = wm->undo_stack != NULL; - /* undo push is needed to prevent memory leak */ - if (has_undo) { - sculpt_undo_push_begin("Dynamic topology enable"); - } - sculpt_dynamic_topology_enable_ex(depsgraph, scene, ob); - if (has_undo) { - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); - sculpt_undo_push_end(); - } - } - else { - BKE_reportf(reports, RPT_WARNING, - "Dynamic Topology found: %s, disabled", - message_unsupported); - me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; - } - } - - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + /* Create persistent sculpt mode data */ + BKE_sculpt_toolsettings_data_ensure(scene); + + ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session"); + ob->sculpt->mode_type = OB_MODE_SCULPT; + BKE_sculpt_update_mesh_elements(depsgraph, scene, scene->toolsettings->sculpt, ob, false, false); +} + +static int ed_object_sculptmode_flush_recalc_flag(Scene *scene, + Object *ob, + MultiresModifierData *mmd) +{ + int flush_recalc = 0; + /* multires in sculpt mode could have different from object mode subdivision level */ + flush_recalc |= mmd && mmd->sculptlvl != mmd->lvl; + /* if object has got active modifiers, it's dm could be different in sculpt mode */ + flush_recalc |= sculpt_has_active_modifiers(scene, ob); + return flush_recalc; +} + +void ED_object_sculptmode_enter_ex(Main *bmain, + Depsgraph *depsgraph, + Scene *scene, + Object *ob, + const bool force_dyntopo, + ReportList *reports) +{ + const int mode_flag = OB_MODE_SCULPT; + Mesh *me = BKE_mesh_from_object(ob); + + /* Enter sculptmode */ + ob->mode |= mode_flag; + + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + + const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); + + if (flush_recalc) + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + + /* Create sculpt mode session data */ + if (ob->sculpt) { + BKE_sculptsession_free(ob); + } + + /* Make sure derived final from original object does not reference possibly + * freed memory. + */ + BKE_object_free_derived_caches(ob); + + sculpt_init_session(depsgraph, scene, ob); + + /* Mask layer is required */ + if (mmd) { + /* XXX, we could attempt to support adding mask data mid-sculpt mode (with multi-res) + * but this ends up being quite tricky (and slow) */ + BKE_sculpt_mask_layers_ensure(ob, mmd); + } + + if (!(fabsf(ob->scale[0] - ob->scale[1]) < 1e-4f && + fabsf(ob->scale[1] - ob->scale[2]) < 1e-4f)) { + BKE_report( + reports, RPT_WARNING, "Object has non-uniform scale, sculpting may be unpredictable"); + } + else if (is_negative_m4(ob->obmat)) { + BKE_report(reports, RPT_WARNING, "Object has negative scale, sculpting may be unpredictable"); + } + + Paint *paint = BKE_paint_get_active_from_paintmode(scene, PAINT_MODE_SCULPT); + BKE_paint_init(bmain, scene, PAINT_MODE_SCULPT, PAINT_CURSOR_SCULPT); + + paint_cursor_start_explicit(paint, bmain->wm.first, sculpt_poll_view3d); + + /* Check dynamic-topology flag; re-enter dynamic-topology mode when changing modes, + * As long as no data was added that is not supported. */ + if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { + const char *message_unsupported = NULL; + if (me->totloop != me->totpoly * 3) { + message_unsupported = TIP_("non-triangle face"); + } + else if (mmd != NULL) { + message_unsupported = TIP_("multi-res modifier"); + } + else { + enum eDynTopoWarnFlag flag = sculpt_dynamic_topology_check(scene, ob); + if (flag == 0) { + /* pass */ + } + else if (flag & DYNTOPO_WARN_VDATA) { + message_unsupported = TIP_("vertex data"); + } + else if (flag & DYNTOPO_WARN_EDATA) { + message_unsupported = TIP_("edge data"); + } + else if (flag & DYNTOPO_WARN_LDATA) { + message_unsupported = TIP_("face data"); + } + else if (flag & DYNTOPO_WARN_MODIFIER) { + message_unsupported = TIP_("constructive modifier"); + } + else { + BLI_assert(0); + } + } + + if ((message_unsupported == NULL) || force_dyntopo) { + /* Needed because we may be entering this mode before the undo system loads. */ + wmWindowManager *wm = bmain->wm.first; + bool has_undo = wm->undo_stack != NULL; + /* undo push is needed to prevent memory leak */ + if (has_undo) { + sculpt_undo_push_begin("Dynamic topology enable"); + } + sculpt_dynamic_topology_enable_ex(depsgraph, scene, ob); + if (has_undo) { + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_DYNTOPO_BEGIN); + sculpt_undo_push_end(); + } + } + else { + BKE_reportf( + reports, RPT_WARNING, "Dynamic Topology found: %s, disabled", message_unsupported); + me->flag &= ~ME_SCULPT_DYNAMIC_TOPOLOGY; + } + } + + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } void ED_object_sculptmode_enter(struct bContext *C, ReportList *reports) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - Depsgraph *depsgraph = CTX_data_depsgraph(C); - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports); } -void ED_object_sculptmode_exit_ex( - Depsgraph *depsgraph, - Scene *scene, Object *ob) +void ED_object_sculptmode_exit_ex(Depsgraph *depsgraph, Scene *scene, Object *ob) { - const int mode_flag = OB_MODE_SCULPT; - Mesh *me = BKE_mesh_from_object(ob); + const int mode_flag = OB_MODE_SCULPT; + Mesh *me = BKE_mesh_from_object(ob); - MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); - if (mmd) { - multires_force_update(ob); - } + MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob); + if (mmd) { + multires_force_update(ob); + } - /* Not needed for now. */ + /* Not needed for now. */ #if 0 - const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); + const int flush_recalc = ed_object_sculptmode_flush_recalc_flag(scene, ob, mmd); #endif - /* Always for now, so leaving sculpt mode always ensures scene is in - * a consistent state. - */ - if (true || /* flush_recalc || */ (ob->sculpt && ob->sculpt->bm)) { - DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); - } + /* Always for now, so leaving sculpt mode always ensures scene is in + * a consistent state. + */ + if (true || /* flush_recalc || */ (ob->sculpt && ob->sculpt->bm)) { + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + } - if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { - /* Dynamic topology must be disabled before exiting sculpt - * mode to ensure the undo stack stays in a consistent - * state */ - sculpt_dynamic_topology_disable_with_undo(depsgraph, scene, ob); + if (me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) { + /* Dynamic topology must be disabled before exiting sculpt + * mode to ensure the undo stack stays in a consistent + * state */ + sculpt_dynamic_topology_disable_with_undo(depsgraph, scene, ob); - /* store so we know to re-enable when entering sculpt mode */ - me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; - } + /* store so we know to re-enable when entering sculpt mode */ + me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY; + } - /* Leave sculptmode */ - ob->mode &= ~mode_flag; + /* Leave sculptmode */ + ob->mode &= ~mode_flag; - BKE_sculptsession_free(ob); + BKE_sculptsession_free(ob); - paint_cursor_delete_textures(); + paint_cursor_delete_textures(); - /* Never leave derived meshes behind. */ - BKE_object_free_derived_caches(ob); + /* Never leave derived meshes behind. */ + BKE_object_free_derived_caches(ob); - /* Flush object mode. */ - DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); + /* Flush object mode. */ + DEG_id_tag_update(&ob->id, ID_RECALC_COPY_ON_WRITE); } void ED_object_sculptmode_exit(bContext *C) { - Depsgraph *depsgraph = CTX_data_depsgraph(C); - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - ED_object_sculptmode_exit_ex(depsgraph, scene, ob); + Depsgraph *depsgraph = CTX_data_depsgraph(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + ED_object_sculptmode_exit_ex(depsgraph, scene, ob); } static int sculpt_mode_toggle_exec(bContext *C, wmOperator *op) { - struct wmMsgBus *mbus = CTX_wm_message_bus(C); - Main *bmain = CTX_data_main(C); - Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); - Scene *scene = CTX_data_scene(C); - ToolSettings *ts = scene->toolsettings; - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob = OBACT(view_layer); - const int mode_flag = OB_MODE_SCULPT; - const bool is_mode_set = (ob->mode & mode_flag) != 0; + struct wmMsgBus *mbus = CTX_wm_message_bus(C); + Main *bmain = CTX_data_main(C); + Depsgraph *depsgraph = CTX_data_depsgraph_on_load(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob = OBACT(view_layer); + const int mode_flag = OB_MODE_SCULPT; + const bool is_mode_set = (ob->mode & mode_flag) != 0; - if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { - return OPERATOR_CANCELLED; - } - } + if (!is_mode_set) { + if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + return OPERATOR_CANCELLED; + } + } - if (is_mode_set) { - ED_object_sculptmode_exit_ex(depsgraph, scene, ob); - } - else { - ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); - BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); - } + if (is_mode_set) { + ED_object_sculptmode_exit_ex(depsgraph, scene, ob); + } + else { + ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports); + BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint); + } - WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); + WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene); - WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); + WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode); - WM_toolsystem_update_from_context_view3d(C); + WM_toolsystem_update_from_context_view3d(C); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_sculptmode_toggle(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sculpt Mode"; - ot->idname = "SCULPT_OT_sculptmode_toggle"; - ot->description = "Toggle sculpt mode in 3D view"; + /* identifiers */ + ot->name = "Sculpt Mode"; + ot->idname = "SCULPT_OT_sculptmode_toggle"; + ot->description = "Toggle sculpt mode in 3D view"; - /* api callbacks */ - ot->exec = sculpt_mode_toggle_exec; - ot->poll = ED_operator_object_active_editable_mesh; + /* api callbacks */ + ot->exec = sculpt_mode_toggle_exec; + ot->poll = ED_operator_object_active_editable_mesh; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA; } static bool sculpt_and_constant_or_manual_detail_poll(bContext *C) { - Object *ob = CTX_data_active_object(C); - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - return sculpt_mode_poll(C) && ob->sculpt->bm && - (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)); + return sculpt_mode_poll(C) && ob->sculpt->bm && + (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)); } static int sculpt_detail_flood_fill_exec(bContext *C, wmOperator *UNUSED(op)) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = CTX_data_active_object(C); - SculptSession *ss = ob->sculpt; - float size; - float bb_min[3], bb_max[3], center[3], dim[3]; - int i, totnodes; - PBVHNode **nodes; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + float size; + float bb_min[3], bb_max[3], center[3], dim[3]; + int i, totnodes; + PBVHNode **nodes; - BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnodes); - if (!totnodes) - return OPERATOR_CANCELLED; + if (!totnodes) + return OPERATOR_CANCELLED; - for (i = 0; i < totnodes; i++) { - BKE_pbvh_node_mark_topology_update(nodes[i]); - } - /* get the bounding box, it's center and size */ - BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); - add_v3_v3v3(center, bb_min, bb_max); - mul_v3_fl(center, 0.5f); - sub_v3_v3v3(dim, bb_max, bb_min); - size = max_fff(dim[0], dim[1], dim[2]); + for (i = 0; i < totnodes; i++) { + BKE_pbvh_node_mark_topology_update(nodes[i]); + } + /* get the bounding box, it's center and size */ + BKE_pbvh_bounding_box(ob->sculpt->pbvh, bb_min, bb_max); + add_v3_v3v3(center, bb_min, bb_max); + mul_v3_fl(center, 0.5f); + sub_v3_v3v3(dim, bb_max, bb_min); + size = max_fff(dim[0], dim[1], dim[2]); - /* update topology size */ - float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); - BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); + /* update topology size */ + float object_space_constant_detail = 1.0f / (sd->constant_detail * mat4_to_scale(ob->obmat)); + BKE_pbvh_bmesh_detail_size_set(ss->pbvh, object_space_constant_detail); - sculpt_undo_push_begin("Dynamic topology flood fill"); - sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); + sculpt_undo_push_begin("Dynamic topology flood fill"); + sculpt_undo_push_node(ob, NULL, SCULPT_UNDO_COORDS); - while (BKE_pbvh_bmesh_update_topology( - ss->pbvh, PBVH_Collapse | PBVH_Subdivide, - center, NULL, size, false, false)) - { - for (i = 0; i < totnodes; i++) - BKE_pbvh_node_mark_topology_update(nodes[i]); - } + while (BKE_pbvh_bmesh_update_topology( + ss->pbvh, PBVH_Collapse | PBVH_Subdivide, center, NULL, size, false, false)) { + for (i = 0; i < totnodes; i++) + BKE_pbvh_node_mark_topology_update(nodes[i]); + } - MEM_freeN(nodes); - sculpt_undo_push_end(); + MEM_freeN(nodes); + sculpt_undo_push_end(); - /* force rebuild of pbvh for better BB placement */ - sculpt_pbvh_clear(ob); - /* Redraw */ - WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); + /* force rebuild of pbvh for better BB placement */ + sculpt_pbvh_clear(ob); + /* Redraw */ + WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_detail_flood_fill(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Detail Flood Fill"; - ot->idname = "SCULPT_OT_detail_flood_fill"; - ot->description = "Flood fill the mesh with the selected detail setting"; + /* identifiers */ + ot->name = "Detail Flood Fill"; + ot->idname = "SCULPT_OT_detail_flood_fill"; + ot->description = "Flood fill the mesh with the selected detail setting"; - /* api callbacks */ - ot->exec = sculpt_detail_flood_fill_exec; - ot->poll = sculpt_and_constant_or_manual_detail_poll; + /* api callbacks */ + ot->exec = sculpt_detail_flood_fill_exec; + ot->poll = sculpt_and_constant_or_manual_detail_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } static void sample_detail(bContext *C, int mx, int my) { - /* Find 3D view to pick from. */ - bScreen *screen = CTX_wm_screen(C); - ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, mx, my); - ARegion *ar = (sa) ? BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my) : NULL; - if (ar == NULL) { - return; - } + /* Find 3D view to pick from. */ + bScreen *screen = CTX_wm_screen(C); + ScrArea *sa = BKE_screen_find_area_xy(screen, SPACE_VIEW3D, mx, my); + ARegion *ar = (sa) ? BKE_area_find_region_xy(sa, RGN_TYPE_WINDOW, mx, my) : NULL; + if (ar == NULL) { + return; + } - /* Set context to 3D view. */ - ScrArea *prev_sa = CTX_wm_area(C); - ARegion *prev_ar = CTX_wm_region(C); - CTX_wm_area_set(C, sa); - CTX_wm_region_set(C, ar); + /* Set context to 3D view. */ + ScrArea *prev_sa = CTX_wm_area(C); + ARegion *prev_ar = CTX_wm_region(C); + CTX_wm_area_set(C, sa); + CTX_wm_region_set(C, ar); - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); + ViewContext vc; + ED_view3d_viewcontext_init(C, &vc); - /* Pick sample detail. */ - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - Object *ob = vc.obact; - Brush *brush = BKE_paint_brush(&sd->paint); + /* Pick sample detail. */ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Object *ob = vc.obact; + Brush *brush = BKE_paint_brush(&sd->paint); - sculpt_stroke_modifiers_check(C, ob, brush); + sculpt_stroke_modifiers_check(C, ob, brush); - float mouse[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; - float ray_start[3], ray_end[3], ray_normal[3]; - float depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, false); + float mouse[2] = {mx - ar->winrct.xmin, my - ar->winrct.ymin}; + float ray_start[3], ray_end[3], ray_normal[3]; + float depth = sculpt_raycast_init(&vc, mouse, ray_start, ray_end, ray_normal, false); - SculptDetailRaycastData srd; - srd.hit = 0; - srd.ray_start = ray_start; - srd.ray_normal = ray_normal; - srd.depth = depth; - srd.edge_length = 0.0f; + SculptDetailRaycastData srd; + srd.hit = 0; + srd.ray_start = ray_start; + srd.ray_normal = ray_normal; + srd.depth = depth; + srd.edge_length = 0.0f; - BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, - ray_start, ray_normal, false); + BKE_pbvh_raycast(ob->sculpt->pbvh, sculpt_raycast_detail_cb, &srd, ray_start, ray_normal, false); - if (srd.hit && srd.edge_length > 0.0f) { - /* Convert edge length to world space detail resolution. */ - sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->obmat)); - } + if (srd.hit && srd.edge_length > 0.0f) { + /* Convert edge length to world space detail resolution. */ + sd->constant_detail = 1 / (srd.edge_length * mat4_to_scale(ob->obmat)); + } - /* Restore context. */ - CTX_wm_area_set(C, prev_sa); - CTX_wm_region_set(C, prev_ar); + /* Restore context. */ + CTX_wm_area_set(C, prev_sa); + CTX_wm_region_set(C, prev_ar); } static int sculpt_sample_detail_size_exec(bContext *C, wmOperator *op) { - int ss_co[2]; - RNA_int_get_array(op->ptr, "location", ss_co); - sample_detail(C, ss_co[0], ss_co[1]); - return OPERATOR_FINISHED; + int ss_co[2]; + RNA_int_get_array(op->ptr, "location", ss_co); + sample_detail(C, ss_co[0], ss_co[1]); + return OPERATOR_FINISHED; } - static int sculpt_sample_detail_size_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(e)) { - ED_workspace_status_text(C, "Click on the mesh to set the detail"); - WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); - WM_event_add_modal_handler(C, op); - return OPERATOR_RUNNING_MODAL; + ED_workspace_status_text(C, "Click on the mesh to set the detail"); + WM_cursor_modal_set(CTX_wm_window(C), BC_EYEDROPPER_CURSOR); + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; } static int sculpt_sample_detail_size_modal(bContext *C, wmOperator *op, const wmEvent *event) { - switch (event->type) { - case LEFTMOUSE: - if (event->val == KM_PRESS) { - int ss_co[2] = {event->x, event->y}; + switch (event->type) { + case LEFTMOUSE: + if (event->val == KM_PRESS) { + int ss_co[2] = {event->x, event->y}; - sample_detail(C, ss_co[0], ss_co[1]); + sample_detail(C, ss_co[0], ss_co[1]); - RNA_int_set_array(op->ptr, "location", ss_co); - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); - WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); + RNA_int_set_array(op->ptr, "location", ss_co); + WM_cursor_modal_restore(CTX_wm_window(C)); + ED_workspace_status_text(C, NULL); + WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); - return OPERATOR_FINISHED; - } - break; + return OPERATOR_FINISHED; + } + break; - case RIGHTMOUSE: - { - WM_cursor_modal_restore(CTX_wm_window(C)); - ED_workspace_status_text(C, NULL); + case RIGHTMOUSE: { + WM_cursor_modal_restore(CTX_wm_window(C)); + ED_workspace_status_text(C, NULL); - return OPERATOR_CANCELLED; - } - } + return OPERATOR_CANCELLED; + } + } - return OPERATOR_RUNNING_MODAL; + return OPERATOR_RUNNING_MODAL; } - static void SCULPT_OT_sample_detail_size(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Sample Detail Size"; - ot->idname = "SCULPT_OT_sample_detail_size"; - ot->description = "Sample the mesh detail on clicked point"; + /* identifiers */ + ot->name = "Sample Detail Size"; + ot->idname = "SCULPT_OT_sample_detail_size"; + ot->description = "Sample the mesh detail on clicked point"; - /* api callbacks */ - ot->invoke = sculpt_sample_detail_size_invoke; - ot->exec = sculpt_sample_detail_size_exec; - ot->modal = sculpt_sample_detail_size_modal; - ot->poll = sculpt_and_constant_or_manual_detail_poll; + /* api callbacks */ + ot->invoke = sculpt_sample_detail_size_invoke; + ot->exec = sculpt_sample_detail_size_exec; + ot->modal = sculpt_sample_detail_size_modal; + ot->poll = sculpt_and_constant_or_manual_detail_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - RNA_def_int_array(ot->srna, "location", 2, NULL, 0, SHRT_MAX, - "Location", "Screen Coordinates of sampling", 0, SHRT_MAX); + RNA_def_int_array(ot->srna, + "location", + 2, + NULL, + 0, + SHRT_MAX, + "Location", + "Screen Coordinates of sampling", + 0, + SHRT_MAX); } - /* Dynamic-topology detail size * * This should be improved further, perhaps by showing a triangle * grid rather than brush alpha */ static void set_brush_rc_props(PointerRNA *ptr, const char *prop) { - char *path = BLI_sprintfN("tool_settings.sculpt.brush.%s", prop); - RNA_string_set(ptr, "data_path_primary", path); - MEM_freeN(path); + char *path = BLI_sprintfN("tool_settings.sculpt.brush.%s", prop); + RNA_string_set(ptr, "data_path_primary", path); + MEM_freeN(path); } static int sculpt_set_detail_size_exec(bContext *C, wmOperator *UNUSED(op)) { - Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; - PointerRNA props_ptr; - wmOperatorType *ot = WM_operatortype_find("WM_OT_radial_control", true); + PointerRNA props_ptr; + wmOperatorType *ot = WM_operatortype_find("WM_OT_radial_control", true); - WM_operator_properties_create_ptr(&props_ptr, ot); + WM_operator_properties_create_ptr(&props_ptr, ot); - if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { - set_brush_rc_props(&props_ptr, "constant_detail_resolution"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail_resolution"); - } - else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { - set_brush_rc_props(&props_ptr, "constant_detail_resolution"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); - } - else { - set_brush_rc_props(&props_ptr, "detail_size"); - RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); - } + if (sd->flags & (SCULPT_DYNTOPO_DETAIL_CONSTANT | SCULPT_DYNTOPO_DETAIL_MANUAL)) { + set_brush_rc_props(&props_ptr, "constant_detail_resolution"); + RNA_string_set( + &props_ptr, "data_path_primary", "tool_settings.sculpt.constant_detail_resolution"); + } + else if (sd->flags & SCULPT_DYNTOPO_DETAIL_BRUSH) { + set_brush_rc_props(&props_ptr, "constant_detail_resolution"); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_percent"); + } + else { + set_brush_rc_props(&props_ptr, "detail_size"); + RNA_string_set(&props_ptr, "data_path_primary", "tool_settings.sculpt.detail_size"); + } - WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); + WM_operator_name_call_ptr(C, ot, WM_OP_INVOKE_DEFAULT, &props_ptr); - WM_operator_properties_free(&props_ptr); + WM_operator_properties_free(&props_ptr); - return OPERATOR_FINISHED; + return OPERATOR_FINISHED; } static void SCULPT_OT_set_detail_size(wmOperatorType *ot) { - /* identifiers */ - ot->name = "Set Detail Size"; - ot->idname = "SCULPT_OT_set_detail_size"; - ot->description = "Set the mesh detail (either relative or constant one, depending on current dyntopo mode)"; + /* identifiers */ + ot->name = "Set Detail Size"; + ot->idname = "SCULPT_OT_set_detail_size"; + ot->description = + "Set the mesh detail (either relative or constant one, depending on current dyntopo mode)"; - /* api callbacks */ - ot->exec = sculpt_set_detail_size_exec; - ot->poll = sculpt_and_dynamic_topology_poll; + /* api callbacks */ + ot->exec = sculpt_set_detail_size_exec; + ot->poll = sculpt_and_dynamic_topology_poll; - ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } void ED_operatortypes_sculpt(void) { - WM_operatortype_append(SCULPT_OT_brush_stroke); - WM_operatortype_append(SCULPT_OT_sculptmode_toggle); - WM_operatortype_append(SCULPT_OT_set_persistent_base); - WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); - WM_operatortype_append(SCULPT_OT_optimize); - WM_operatortype_append(SCULPT_OT_symmetrize); - WM_operatortype_append(SCULPT_OT_detail_flood_fill); - WM_operatortype_append(SCULPT_OT_sample_detail_size); - WM_operatortype_append(SCULPT_OT_set_detail_size); + WM_operatortype_append(SCULPT_OT_brush_stroke); + WM_operatortype_append(SCULPT_OT_sculptmode_toggle); + WM_operatortype_append(SCULPT_OT_set_persistent_base); + WM_operatortype_append(SCULPT_OT_dynamic_topology_toggle); + WM_operatortype_append(SCULPT_OT_optimize); + WM_operatortype_append(SCULPT_OT_symmetrize); + WM_operatortype_append(SCULPT_OT_detail_flood_fill); + WM_operatortype_append(SCULPT_OT_sample_detail_size); + WM_operatortype_append(SCULPT_OT_set_detail_size); } |