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

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors')
-rw-r--r--source/blender/editors/animation/drivers.c10
-rw-r--r--source/blender/editors/animation/keyframes_general.c35
-rw-r--r--source/blender/editors/armature/pose_edit.c2
-rw-r--r--source/blender/editors/armature/pose_lib.c2
-rw-r--r--source/blender/editors/armature/pose_select.c2
-rw-r--r--source/blender/editors/asset/intern/asset_indexer.cc7
-rw-r--r--source/blender/editors/asset/intern/asset_ops.cc8
-rw-r--r--source/blender/editors/asset/intern/asset_temp_id_consumer.cc5
-rw-r--r--source/blender/editors/gpencil/gpencil_paint.c12
-rw-r--r--source/blender/editors/include/ED_keyframes_edit.h5
-rw-r--r--source/blender/editors/include/ED_node.h22
-rw-r--r--source/blender/editors/interface/interface_ops.c24
-rw-r--r--source/blender/editors/interface/interface_templates.c2
-rw-r--r--source/blender/editors/interface/interface_view.cc4
-rw-r--r--source/blender/editors/object/object_constraint.c8
-rw-r--r--source/blender/editors/object/object_edit.c10
-rw-r--r--source/blender/editors/object/object_hook.c2
-rw-r--r--source/blender/editors/object/object_relations.c4
-rw-r--r--source/blender/editors/object/object_utils.c2
-rw-r--r--source/blender/editors/render/render_internal.c4
-rw-r--r--source/blender/editors/screen/screen_context.c2
-rw-r--r--source/blender/editors/sculpt_paint/CMakeLists.txt2
-rw-r--r--source/blender/editors/sculpt_paint/paint_image_proj.c2
-rw-r--r--source/blender/editors/sculpt_paint/paint_intern.h59
-rw-r--r--source/blender/editors/sculpt_paint/paint_utils.c45
-rw-r--r--source/blender/editors/sculpt_paint/sculpt.c3839
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_brushes.c2847
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_intern.h122
-rw-r--r--source/blender/editors/sculpt_paint/sculpt_ops.c1141
-rw-r--r--source/blender/editors/space_file/asset_catalog_tree_view.cc5
-rw-r--r--source/blender/editors/space_info/info_stats.cc3
-rw-r--r--source/blender/editors/space_node/drawnode.cc4
-rw-r--r--source/blender/editors/space_node/link_drag_search.cc10
-rw-r--r--source/blender/editors/space_node/node_add.cc49
-rw-r--r--source/blender/editors/space_node/node_draw.cc50
-rw-r--r--source/blender/editors/space_node/node_edit.cc196
-rw-r--r--source/blender/editors/space_node/node_geometry_attribute_search.cc4
-rw-r--r--source/blender/editors/space_node/node_group.cc42
-rw-r--r--source/blender/editors/space_node/node_intern.hh5
-rw-r--r--source/blender/editors/space_node/node_relationships.cc239
-rw-r--r--source/blender/editors/space_node/node_templates.cc23
-rw-r--r--source/blender/editors/space_node/space_node.cc22
-rw-r--r--source/blender/editors/space_outliner/outliner_dragdrop.c38
-rw-r--r--source/blender/editors/space_outliner/outliner_edit.c10
-rw-r--r--source/blender/editors/space_sequencer/sequencer_add.c15
-rw-r--r--source/blender/editors/space_spreadsheet/space_spreadsheet.cc9
-rw-r--r--source/blender/editors/space_topbar/space_topbar.c55
-rw-r--r--source/blender/editors/space_view3d/view3d_buttons.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_edit.c2
-rw-r--r--source/blender/editors/space_view3d/view3d_gizmo_armature.c6
-rw-r--r--source/blender/editors/transform/transform_convert.c4
-rw-r--r--source/blender/editors/transform/transform_convert.h1
-rw-r--r--source/blender/editors/transform/transform_convert_armature.c2
-rw-r--r--source/blender/editors/transform/transform_convert_mask.c7
-rw-r--r--source/blender/editors/transform/transform_convert_node.c3
-rw-r--r--source/blender/editors/transform/transform_convert_sequencer_image.c45
-rw-r--r--source/blender/editors/transform/transform_convert_tracking.c10
-rw-r--r--source/blender/editors/transform/transform_mode_timetranslate.c15
-rw-r--r--source/blender/editors/transform/transform_mode_translate.c7
-rw-r--r--source/blender/editors/transform/transform_orientations.c4
-rw-r--r--source/blender/editors/undo/ed_undo.c87
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c2
62 files changed, 4670 insertions, 4535 deletions
diff --git a/source/blender/editors/animation/drivers.c b/source/blender/editors/animation/drivers.c
index f509898f2ee..e99e4a63786 100644
--- a/source/blender/editors/animation/drivers.c
+++ b/source/blender/editors/animation/drivers.c
@@ -879,10 +879,10 @@ EnumPropertyItem prop_driver_create_mapping_types[] = {
};
/* Filtering callback for driver mapping types enum */
-static const EnumPropertyItem *driver_mapping_type_itemsf(bContext *C,
- PointerRNA *UNUSED(owner_ptr),
- PropertyRNA *UNUSED(owner_prop),
- bool *r_free)
+static const EnumPropertyItem *driver_mapping_type_itemf(bContext *C,
+ PointerRNA *UNUSED(owner_ptr),
+ PropertyRNA *UNUSED(owner_prop),
+ bool *r_free)
{
EnumPropertyItem *input = prop_driver_create_mapping_types;
EnumPropertyItem *item = NULL;
@@ -1039,7 +1039,7 @@ static void UNUSED_FUNCTION(ANIM_OT_driver_button_add_menu)(wmOperatorType *ot)
0,
"Mapping Type",
"Method used to match target and driven properties");
- RNA_def_enum_funcs(ot->prop, driver_mapping_type_itemsf);
+ RNA_def_enum_funcs(ot->prop, driver_mapping_type_itemf);
}
/* Add Driver Button Operator ------------------------ */
diff --git a/source/blender/editors/animation/keyframes_general.c b/source/blender/editors/animation/keyframes_general.c
index dc5d71b5a1e..4fa5dee99a6 100644
--- a/source/blender/editors/animation/keyframes_general.c
+++ b/source/blender/editors/animation/keyframes_general.c
@@ -345,6 +345,28 @@ static bool find_fcurve_segment(FCurve *fcu,
return in_segment;
}
+/* Return a list of FCurveSegment with a start index and a length.
+ * A segment is a continuous selection of keyframes.
+ * Keys that have BEZT_FLAG_IGNORE_TAG set are treated as unselected.
+ * The caller is responsible for freeing the memory. */
+ListBase find_fcurve_segments(FCurve *fcu)
+{
+ ListBase segments = {NULL, NULL};
+ int segment_start_idx = 0;
+ int segment_len = 0;
+ int current_index = 0;
+
+ while (find_fcurve_segment(fcu, current_index, &segment_start_idx, &segment_len)) {
+ FCurveSegment *segment;
+ segment = MEM_callocN(sizeof(*segment), "FCurveSegment");
+ segment->start_index = segment_start_idx;
+ segment->length = segment_len;
+ BLI_addtail(&segments, segment);
+ current_index = segment_start_idx + segment_len;
+ }
+ return segments;
+}
+
/* ---------------- */
/* Check if the keyframe interpolation type is supported */
@@ -440,15 +462,12 @@ bool decimate_fcurve(bAnimListElem *ale, float remove_ratio, float error_sq_max)
fcu->bezt[i].f2 &= ~BEZT_FLAG_TEMP_TAG;
}
- /* Only decimate the individual selected curve segments. */
- int segment_start_idx = 0;
- int segment_len = 0;
- int current_index = 0;
-
- while (find_fcurve_segment(fcu, current_index, &segment_start_idx, &segment_len)) {
- decimate_fcurve_segment(fcu, segment_start_idx, segment_len, remove_ratio, error_sq_max);
- current_index = segment_start_idx + segment_len;
+ ListBase segments = find_fcurve_segments(fcu);
+ LISTBASE_FOREACH (FCurveSegment *, segment, &segments) {
+ decimate_fcurve_segment(
+ fcu, segment->start_index, segment->length, remove_ratio, error_sq_max);
}
+ BLI_freelistN(&segments);
uint old_totvert = fcu->totvert;
fcu->bezt = NULL;
diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c
index 772fe8f3196..8bd6c9f54fd 100644
--- a/source/blender/editors/armature/pose_edit.c
+++ b/source/blender/editors/armature/pose_edit.c
@@ -160,7 +160,7 @@ static bool pose_has_protected_selected(Object *ob, short warn)
bArmature *arm = ob->data;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->bone && (pchan->bone->layer & arm->layer)) {
+ if (pchan->bone && BKE_pose_is_layer_visible(arm, pchan)) {
if (pchan->bone->layer & arm->layer_protected) {
if (pchan->bone->flag & BONE_SELECTED) {
break;
diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c
index 646356e7a45..dc96c777be0 100644
--- a/source/blender/editors/armature/pose_lib.c
+++ b/source/blender/editors/armature/pose_lib.c
@@ -1070,7 +1070,7 @@ static void poselib_apply_pose(tPoseLib_PreviewData *pld,
else if (pchan->bone) {
/* only ok if bone is visible and selected */
if ((pchan->bone->flag & BONE_SELECTED) && (pchan->bone->flag & BONE_HIDDEN_P) == 0 &&
- (pchan->bone->layer & arm->layer)) {
+ BKE_pose_is_layer_visible(arm, pchan)) {
ok = 1;
}
}
diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c
index 17347aa57fe..0b889149f9d 100644
--- a/source/blender/editors/armature/pose_select.c
+++ b/source/blender/editors/armature/pose_select.c
@@ -746,7 +746,7 @@ static int pose_select_hierarchy_exec(bContext *C, wmOperator *op)
const bool add_to_sel = RNA_boolean_get(op->ptr, "extend");
bool changed = false;
- pchan_act = BKE_pose_channel_active(ob);
+ pchan_act = BKE_pose_channel_active_if_layer_visible(ob);
if (pchan_act == NULL) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/asset/intern/asset_indexer.cc b/source/blender/editors/asset/intern/asset_indexer.cc
index 4107d28b5e3..fa55560d3c8 100644
--- a/source/blender/editors/asset/intern/asset_indexer.cc
+++ b/source/blender/editors/asset/intern/asset_indexer.cc
@@ -742,16 +742,15 @@ static void update_index(const char *filename, FileIndexerEntries *entries, void
static void *init_user_data(const char *root_directory, size_t root_directory_maxlen)
{
- AssetLibraryIndex *library_index = OBJECT_GUARDED_NEW(
- AssetLibraryIndex,
- StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxlen)));
+ AssetLibraryIndex *library_index = MEM_new<AssetLibraryIndex>(
+ __func__, StringRef(root_directory, BLI_strnlen(root_directory, root_directory_maxlen)));
library_index->init_unused_index_files();
return library_index;
}
static void free_user_data(void *user_data)
{
- OBJECT_GUARDED_DELETE(user_data, AssetLibraryIndex);
+ MEM_delete((AssetLibraryIndex *)user_data);
}
static void filelist_finished(void *user_data)
diff --git a/source/blender/editors/asset/intern/asset_ops.cc b/source/blender/editors/asset/intern/asset_ops.cc
index e4edff19a21..f7755aa9fea 100644
--- a/source/blender/editors/asset/intern/asset_ops.cc
+++ b/source/blender/editors/asset/intern/asset_ops.cc
@@ -934,10 +934,10 @@ static bool has_external_files(Main *bmain, struct ReportList *reports)
BKE_reportf(
callback_info.reports,
RPT_ERROR,
- "Unable to copy bundle due to %ld external dependencies; more details on the console",
- callback_info.external_files.size());
- printf("Unable to copy bundle due to %ld external dependencies:\n",
- callback_info.external_files.size());
+ "Unable to copy bundle due to %zu external dependencies; more details on the console",
+ (size_t)callback_info.external_files.size());
+ printf("Unable to copy bundle due to %zu external dependencies:\n",
+ (size_t)callback_info.external_files.size());
for (const std::string &path : callback_info.external_files) {
printf(" \"%s\"\n", path.c_str());
}
diff --git a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc
index f136c08f129..8790c907f05 100644
--- a/source/blender/editors/asset/intern/asset_temp_id_consumer.cc
+++ b/source/blender/editors/asset/intern/asset_temp_id_consumer.cc
@@ -88,12 +88,13 @@ AssetTempIDConsumer *ED_asset_temp_id_consumer_create(const AssetHandle *handle)
}
BLI_assert(handle->file_data->asset_data != nullptr);
return reinterpret_cast<AssetTempIDConsumer *>(
- OBJECT_GUARDED_NEW(AssetTemporaryIDConsumer, *handle));
+ MEM_new<AssetTemporaryIDConsumer>(__func__, *handle));
}
void ED_asset_temp_id_consumer_free(AssetTempIDConsumer **consumer)
{
- OBJECT_GUARDED_SAFE_DELETE(*consumer, AssetTemporaryIDConsumer);
+ MEM_delete(reinterpret_cast<AssetTemporaryIDConsumer *>(*consumer));
+ *consumer = nullptr;
}
ID *ED_asset_temp_id_consumer_ensure_local_id(AssetTempIDConsumer *consumer_,
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index dabe2050b28..d1fbd3bc507 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -1567,6 +1567,12 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
}
bGPDspoint npt;
+ gpencil_point_to_parent_space(pt1, p->diff_mat, &npt);
+ gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
+
+ gpencil_point_to_parent_space(pt2, p->diff_mat, &npt);
+ gpencil_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]);
+
if (pt0) {
gpencil_point_to_parent_space(pt0, p->diff_mat, &npt);
gpencil_point_to_xy(&p->gsc, gps, &npt, &pc0[0], &pc0[1]);
@@ -1576,12 +1582,6 @@ static void gpencil_stroke_eraser_dostroke(tGPsdata *p,
copy_v2_v2_int(pc0, pc1);
}
- gpencil_point_to_parent_space(pt1, p->diff_mat, &npt);
- gpencil_point_to_xy(&p->gsc, gps, &npt, &pc1[0], &pc1[1]);
-
- gpencil_point_to_parent_space(pt2, p->diff_mat, &npt);
- gpencil_point_to_xy(&p->gsc, gps, &npt, &pc2[0], &pc2[1]);
-
/* Check that point segment of the boundbox of the eraser stroke */
if (((!ELEM(V2D_IS_CLIPPED, pc0[0], pc0[1])) && BLI_rcti_isect_pt(rect, pc0[0], pc0[1])) ||
((!ELEM(V2D_IS_CLIPPED, pc1[0], pc1[1])) && BLI_rcti_isect_pt(rect, pc1[0], pc1[1])) ||
diff --git a/source/blender/editors/include/ED_keyframes_edit.h b/source/blender/editors/include/ED_keyframes_edit.h
index bafe68bd28d..8a7831db0ea 100644
--- a/source/blender/editors/include/ED_keyframes_edit.h
+++ b/source/blender/editors/include/ED_keyframes_edit.h
@@ -380,6 +380,11 @@ bool delete_fcurve_keys(struct FCurve *fcu);
void clear_fcurve_keys(struct FCurve *fcu);
void duplicate_fcurve_keys(struct FCurve *fcu);
+typedef struct FCurveSegment {
+ struct FCurveSegment *next, *prev;
+ int start_index, length;
+} FCurveSegment;
+ListBase find_fcurve_segments(struct FCurve *fcu);
void clean_fcurve(struct bAnimContext *ac,
struct bAnimListElem *ale,
float thresh,
diff --git a/source/blender/editors/include/ED_node.h b/source/blender/editors/include/ED_node.h
index 5bac452c7c9..db1ba80dd3c 100644
--- a/source/blender/editors/include/ED_node.h
+++ b/source/blender/editors/include/ED_node.h
@@ -100,7 +100,7 @@ void ED_node_socket_draw(struct bNodeSocket *sock,
float scale);
void ED_node_tree_update(const struct bContext *C);
void ED_node_tag_update_id(struct ID *id);
-void ED_node_tag_update_nodetree(struct Main *bmain, struct bNodeTree *ntree, struct bNode *node);
+
/**
* Sort nodes by selection: unselected nodes first, then selected,
* then the active node at the very end. Relative order is kept intact.
@@ -152,6 +152,26 @@ void ED_node_set_active(struct Main *bmain,
bool *r_active_texture_changed);
/**
+ * Call after one or more node trees have been changed and tagged accordingly.
+ *
+ * This function will make sure that other parts of Blender update accordingly. For example, if the
+ * node group interface changed, parent node groups have to be updated as well.
+ *
+ * Additionally, this will send notifiers and tag the depsgraph based on the changes. Depsgraph
+ * relation updates have to be triggered by the caller.
+ *
+ * \param C: Context if available. This can be null.
+ * \param bmain: Main whose data-blocks should be updated based on the changes.
+ * \param ntree: Under some circumstances the caller knows that only one node tree has
+ * changed since the last update. In this case the function may be able to skip scanning #bmain
+ * for other things that have to be changed. It may still scan #bmain if the interface of the
+ * node tree has changed.
+ */
+void ED_node_tree_propagate_change(const struct bContext *C,
+ struct Main *bmain,
+ struct bNodeTree *ntree);
+
+/**
* \param scene_owner: is the owner of the job,
* we don't use it for anything else currently so could also be a void pointer,
* but for now keep it an 'Scene' for consistency.
diff --git a/source/blender/editors/interface/interface_ops.c b/source/blender/editors/interface/interface_ops.c
index 61869f3da41..35b621b0272 100644
--- a/source/blender/editors/interface/interface_ops.c
+++ b/source/blender/editors/interface/interface_ops.c
@@ -1023,8 +1023,8 @@ bool UI_context_copy_to_selected_check(PointerRNA *ptr,
return false;
}
- /* Skip non-existing properties on link. This was previously covered with the lprop != prop check
- * but we are now more permissive when it comes to ID properties, see below. */
+ /* Skip non-existing properties on link. This was previously covered with the `lprop != prop`
+ * check but we are now more permissive when it comes to ID properties, see below. */
if (lprop == NULL) {
return false;
}
@@ -1033,19 +1033,19 @@ bool UI_context_copy_to_selected_check(PointerRNA *ptr,
return false;
}
- /* Check property pointers matching
- * For ID properties, these pointers match
- * - if the property is API defined on an existing class (and they are equally named)
- * - never for ID properties on specific ID (even if they are equally named)
- * - never for NodesModifierSettings properties (even if they are equally named)
+ /* Check property pointers matching.
+ * For ID properties, these pointers match:
+ * - If the property is API defined on an existing class (and they are equally named).
+ * - Never for ID properties on specific ID (even if they are equally named).
+ * - Never for NodesModifierSettings properties (even if they are equally named).
*
* Be permissive on ID properties in the following cases:
- * - NodesModifierSettings properties
- * - (special check: only if the nodegroup matches, since the 'Input_n' properties are name
- * based and similar on potentionally very different nodegroups)
+ * - #NodesModifierSettings properties
+ * - (special check: only if the node-group matches, since the 'Input_n' properties are name
+ * based and similar on potentially very different node-groups).
* - ID properties on specific ID
- * - (no special check, copying seems OK [even if type does not match -- does not do anything
- * then])
+ * - (no special check, copying seems OK [even if type does not match -- does not do anything
+ * then])
*/
bool ignore_prop_eq = RNA_property_is_idprop(lprop) && RNA_property_is_idprop(prop);
if (RNA_struct_is_a(lptr.type, &RNA_NodesModifier) &&
diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c
index 166a8911ca1..0b57b6adcc6 100644
--- a/source/blender/editors/interface/interface_templates.c
+++ b/source/blender/editors/interface/interface_templates.c
@@ -2672,7 +2672,7 @@ static void constraint_ops_extra_draw(bContext *C, uiLayout *layout, void *con_v
static void draw_constraint_header(uiLayout *layout, Object *ob, bConstraint *con)
{
- bPoseChannel *pchan = BKE_pose_channel_active(ob);
+ bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
short proxy_protected, xco = 0, yco = 0;
// int rb_col; // UNUSED
diff --git a/source/blender/editors/interface/interface_view.cc b/source/blender/editors/interface/interface_view.cc
index 500834f4434..81b24c75020 100644
--- a/source/blender/editors/interface/interface_view.cc
+++ b/source/blender/editors/interface/interface_view.cc
@@ -60,7 +60,7 @@ AbstractTreeView *UI_block_add_view(uiBlock &block,
StringRef idname,
std::unique_ptr<AbstractTreeView> tree_view)
{
- ViewLink *view_link = OBJECT_GUARDED_NEW(ViewLink);
+ ViewLink *view_link = MEM_new<ViewLink>(__func__);
BLI_addtail(&block.views, view_link);
view_link->view = std::move(tree_view);
@@ -72,7 +72,7 @@ AbstractTreeView *UI_block_add_view(uiBlock &block,
void ui_block_free_views(uiBlock *block)
{
LISTBASE_FOREACH_MUTABLE (ViewLink *, link, &block->views) {
- OBJECT_GUARDED_DELETE(link, ViewLink);
+ MEM_delete(link);
}
}
diff --git a/source/blender/editors/object/object_constraint.c b/source/blender/editors/object/object_constraint.c
index 5c3a8fc2277..91a512ae8e9 100644
--- a/source/blender/editors/object/object_constraint.c
+++ b/source/blender/editors/object/object_constraint.c
@@ -89,7 +89,7 @@ ListBase *ED_object_constraint_active_list(Object *ob)
if (ob->mode & OB_MODE_POSE) {
bPoseChannel *pchan;
- pchan = BKE_pose_channel_active(ob);
+ pchan = BKE_pose_channel_active_if_layer_visible(ob);
if (pchan) {
return &pchan->constraints;
}
@@ -2215,7 +2215,7 @@ static bool get_new_constraint_target(
bContext *C, int con_type, Object **tar_ob, bPoseChannel **tar_pchan, bool add)
{
Object *obact = ED_object_active_context(C);
- bPoseChannel *pchanact = BKE_pose_channel_active(obact);
+ bPoseChannel *pchanact = BKE_pose_channel_active_if_layer_visible(obact);
bool only_curve = false, only_mesh = false, only_ob = false;
bool found = false;
@@ -2370,7 +2370,7 @@ static int constraint_add_exec(
pchan = NULL;
}
else {
- pchan = BKE_pose_channel_active(ob);
+ pchan = BKE_pose_channel_active_if_layer_visible(ob);
/* ensure not to confuse object/pose adding */
if (pchan == NULL) {
@@ -2650,7 +2650,7 @@ void POSE_OT_constraint_add_with_targets(wmOperatorType *ot)
static int pose_ik_add_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C));
- bPoseChannel *pchan = BKE_pose_channel_active(ob);
+ bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
bConstraint *con = NULL;
uiPopupMenu *pup;
diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c
index f06744068d5..38d0a044cb4 100644
--- a/source/blender/editors/object/object_edit.c
+++ b/source/blender/editors/object/object_edit.c
@@ -1638,10 +1638,10 @@ void OBJECT_OT_shade_smooth(wmOperatorType *ot)
/** \name Object Mode Set Operator
* \{ */
-static const EnumPropertyItem *object_mode_set_itemsf(bContext *C,
- PointerRNA *UNUSED(ptr),
- PropertyRNA *UNUSED(prop),
- bool *r_free)
+static const EnumPropertyItem *object_mode_set_itemf(bContext *C,
+ PointerRNA *UNUSED(ptr),
+ PropertyRNA *UNUSED(prop),
+ bool *r_free)
{
const EnumPropertyItem *input = rna_enum_object_mode_items;
EnumPropertyItem *item = NULL;
@@ -1790,7 +1790,7 @@ void OBJECT_OT_mode_set(wmOperatorType *ot)
ot->prop = RNA_def_enum(
ot->srna, "mode", rna_enum_object_mode_items, OB_MODE_OBJECT, "Mode", "");
- RNA_def_enum_funcs(ot->prop, object_mode_set_itemsf);
+ RNA_def_enum_funcs(ot->prop, object_mode_set_itemf);
RNA_def_property_flag(ot->prop, PROP_SKIP_SAVE);
prop = RNA_def_boolean(ot->srna, "toggle", 0, "Toggle", "");
diff --git a/source/blender/editors/object/object_hook.c b/source/blender/editors/object/object_hook.c
index 5065a2c00f0..51967ff35c7 100644
--- a/source/blender/editors/object/object_hook.c
+++ b/source/blender/editors/object/object_hook.c
@@ -582,7 +582,7 @@ static int add_hook_object(const bContext *C,
BLI_strncpy(hmd->subtarget, arm->act_bone->name, sizeof(hmd->subtarget));
- pchan_act = BKE_pose_channel_active(ob);
+ pchan_act = BKE_pose_channel_active_if_layer_visible(ob);
if (LIKELY(pchan_act)) {
invert_m4_m4(pose_mat, pchan_act->pose_mat);
mul_v3_m4v3(cent, ob->obmat, pchan_act->pose_mat[3]);
diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c
index 811f20e82be..71d9482597d 100644
--- a/source/blender/editors/object/object_relations.c
+++ b/source/blender/editors/object/object_relations.c
@@ -583,8 +583,8 @@ bool ED_object_parent_set(ReportList *reports,
}
case PAR_BONE:
case PAR_BONE_RELATIVE:
- pchan = BKE_pose_channel_active(par);
- pchan_eval = BKE_pose_channel_active(parent_eval);
+ pchan = BKE_pose_channel_active_if_layer_visible(par);
+ pchan_eval = BKE_pose_channel_active_if_layer_visible(parent_eval);
if (pchan == NULL) {
BKE_report(reports, RPT_ERROR, "No active bone");
diff --git a/source/blender/editors/object/object_utils.c b/source/blender/editors/object/object_utils.c
index 5f85f6ea0eb..df44d840ad3 100644
--- a/source/blender/editors/object/object_utils.c
+++ b/source/blender/editors/object/object_utils.c
@@ -115,7 +115,7 @@ bool ED_object_calc_active_center_for_posemode(Object *ob,
const bool select_only,
float r_center[3])
{
- bPoseChannel *pchan = BKE_pose_channel_active(ob);
+ bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
if (pchan && (!select_only || (pchan->bone->flag & BONE_SELECTED))) {
copy_v3_v3(r_center, pchan->pose_head);
return true;
diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c
index 29d829dc131..1a9b72c1fab 100644
--- a/source/blender/editors/render/render_internal.c
+++ b/source/blender/editors/render/render_internal.c
@@ -50,6 +50,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_tree_update.h"
#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_scene.h"
@@ -725,7 +726,8 @@ static void render_endjob(void *rjv)
rj->scene->r.scemode &= ~R_NO_FRAME_UPDATE;
if (rj->single_layer) {
- nodeUpdateID(rj->scene->nodetree, &rj->scene->id);
+ BKE_ntree_update_tag_id_changed(rj->main, &rj->scene->id);
+ BKE_ntree_update_main(rj->main, NULL);
WM_main_add_notifier(NC_NODE | NA_EDITED, rj->scene);
}
diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c
index 304205d0cc4..04df90bf912 100644
--- a/source/blender/editors/screen/screen_context.c
+++ b/source/blender/editors/screen/screen_context.c
@@ -501,7 +501,7 @@ static eContextResult screen_ctx_active_pose_bone(const bContext *C, bContextDat
Object *obact = view_layer->basact ? view_layer->basact->object : NULL;
Object *obpose = BKE_object_pose_armature_get(obact);
- bPoseChannel *pchan = BKE_pose_channel_active(obpose);
+ bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(obpose);
if (pchan) {
CTX_data_pointer_set(result, &obpose->id, &RNA_PoseBone, pchan);
return CTX_RESULT_OK;
diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt
index b826ff8701d..517125f016e 100644
--- a/source/blender/editors/sculpt_paint/CMakeLists.txt
+++ b/source/blender/editors/sculpt_paint/CMakeLists.txt
@@ -59,6 +59,7 @@ set(SRC
sculpt.c
sculpt_automasking.c
sculpt_boundary.c
+ sculpt_brushes.c
sculpt_cloth.c
sculpt_detail.c
sculpt_dyntopo.c
@@ -71,6 +72,7 @@ set(SRC
sculpt_mask_expand.c
sculpt_mask_init.c
sculpt_multiplane_scrape.c
+ sculpt_ops.c
sculpt_paint_color.c
sculpt_pose.c
sculpt_smooth.c
diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c
index 7df5848e068..8a5d75d5f77 100644
--- a/source/blender/editors/sculpt_paint/paint_image_proj.c
+++ b/source/blender/editors/sculpt_paint/paint_image_proj.c
@@ -6623,7 +6623,7 @@ static bool proj_paint_add_slot(bContext *C, wmOperator *op)
}
}
- ntreeUpdateTree(CTX_data_main(C), ntree);
+ ED_node_tree_propagate_change(C, bmain, ntree);
/* In case we added more than one node, position them too. */
nodePositionPropagate(out_node);
diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h
index 90887b9fc39..09013ea00f4 100644
--- a/source/blender/editors/sculpt_paint/paint_intern.h
+++ b/source/blender/editors/sculpt_paint/paint_intern.h
@@ -25,6 +25,8 @@
#include "BKE_paint.h"
+#include "BLI_compiler_compat.h"
+#include "BLI_math.h"
#include "BLI_rect.h"
#include "DNA_scene_types.h"
@@ -404,8 +406,61 @@ bool facemask_paint_poll(struct bContext *C);
/**
* Uses symm to selectively flip any axis of a coordinate.
*/
-void flip_v3_v3(float out[3], const float in[3], const enum ePaintSymmetryFlags symm);
-void flip_qt_qt(float out[4], const float in[4], const enum ePaintSymmetryFlags symm);
+
+BLI_INLINE void flip_v3_v3(float out[3], const float in[3], const ePaintSymmetryFlags symm)
+{
+ if (symm & PAINT_SYMM_X) {
+ out[0] = -in[0];
+ }
+ else {
+ out[0] = in[0];
+ }
+ if (symm & PAINT_SYMM_Y) {
+ out[1] = -in[1];
+ }
+ else {
+ out[1] = in[1];
+ }
+ if (symm & PAINT_SYMM_Z) {
+ out[2] = -in[2];
+ }
+ else {
+ out[2] = in[2];
+ }
+}
+
+BLI_INLINE void flip_qt_qt(float out[4], const float in[4], const ePaintSymmetryFlags symm)
+{
+ float axis[3], angle;
+
+ quat_to_axis_angle(axis, &angle, in);
+ normalize_v3(axis);
+
+ if (symm & PAINT_SYMM_X) {
+ axis[0] *= -1.0f;
+ angle *= -1.0f;
+ }
+ if (symm & PAINT_SYMM_Y) {
+ axis[1] *= -1.0f;
+ angle *= -1.0f;
+ }
+ if (symm & PAINT_SYMM_Z) {
+ axis[2] *= -1.0f;
+ angle *= -1.0f;
+ }
+
+ axis_angle_normalized_to_quat(out, axis, angle);
+}
+
+BLI_INLINE void flip_v3(float v[3], const ePaintSymmetryFlags symm)
+{
+ flip_v3_v3(v, v, symm);
+}
+
+BLI_INLINE void flip_qt(float quat[4], const ePaintSymmetryFlags symm)
+{
+ flip_qt_qt(quat, quat, symm);
+}
/* stroke operator */
typedef enum BrushStrokeMode {
diff --git a/source/blender/editors/sculpt_paint/paint_utils.c b/source/blender/editors/sculpt_paint/paint_utils.c
index 541893f7957..95a0aba1ffb 100644
--- a/source/blender/editors/sculpt_paint/paint_utils.c
+++ b/source/blender/editors/sculpt_paint/paint_utils.c
@@ -397,51 +397,6 @@ static Image *imapaint_face_image(Object *ob, Mesh *me, int face_index)
return ima;
}
-void flip_v3_v3(float out[3], const float in[3], const ePaintSymmetryFlags symm)
-{
- if (symm & PAINT_SYMM_X) {
- out[0] = -in[0];
- }
- else {
- out[0] = in[0];
- }
- if (symm & PAINT_SYMM_Y) {
- out[1] = -in[1];
- }
- else {
- out[1] = in[1];
- }
- if (symm & PAINT_SYMM_Z) {
- out[2] = -in[2];
- }
- else {
- out[2] = in[2];
- }
-}
-
-void flip_qt_qt(float out[4], const float in[4], const ePaintSymmetryFlags symm)
-{
- float axis[3], angle;
-
- quat_to_axis_angle(axis, &angle, in);
- normalize_v3(axis);
-
- if (symm & PAINT_SYMM_X) {
- axis[0] *= -1.0f;
- angle *= -1.0f;
- }
- if (symm & PAINT_SYMM_Y) {
- axis[1] *= -1.0f;
- angle *= -1.0f;
- }
- if (symm & PAINT_SYMM_Z) {
- axis[2] *= -1.0f;
- angle *= -1.0f;
- }
-
- axis_angle_normalized_to_quat(out, axis, angle);
-}
-
void paint_sample_color(
bContext *C, ARegion *region, int x, int y, bool texpaint_proj, bool use_palette)
{
diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c
index b764d0e1b5b..ea13bb7adca 100644
--- a/source/blender/editors/sculpt_paint/sculpt.c
+++ b/source/blender/editors/sculpt_paint/sculpt.c
@@ -1329,103 +1329,6 @@ static void sculpt_rake_data_update(struct SculptRakeData *srd, const float co[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];
-
-#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);
-#else
- /* 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);
-
- add_v3_v3(vec_rot, sculpt_co);
- sub_v3_v3v3(r_delta, vec_rot, v_co);
-#endif
-}
-
-/**
- * Align the grab delta to the brush normal.
- *
- * \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])
-{
- /* 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;
- }
-
- 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
- * \{ */
-
-typedef struct SculptProjectVector {
- 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])
-{
- 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])
-{
-#if 0
- 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);
-#endif
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -1835,15 +1738,6 @@ static bool sculpt_brush_test_cyl(SculptBrushTest *test,
/* ===== Sculpting =====
*/
-static void flip_v3(float v[3], const ePaintSymmetryFlags symm)
-{
- flip_v3_v3(v, v, symm);
-}
-
-static void flip_qt(float quat[4], const ePaintSymmetryFlags symm)
-{
- flip_qt_qt(quat, quat, symm);
-}
static float calc_overlap(StrokeCache *cache, const char symm, const char axis, const float angle)
{
@@ -1913,9 +1807,9 @@ static float calc_symmetry_feather(Sculpt *sd, StrokeCache *cache)
* (optionally using original coordinates).
*
* Functions are:
- * - #calc_area_center
- * - #calc_area_normal
- * - #calc_area_normal_and_center
+ * - #SCULPT_calc_area_center
+ * - #SCULPT_calc_area_normal
+ * - #SCULPT_calc_area_normal_and_center
*
* \note These are all _very_ similar, when changing one, check others.
* \{ */
@@ -2137,7 +2031,7 @@ static void calc_area_normal_and_center_reduce(const void *__restrict UNUSED(use
add_v2_v2_int(join->count_co, anctd->count_co);
}
-static void calc_area_center(
+void SCULPT_calc_area_center(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3])
{
const Brush *brush = BKE_paint_brush(&sd->paint);
@@ -2238,7 +2132,7 @@ bool SCULPT_pbvh_calc_area_normal(const Brush *brush,
* 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(
+void SCULPT_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);
@@ -2856,10 +2750,6 @@ static void update_brush_local_mat(Sculpt *sd, Object *ob)
/** \} */
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Topology Rake (Shared Utility)
- * \{ */
-
typedef struct {
SculptSession *ss;
const float *ray_start;
@@ -2885,1295 +2775,6 @@ typedef struct {
bool original;
} SculptFindNearestToRayData;
-static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__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);
- normalize_v3(direction);
-
- /* Cancel if there's no grab data. */
- if (is_zero_v3(direction)) {
- return;
- }
-
- const float bstrength = clamp_f(data->strength, 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- const float fade =
- bstrength *
- SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) *
- ss->cache->pressure;
-
- float avg[3], val[3];
-
- SCULPT_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 bmesh_topology_rake(
- Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength)
-{
- Brush *brush = BKE_paint_brush(&sd->paint);
- const float strength = clamp_f(bstrength, 0.0f, 1.0f);
-
- /* Interactions increase both strength and quality. */
- const int iterations = 3;
-
- int iteration;
- const int count = iterations * strength + 1;
- const float factor = iterations * strength / count;
-
- for (iteration = 0; iteration <= count; iteration++) {
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .strength = factor,
- };
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
-
- BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings);
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Mask Brush
- * \{ */
-
-static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- const float bstrength = ss->cache->bstrength;
-
- PBVHVertexIter vd;
-
- SculptBrushTest test;
- SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
- ss, &test, data->brush->falloff_shape);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
-
- const float fade = SCULPT_brush_strength_factor(
- ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
-
- if (bstrength > 0.0f) {
- (*vd.mask) += fade * bstrength * (1.0f - *vd.mask);
- }
- else {
- (*vd.mask) += fade * bstrength * (*vd.mask);
- }
- *vd.mask = clamp_f(*vd.mask, 0.0f, 1.0f);
-
- 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);
-
- /* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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);
-
- switch ((BrushMaskTool)brush->mask_tool) {
- case BRUSH_MASK_DRAW:
- do_mask_brush_draw(sd, ob, nodes, totnode);
- break;
- case BRUSH_MASK_SMOOTH:
- SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true);
- break;
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Multires Displacement Eraser Brush
- * \{ */
-
-static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f);
-
- float(*proxy)[3] = 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- float limit_co[3];
- float disp[3];
- SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co);
- sub_v3_v3v3(disp, limit_co, vd.co);
- mul_v3_v3fl(proxy[vd.i], disp, fade);
-
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- Brush *brush = BKE_paint_brush(&sd->paint);
- BKE_curvemapping_init(brush->curve);
-
- /* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_displacement_eraser_brush_task_cb_ex, &settings);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Multires Displacement Smear Brush
- * \{ */
-
-static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- const float bstrength = clamp_f(ss->cache->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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- float current_disp[3];
- float current_disp_norm[3];
- float interp_limit_surface_disp[3];
-
- copy_v3_v3(interp_limit_surface_disp, ss->cache->prev_displacement[vd.index]);
-
- switch (brush->smear_deform_type) {
- case BRUSH_SMEAR_DEFORM_DRAG:
- sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
- break;
- case BRUSH_SMEAR_DEFORM_PINCH:
- sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
- break;
- case BRUSH_SMEAR_DEFORM_EXPAND:
- sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
- break;
- }
-
- normalize_v3_v3(current_disp_norm, current_disp);
- mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
-
- float weights_accum = 1.0f;
-
- SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
- float vertex_disp[3];
- float vertex_disp_norm[3];
- float neighbor_limit_co[3];
- SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co);
- sub_v3_v3v3(vertex_disp,
- ss->cache->limit_surface_co[ni.index],
- ss->cache->limit_surface_co[vd.index]);
- const float *neighbor_limit_surface_disp = ss->cache->prev_displacement[ni.index];
- normalize_v3_v3(vertex_disp_norm, vertex_disp);
-
- if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) {
- continue;
- }
-
- const float disp_interp = clamp_f(
- -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f);
- madd_v3_v3fl(interp_limit_surface_disp, neighbor_limit_surface_disp, disp_interp);
- weights_accum += disp_interp;
- }
- SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
-
- mul_v3_fl(interp_limit_surface_disp, 1.0f / weights_accum);
-
- float new_co[3];
- add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp);
- interp_v3_v3v3(vd.co, vd.co, new_co, fade);
-
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_displacement_smear_store_prev_disp_task_cb_ex(
- void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
-
- PBVHVertexIter vd;
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- sub_v3_v3v3(ss->cache->prev_displacement[vd.index],
- SCULPT_vertex_co_get(ss, vd.index),
- ss->cache->limit_surface_co[vd.index]);
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- Brush *brush = BKE_paint_brush(&sd->paint);
- SculptSession *ss = ob->sculpt;
-
- BKE_curvemapping_init(brush->curve);
-
- const int totvert = SCULPT_vertex_count_get(ss);
- if (!ss->cache->prev_displacement) {
- ss->cache->prev_displacement = MEM_malloc_arrayN(
- totvert, sizeof(float[3]), "prev displacement");
- ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co");
- for (int i = 0; i < totvert; i++) {
- SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]);
- sub_v3_v3v3(ss->cache->prev_displacement[i],
- SCULPT_vertex_co_get(ss, i),
- ss->cache->limit_surface_co[i]);
- }
- }
- /* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(
- 0, totnode, &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings);
- BLI_task_parallel_range(0, totnode, &data, do_displacement_smear_brush_task_cb_ex, &settings);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Draw Brush
- * \{ */
-
-static void do_draw_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
- /* Offset vertex. */
- const float fade = SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- mul_v3_v3fl(proxy[vd.i], offset, fade);
-
- 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;
-
- /* Offset with as much as possible factored in already. */
- float effective_normal[3];
- SCULPT_tilt_effective_normal_get(ss, brush, effective_normal);
- mul_v3_v3fl(offset, effective_normal, 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. */
- BKE_curvemapping_init(brush->curve);
-
- /* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .offset = offset,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings);
-}
-
-static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- const float *offset = data->offset;
-
- PBVHVertexIter vd;
- SculptOrigVertData orig_data;
- float(*proxy)[3];
-
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- /* Offset vertex. */
- const float fade = SCULPT_brush_strength_factor(ss,
- brush,
- orig_data.co,
- sqrtf(test.dist),
- orig_data.no,
- NULL,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- mul_v3_v3fl(proxy[vd.i], offset, fade);
-
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_draw_sharp_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;
-
- /* Offset with as much as possible factored in already. */
- float effective_normal[3];
- SCULPT_tilt_effective_normal_get(ss, brush, effective_normal);
- mul_v3_v3fl(offset, effective_normal, 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. */
- BKE_curvemapping_init(brush->curve);
-
- /* Threaded loop over nodes. */
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .offset = offset,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Topology Brush
- * \{ */
-
-static void do_topology_slide_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
-
- PBVHVertexIter vd;
- SculptOrigVertData orig_data;
- float(*proxy)[3];
-
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- const float fade = SCULPT_brush_strength_factor(ss,
- brush,
- orig_data.co,
- sqrtf(test.dist),
- orig_data.no,
- NULL,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
- float current_disp[3];
- float current_disp_norm[3];
- float final_disp[3] = {0.0f, 0.0f, 0.0f};
-
- switch (brush->slide_deform_type) {
- case BRUSH_SLIDE_DEFORM_DRAG:
- sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
- break;
- case BRUSH_SLIDE_DEFORM_PINCH:
- sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
- break;
- case BRUSH_SLIDE_DEFORM_EXPAND:
- sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
- break;
- }
-
- normalize_v3_v3(current_disp_norm, current_disp);
- mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
-
- SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
- float vertex_disp[3];
- float vertex_disp_norm[3];
- sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
- normalize_v3_v3(vertex_disp_norm, vertex_disp);
- if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) {
- madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp));
- }
- }
- SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
-
- mul_v3_v3fl(proxy[vd.i], final_disp, fade);
-
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-void SCULPT_relax_vertex(SculptSession *ss,
- PBVHVertexIter *vd,
- float factor,
- bool filter_boundary_face_sets,
- float *r_final_pos)
-{
- float smooth_pos[3];
- float final_disp[3];
- float boundary_normal[3];
- int avg_count = 0;
- int neighbor_count = 0;
- zero_v3(smooth_pos);
- zero_v3(boundary_normal);
- const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index);
-
- SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
- neighbor_count++;
- if (!filter_boundary_face_sets ||
- (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) {
-
- /* When the vertex to relax is boundary, use only connected boundary vertices for the average
- * position. */
- if (is_boundary) {
- if (!SCULPT_vertex_is_boundary(ss, ni.index)) {
- continue;
- }
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
- avg_count++;
-
- /* Calculate a normal for the constraint plane using the edges of the boundary. */
- float to_neighbor[3];
- sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co);
- normalize_v3(to_neighbor);
- add_v3_v3(boundary_normal, to_neighbor);
- }
- else {
- add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
- avg_count++;
- }
- }
- }
- SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
-
- /* Don't modify corner vertices. */
- if (neighbor_count <= 2) {
- copy_v3_v3(r_final_pos, vd->co);
- return;
- }
-
- if (avg_count > 0) {
- mul_v3_fl(smooth_pos, 1.0f / avg_count);
- }
- else {
- copy_v3_v3(r_final_pos, vd->co);
- return;
- }
-
- float plane[4];
- float smooth_closest_plane[3];
- float vno[3];
-
- if (is_boundary && avg_count == 2) {
- normalize_v3_v3(vno, boundary_normal);
- }
- else {
- SCULPT_vertex_normal_get(ss, vd->index, vno);
- }
-
- if (is_zero_v3(vno)) {
- copy_v3_v3(r_final_pos, vd->co);
- return;
- }
-
- plane_from_point_normal_v3(plane, vd->co, vno);
- closest_to_plane_v3(smooth_closest_plane, plane, smooth_pos);
- sub_v3_v3v3(final_disp, smooth_closest_plane, vd->co);
-
- mul_v3_fl(final_disp, factor);
- add_v3_v3v3(r_final_pos, vd->co, final_disp);
-}
-
-static void do_topology_relax_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- const float bstrength = ss->cache->bstrength;
-
- PBVHVertexIter vd;
- SculptOrigVertData orig_data;
-
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
-
- BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]);
-
- SculptBrushTest test;
- SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
- ss, &test, data->brush->falloff_shape);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- const float fade = SCULPT_brush_strength_factor(ss,
- brush,
- orig_data.co,
- sqrtf(test.dist),
- orig_data.no,
- NULL,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co);
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
- return;
- }
-
- BKE_curvemapping_init(brush->curve);
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- if (ss->cache->alt_smooth) {
- SCULPT_boundary_info_ensure(ob);
- for (int i = 0; i < 4; i++) {
- BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings);
- }
- }
- else {
- BLI_task_parallel_range(0, totnode, &data, do_topology_slide_task_cb_ex, &settings);
- }
-}
-
-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 (SCULPT_stroke_is_main_symmetry_pass(ss->cache) &&
- (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) ||
- !(brush->flag & BRUSH_ORIGINAL_PLANE) || !(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.0f, 0.0f, 0.0f);
- break;
-
- case SCULPT_DISP_DIR_Y:
- ARRAY_SET_ITEMS(r_area_no, 0.0f, 1.0f, 0.0f);
- break;
-
- case SCULPT_DISP_DIR_Z:
- ARRAY_SET_ITEMS(r_area_no, 0.0f, 0.0f, 1.0f);
- 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. */
- if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) &&
- (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
- copy_v3_v3(r_area_no, ss->cache->sculpt_normal);
- }
- else {
- copy_v3_v3(ss->cache->sculpt_normal, r_area_no);
- }
-
- /* For flatten center. */
- if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) &&
- (brush->flag & BRUSH_ORIGINAL_PLANE)) {
- copy_v3_v3(r_area_co, ss->cache->last_center);
- }
- else {
- 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);
- }
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Crease & Blob Brush
- * \{ */
-
-/**
- * Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB'
- */
-static void do_crease_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
- /* Offset vertex. */
- const float fade = SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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.0f) ? -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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- float(*stroke_xz)[3] = data->stroke_xz;
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- float x_object_space[3];
- float z_object_space[3];
- copy_v3_v3(x_object_space, stroke_xz[0]);
- copy_v3_v3(z_object_space, stroke_xz[1]);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
- float disp_center[3];
- float x_disp[3];
- float z_disp[3];
- /* Calculate displacement from the vertex to the brush center. */
- sub_v3_v3v3(disp_center, test.location, vd.co);
-
- /* Project the displacement into the X vector (aligned to the stroke). */
- mul_v3_v3fl(x_disp, x_object_space, dot_v3v3(disp_center, x_object_space));
-
- /* Project the displacement into the Z vector (aligned to the surface normal). */
- mul_v3_v3fl(z_disp, z_object_space, dot_v3v3(disp_center, z_object_space));
-
- /* Add the two projected vectors to calculate the final displacement.
- * The Y component is removed. */
- add_v3_v3v3(disp_center, x_disp, z_disp);
-
- if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
- project_plane_v3_v3v3(disp_center, disp_center, ss->cache->view_normal);
- }
- mul_v3_v3fl(proxy[vd.i], disp_center, 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)
-{
- SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- float area_no[3];
- float area_co[3];
-
- float mat[4][4];
- calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co);
-
- /* delay the first daub because grab delta is not setup */
- if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
- return;
- }
-
- if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
- return;
- }
-
- /* Initialize `mat`. */
- cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
- mat[0][3] = 0.0f;
- cross_v3_v3v3(mat[1], area_no, mat[0]);
- mat[1][3] = 0.0f;
- copy_v3_v3(mat[2], area_no);
- mat[2][3] = 0.0f;
- copy_v3_v3(mat[3], ss->cache->location);
- mat[3][3] = 1.0f;
- normalize_m4(mat);
-
- float stroke_xz[2][3];
- normalize_v3_v3(stroke_xz[0], mat[0]);
- normalize_v3_v3(stroke_xz[1], mat[2]);
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .stroke_xz = stroke_xz,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__restrict tls)
-{
- 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;
-
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE;
-
- 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)) {
- continue;
- }
- float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- orig_data.co,
- sqrtf(test.dist),
- orig_data.no,
- NULL,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- if (grab_silhouette) {
- float silhouette_test_dir[3];
- normalize_v3_v3(silhouette_test_dir, grab_delta);
- if (dot_v3v3(ss->cache->initial_normal, ss->cache->grab_delta_symmetry) < 0.0f) {
- mul_v3_fl(silhouette_test_dir, -1.0f);
- }
- float vno[3];
- normal_short_to_float_v3(vno, orig_data.no);
- fade *= max_ff(dot_v3v3(vno, silhouette_test_dir), 0.0f);
- }
-
- mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
-
- 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];
-
- 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);
- }
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .grab_delta = grab_delta,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings);
-}
-
-static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict UNUSED(tls))
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- const float *grab_delta = data->grab_delta;
- const float *location = ss->cache->location;
-
- 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]);
-
- proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
-
- float dir;
- if (ss->cache->mouse[0] > ss->cache->initial_mouse[0]) {
- dir = 1.0f;
- }
- else {
- dir = -1.0f;
- }
-
- if (brush->elastic_deform_type == BRUSH_ELASTIC_DEFORM_TWIST) {
- int symm = ss->cache->mirror_symmetry_pass;
- if (ELEM(symm, 1, 2, 4, 7)) {
- dir = -dir;
- }
- }
-
- KelvinletParams params;
- float force = len_v3(grab_delta) * dir * bstrength;
- BKE_kelvinlet_init_params(
- &params, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- SCULPT_orig_vert_data_update(&orig_data, &vd);
- float final_disp[3];
- switch (brush->elastic_deform_type) {
- case BRUSH_ELASTIC_DEFORM_GRAB:
- BKE_kelvinlet_grab(final_disp, &params, orig_data.co, location, grab_delta);
- mul_v3_fl(final_disp, bstrength * 20.0f);
- break;
- case BRUSH_ELASTIC_DEFORM_GRAB_BISCALE: {
- BKE_kelvinlet_grab_biscale(final_disp, &params, orig_data.co, location, grab_delta);
- mul_v3_fl(final_disp, bstrength * 20.0f);
- break;
- }
- case BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE: {
- BKE_kelvinlet_grab_triscale(final_disp, &params, orig_data.co, location, grab_delta);
- mul_v3_fl(final_disp, bstrength * 20.0f);
- break;
- }
- case BRUSH_ELASTIC_DEFORM_SCALE:
- BKE_kelvinlet_scale(
- final_disp, &params, orig_data.co, location, ss->cache->sculpt_normal_symm);
- break;
- case BRUSH_ELASTIC_DEFORM_TWIST:
- BKE_kelvinlet_twist(
- final_disp, &params, orig_data.co, location, ss->cache->sculpt_normal_symm);
- break;
- }
-
- if (vd.mask) {
- mul_v3_fl(final_disp, 1.0f - *vd.mask);
- }
-
- mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
-
- copy_v3_v3(proxy[vd.i], final_disp);
-
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- BKE_pbvh_vertex_iter_end;
-}
-
-static void do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- 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);
-
- 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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings);
-}
-
ePaintSymmetryAreas SCULPT_get_vertex_symm_area(const float co[3])
{
ePaintSymmetryAreas symm_area = PAINT_SYMM_AREA_DEFAULT;
@@ -4257,7 +2858,7 @@ void SCULPT_calc_brush_plane(
break;
case SCULPT_DISP_DIR_AREA:
- calc_area_normal_and_center(sd, ob, nodes, totnode, r_area_no, r_area_co);
+ SCULPT_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);
@@ -4271,7 +2872,7 @@ void SCULPT_calc_brush_plane(
/* 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);
+ SCULPT_calc_area_center(sd, ob, nodes, totnode, r_area_co);
}
/* For area normal. */
@@ -4316,564 +2917,12 @@ void SCULPT_calc_brush_plane(
}
}
-static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- 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;
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- mul_v3_v3fl(proxy[vd.i], cono, fade);
-
- 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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__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;
-
- const bool do_elastic = brush->snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC;
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- KelvinletParams params;
- BKE_kelvinlet_init_params(&params, ss->cache->radius, bstrength, 1.0f, 0.4f);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
-
- float fade;
- if (do_elastic) {
- fade = 1.0f;
- }
- else {
- fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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 (do_elastic) {
- float disp[3];
- BKE_kelvinlet_grab_triscale(disp, &params, vd.co, ss->cache->location, proxy[vd.i]);
- mul_v3_fl(disp, bstrength * 20.0f);
- if (vd.mask) {
- mul_v3_fl(disp, 1.0f - *vd.mask);
- }
- mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
- copy_v3_v3(proxy[vd.i], disp);
- }
-
- 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];
-
- SculptProjectVector spvc;
-
- copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
-
- if (bstrength < 0.0f) {
- negate_v3(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);
- }
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .spvc = &spvc,
- .grab_delta = grab_delta,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__restrict tls)
-{
- 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;
-
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- orig_data.co,
- sqrtf(test.dist),
- orig_data.no,
- NULL,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- mul_v3_v3fl(proxy[vd.i], cono, fade);
-
- 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];
-
- 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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__restrict tls)
-{
- 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;
-
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- float vec[3], rot[3][3];
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- orig_data.co,
- sqrtf(test.dist),
- orig_data.no,
- NULL,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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);
-
- 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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- Sculpt *sd = data->sd;
- const Brush *brush = data->brush;
-
- const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT;
-
- PBVHVertexIter vd;
- SculptOrigVertData orig_data;
- const float bstrength = ss->cache->bstrength;
- SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
-
- SculptBrushTest test;
- SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
- ss, &test, data->brush->falloff_shape);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- const float fade = SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
-
- const int vi = vd.index;
- float *disp_factor;
- if (use_persistent_base) {
- disp_factor = &ss->persistent_base[vi].disp;
- }
- else {
- disp_factor = &ss->cache->layer_displacement_factor[vi];
- }
-
- /* When using persistent base, the layer brush (holding Control) invert mode resets the
- * height of the layer to 0. This makes possible to clean edges of previously added layers
- * on top of the base. */
- /* The main direction of the layers is inverted using the regular brush strength with the
- * brush direction property. */
- if (use_persistent_base && ss->cache->invert) {
- (*disp_factor) += fabsf(fade * bstrength * (*disp_factor)) *
- ((*disp_factor) > 0.0f ? -1.0f : 1.0f);
- }
- else {
- (*disp_factor) += fade * bstrength * (1.05f - fabsf(*disp_factor));
- }
- if (vd.mask) {
- const float clamp_mask = 1.0f - *vd.mask;
- *disp_factor = clamp_f(*disp_factor, -clamp_mask, clamp_mask);
- }
- else {
- *disp_factor = clamp_f(*disp_factor, -1.0f, 1.0f);
- }
-
- float final_co[3];
- float normal[3];
-
- if (use_persistent_base) {
- SCULPT_vertex_persistent_normal_get(ss, vi, normal);
- mul_v3_fl(normal, brush->height);
- madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor);
- }
- else {
- normal_short_to_float_v3(normal, orig_data.no);
- mul_v3_fl(normal, brush->height);
- madd_v3_v3v3fl(final_co, orig_data.co, normal, *disp_factor);
- }
-
- float vdisp[3];
- sub_v3_v3v3(vdisp, final_co, vd.co);
- mul_v3_fl(vdisp, fabsf(fade));
- add_v3_v3v3(final_co, vd.co, vdisp);
-
- SCULPT_clip(sd, ss, vd.co, final_co);
-
- 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)
-{
- SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- if (ss->cache->layer_displacement_factor == NULL) {
- ss->cache->layer_displacement_factor = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss),
- "layer displacement factor");
- }
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings);
-}
-
-static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- thread_id);
- float val[3];
-
- if (vd.fno) {
- copy_v3_v3(val, vd.fno);
- }
- else {
- normal_short_to_float_v3(val, vd.no);
- }
-
- 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;
-}
-
-static void do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings);
-}
-
int SCULPT_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)));
}
-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;
-}
-
int SCULPT_plane_point_side(const float co[3], const float plane[4])
{
float d = plane_point_side_v3(plane, co);
@@ -4893,807 +2942,6 @@ float SCULPT_brush_plane_offset_get(Sculpt *sd, SculptSession *ss)
return rv;
}
-static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
- 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 (SCULPT_plane_trim(ss->cache, brush, val)) {
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- const float radius = ss->cache->radius;
-
- float area_no[3];
- float area_co[3];
-
- float offset = SCULPT_brush_plane_offset_get(sd, ss);
- float displace;
- float temp[3];
-
- SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
-
- SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor);
-
- displace = radius * offset;
-
- 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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Clay Brush
- * \{ */
-
-typedef struct ClaySampleData {
- float plane_dist[2];
-} ClaySampleData;
-
-static void calc_clay_surface_task_cb(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict tls)
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
- const Brush *brush = data->brush;
- ClaySampleData *csd = tls->userdata_chunk;
- const float *area_no = data->area_no;
- const float *area_co = data->area_co;
- float plane[4];
-
- PBVHVertexIter vd;
-
- SculptBrushTest test;
- SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
- ss, &test, brush->falloff_shape);
-
- /* Apply the brush normal radius to the test before sampling. */
- float test_radius = sqrtf(test.radius_squared);
- test_radius *= brush->normal_radius_factor;
- test.radius_squared = test_radius * test_radius;
- plane_from_point_normal_v3(plane, area_co, area_no);
-
- if (is_zero_v4(plane)) {
- return;
- }
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
-
- float plane_dist = dist_signed_to_plane_v3(vd.co, plane);
- float plane_dist_abs = fabsf(plane_dist);
- if (plane_dist > 0.0f) {
- csd->plane_dist[0] = MIN2(csd->plane_dist[0], plane_dist_abs);
- }
- else {
- csd->plane_dist[1] = MIN2(csd->plane_dist[1], plane_dist_abs);
- }
- BKE_pbvh_vertex_iter_end;
- }
-}
-
-static void calc_clay_surface_reduce(const void *__restrict UNUSED(userdata),
- void *__restrict chunk_join,
- void *__restrict chunk)
-{
- ClaySampleData *join = chunk_join;
- ClaySampleData *csd = chunk;
- join->plane_dist[0] = MIN2(csd->plane_dist[0], join->plane_dist[0]);
- join->plane_dist[1] = MIN2(csd->plane_dist[1], join->plane_dist[1]);
-}
-
-static void do_clay_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__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 = fabsf(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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
-
- float intr[3];
- float val[3];
- closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
-
- sub_v3_v3v3(val, intr, vd.co);
-
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- const float radius = fabsf(ss->cache->radius);
- const float initial_radius = fabsf(ss->cache->initial_radius);
- bool flip = ss->cache->bstrength < 0.0f;
-
- float offset = SCULPT_brush_plane_offset_get(sd, ss);
- float displace;
-
- float area_no[3];
- float area_co[3];
- float temp[3];
-
- SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
-
- SculptThreadedTaskData sample_data = {
- .sd = NULL,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .totnode = totnode,
- .area_no = area_no,
- .area_co = ss->cache->location,
- };
-
- ClaySampleData csd = {{0}};
-
- TaskParallelSettings sample_settings;
- BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode);
- sample_settings.func_reduce = calc_clay_surface_reduce;
- sample_settings.userdata_chunk = &csd;
- sample_settings.userdata_chunk_size = sizeof(ClaySampleData);
-
- BLI_task_parallel_range(0, totnode, &sample_data, calc_clay_surface_task_cb, &sample_settings);
-
- float d_offset = (csd.plane_dist[0] + csd.plane_dist[1]);
- d_offset = min_ff(radius, d_offset);
- d_offset = d_offset / radius;
- d_offset = 1.0f - d_offset;
- displace = fabsf(initial_radius * (0.25f + offset + (d_offset * 0.15f)));
- if (flip) {
- displace = -displace;
- }
-
- mul_v3_v3v3(temp, area_no, ss->cache->scale);
- mul_v3_fl(temp, displace);
- copy_v3_v3(area_co, ss->cache->location);
- add_v3_v3(area_co, temp);
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .area_no = area_no,
- .area_co = area_co,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__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;
-
- PBVHVertexIter vd;
- SculptBrushTest test;
- float(*proxy)[3];
- const bool flip = (ss->cache->bstrength < 0.0f);
- const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength;
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) {
- continue;
- }
-
- if (!plane_point_side_flip(vd.co, test.plane_tool, flip)) {
- continue;
- }
-
- 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 (!SCULPT_plane_trim(ss->cache, brush, val)) {
- continue;
- }
- /* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- ss->cache->radius * test.dist,
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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_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.0f);
- const float radius = flip ? -ss->cache->radius : ss->cache->radius;
- const float offset = SCULPT_brush_plane_offset_get(sd, ss);
- const float displace = radius * (0.18f + offset);
-
- /* The sculpt-plane normal (whatever its set to). */
- float area_no_sp[3];
-
- /* Geometry normal */
- float area_no[3];
- float area_co[3];
-
- float temp[3];
- float mat[4][4];
- float scale[4][4];
- float tmat[4][4];
-
- SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co);
- SCULPT_tilt_apply_to_normal(area_no_sp, ss->cache, brush->tilt_strength_factor);
-
- if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
- SCULPT_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 (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
- return;
- }
-
- if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
- return;
- }
-
- mul_v3_v3v3(temp, area_no_sp, ss->cache->scale);
- mul_v3_fl(temp, displace);
- add_v3_v3(area_co, temp);
-
- /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the
- * vertices. When in Add mode, vertices that are below the plane and inside the cube are move
- * towards the plane. In this situation, there may be cases where a vertex is outside the cube
- * but below the plane, so won't be deformed, causing artifacts. In order to prevent these
- * artifacts, this displaces the test cube space in relation to the plane in order to
- * deform more vertices that may be below it. */
- /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were set
- * by doing multiple tests using the default "Clay Strips" brush preset. */
- float area_co_displaced[3];
- madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f);
-
- /* Initialize brush local-space matrix. */
- cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
- mat[0][3] = 0.0f;
- cross_v3_v3v3(mat[1], area_no, mat[0]);
- mat[1][3] = 0.0f;
- copy_v3_v3(mat[2], area_no);
- mat[2][3] = 0.0f;
- copy_v3_v3(mat[3], area_co_displaced);
- mat[3][3] = 1.0f;
- normalize_m4(mat);
-
- /* Scale brush local space matrix. */
- scale_m4_fl(scale, ss->cache->radius);
- mul_m4_m4m4(tmat, mat, scale);
-
- /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in
- * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices
- * during big deformation while keeping the surface as uniform as possible. */
- mul_v3_fl(tmat[2], 1.25f);
-
- 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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- 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)) {
- continue;
- }
-
- if (!SCULPT_plane_point_side(vd.co, test.plane_tool)) {
- continue;
- }
-
- 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 (!SCULPT_plane_trim(ss->cache, brush, val)) {
- continue;
- }
-
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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);
-
- const float radius = ss->cache->radius;
-
- float area_no[3];
- float area_co[3];
- float offset = SCULPT_brush_plane_offset_get(sd, ss);
-
- float displace;
-
- float temp[3];
-
- SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
-
- SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor);
-
- displace = radius * offset;
-
- 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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- 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 TaskParallelTLS *__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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
- 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)) {
- continue;
- }
-
- if (SCULPT_plane_point_side(vd.co, test.plane_tool)) {
- continue;
- }
-
- 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 (!SCULPT_plane_trim(ss->cache, brush, val)) {
- continue;
- }
-
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- const float radius = ss->cache->radius;
-
- float area_no[3];
- float area_co[3];
- float offset = SCULPT_brush_plane_offset_get(sd, ss);
-
- float displace;
-
- float temp[3];
-
- SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
-
- SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor);
-
- displace = -radius * offset;
-
- 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,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings);
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
-/** \name Sculpt Clay Thumb Brush
- * \{ */
-
-static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__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;
-
- PBVHVertexIter vd;
- float(*proxy)[3];
- const float bstrength = data->clay_strength;
-
- 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);
- const int thread_id = BLI_task_parallel_thread_id(tls);
-
- float plane_tilt[4];
- float normal_tilt[3];
- float imat[4][4];
-
- invert_m4_m4(imat, mat);
- rotate_v3_v3v3fl(normal_tilt, area_no_sp, imat[0], DEG2RADF(-ss->cache->clay_thumb_front_angle));
-
- /* Plane aligned to the geometry normal (back part of the brush). */
- plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp);
- /* Tilted plane (front part of the brush). */
- plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt);
-
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
- continue;
- }
- float local_co[3];
- mul_v3_m4v3(local_co, mat, vd.co);
- float intr[3], intr_tilt[3];
- float val[3];
-
- closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
- closest_to_plane_normalized_v3(intr_tilt, plane_tilt, vd.co);
-
- /* Mix the deformation of the aligned and the tilted plane based on the brush space vertex
- * coordinates. */
- /* We can also control the mix with a curve if it produces noticeable artifacts in the center
- * of the brush. */
- const float tilt_mix = local_co[1] > 0.0f ? 0.0f : 1.0f;
- interp_v3_v3v3(intr, intr, intr_tilt, tilt_mix);
- sub_v3_v3v3(val, intr_tilt, vd.co);
-
- const float fade = bstrength * SCULPT_brush_strength_factor(ss,
- brush,
- vd.co,
- sqrtf(test.dist),
- vd.no,
- vd.fno,
- vd.mask ? *vd.mask : 0.0f,
- vd.index,
- 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 float sculpt_clay_thumb_get_stabilized_pressure(StrokeCache *cache)
-{
- float final_pressure = 0.0f;
- for (int i = 0; i < SCULPT_CLAY_STABILIZER_LEN; i++) {
- final_pressure += cache->clay_pressure_stabilizer[i];
- }
- return final_pressure / SCULPT_CLAY_STABILIZER_LEN;
-}
-
-static void do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
-{
- SculptSession *ss = ob->sculpt;
- Brush *brush = BKE_paint_brush(&sd->paint);
-
- const float radius = ss->cache->radius;
- const float offset = SCULPT_brush_plane_offset_get(sd, ss);
- const float displace = radius * (0.25f + offset);
-
- /* Sampled geometry normal and area center. */
- float area_no_sp[3];
- float area_no[3];
- float area_co[3];
-
- float temp[3];
- float mat[4][4];
- float scale[4][4];
- float tmat[4][4];
-
- SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co);
-
- if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
- SCULPT_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 (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
- ss->cache->clay_thumb_front_angle = 0.0f;
- return;
- }
-
- /* Simulate the clay accumulation by increasing the plane angle as more samples are added to the
- * stroke. */
- if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
- ss->cache->clay_thumb_front_angle += 0.8f;
- ss->cache->clay_thumb_front_angle = clamp_f(ss->cache->clay_thumb_front_angle, 0.0f, 60.0f);
- }
-
- if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
- return;
- }
-
- /* Displace the brush planes. */
- copy_v3_v3(area_co, ss->cache->location);
- mul_v3_v3v3(temp, area_no_sp, ss->cache->scale);
- mul_v3_fl(temp, displace);
- add_v3_v3(area_co, temp);
-
- /* Initialize brush local-space matrix. */
- cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
- mat[0][3] = 0.0f;
- cross_v3_v3v3(mat[1], area_no, mat[0]);
- mat[1][3] = 0.0f;
- copy_v3_v3(mat[2], area_no);
- mat[2][3] = 0.0f;
- copy_v3_v3(mat[3], ss->cache->location);
- mat[3][3] = 1.0f;
- normalize_m4(mat);
-
- /* Scale brush local space matrix. */
- scale_m4_fl(scale, ss->cache->radius);
- mul_m4_m4m4(tmat, mat, scale);
- invert_m4_m4(mat, tmat);
-
- float clay_strength = ss->cache->bstrength *
- sculpt_clay_thumb_get_stabilized_pressure(ss->cache);
-
- SculptThreadedTaskData data = {
- .sd = sd,
- .ob = ob,
- .brush = brush,
- .nodes = nodes,
- .area_no_sp = area_no_sp,
- .area_co = ss->cache->location,
- .mat = mat,
- .clay_strength = clay_strength,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
@@ -6036,7 +3284,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
/* Apply one type of brush action. */
switch (brush->sculpt_tool) {
case SCULPT_TOOL_DRAW:
- do_draw_brush(sd, ob, nodes, totnode);
+ SCULPT_do_draw_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SMOOTH:
if (brush->smooth_deform_type == BRUSH_SMOOTH_DEFORM_LAPLACIAN) {
@@ -6047,80 +3295,80 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
}
break;
case SCULPT_TOOL_CREASE:
- do_crease_brush(sd, ob, nodes, totnode);
+ SCULPT_do_crease_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_BLOB:
- do_crease_brush(sd, ob, nodes, totnode);
+ SCULPT_do_crease_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_PINCH:
- do_pinch_brush(sd, ob, nodes, totnode);
+ SCULPT_do_pinch_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_INFLATE:
- do_inflate_brush(sd, ob, nodes, totnode);
+ SCULPT_do_inflate_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_GRAB:
- do_grab_brush(sd, ob, nodes, totnode);
+ SCULPT_do_grab_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_ROTATE:
- do_rotate_brush(sd, ob, nodes, totnode);
+ SCULPT_do_rotate_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SNAKE_HOOK:
- do_snake_hook_brush(sd, ob, nodes, totnode);
+ SCULPT_do_snake_hook_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_NUDGE:
- do_nudge_brush(sd, ob, nodes, totnode);
+ SCULPT_do_nudge_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_THUMB:
- do_thumb_brush(sd, ob, nodes, totnode);
+ SCULPT_do_thumb_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_LAYER:
- do_layer_brush(sd, ob, nodes, totnode);
+ SCULPT_do_layer_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_FLATTEN:
- do_flatten_brush(sd, ob, nodes, totnode);
+ SCULPT_do_flatten_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_CLAY:
- do_clay_brush(sd, ob, nodes, totnode);
+ SCULPT_do_clay_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_CLAY_STRIPS:
- do_clay_strips_brush(sd, ob, nodes, totnode);
+ SCULPT_do_clay_strips_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_MULTIPLANE_SCRAPE:
SCULPT_do_multiplane_scrape_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_CLAY_THUMB:
- do_clay_thumb_brush(sd, ob, nodes, totnode);
+ SCULPT_do_clay_thumb_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_FILL:
if (invert && brush->flag & BRUSH_INVERT_TO_SCRAPE_FILL) {
- do_scrape_brush(sd, ob, nodes, totnode);
+ SCULPT_do_scrape_brush(sd, ob, nodes, totnode);
}
else {
- do_fill_brush(sd, ob, nodes, totnode);
+ SCULPT_do_fill_brush(sd, ob, nodes, totnode);
}
break;
case SCULPT_TOOL_SCRAPE:
if (invert && brush->flag & BRUSH_INVERT_TO_SCRAPE_FILL) {
- do_fill_brush(sd, ob, nodes, totnode);
+ SCULPT_do_fill_brush(sd, ob, nodes, totnode);
}
else {
- do_scrape_brush(sd, ob, nodes, totnode);
+ SCULPT_do_scrape_brush(sd, ob, nodes, totnode);
}
break;
case SCULPT_TOOL_MASK:
- do_mask_brush(sd, ob, nodes, totnode);
+ SCULPT_do_mask_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_POSE:
SCULPT_do_pose_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_DRAW_SHARP:
- do_draw_sharp_brush(sd, ob, nodes, totnode);
+ SCULPT_do_draw_sharp_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_ELASTIC_DEFORM:
- do_elastic_deform_brush(sd, ob, nodes, totnode);
+ SCULPT_do_elastic_deform_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_SLIDE_RELAX:
- do_slide_relax_brush(sd, ob, nodes, totnode);
+ SCULPT_do_slide_relax_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_BOUNDARY:
SCULPT_do_boundary_brush(sd, ob, nodes, totnode);
@@ -6132,10 +3380,10 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_DISPLACEMENT_ERASER:
- do_displacement_eraser_brush(sd, ob, nodes, totnode);
+ SCULPT_do_displacement_eraser_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_DISPLACEMENT_SMEAR:
- do_displacement_smear_brush(sd, ob, nodes, totnode);
+ SCULPT_do_displacement_smear_brush(sd, ob, nodes, totnode);
break;
case SCULPT_TOOL_PAINT:
SCULPT_do_paint_brush(sd, ob, nodes, totnode);
@@ -6157,7 +3405,7 @@ static void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSe
}
if (sculpt_brush_use_topology_rake(ss, brush)) {
- bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor);
+ SCULPT_bmesh_topology_rake(sd, ob, nodes, totnode, brush->topology_rake_factor);
}
/* The cloth brush adds the gravity as a regular force and it is processed in the solver. */
@@ -6946,7 +4194,7 @@ static float sculpt_brush_dynamic_size_get(Brush *brush, StrokeCache *cache, flo
case SCULPT_TOOL_CLAY_STRIPS:
return max_ff(initial_size * 0.30f, initial_size * powf(cache->pressure, 1.5f));
case SCULPT_TOOL_CLAY_THUMB: {
- float clay_stabilized_pressure = sculpt_clay_thumb_get_stabilized_pressure(cache);
+ float clay_stabilized_pressure = SCULPT_clay_thumb_get_stabilized_pressure(cache);
return initial_size * clay_stabilized_pressure;
}
default:
@@ -8131,7 +5379,7 @@ static void sculpt_brush_stroke_cancel(bContext *C, wmOperator *op)
sculpt_brush_exit_tex(sd);
}
-static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
+void SCULPT_OT_brush_stroke(wmOperatorType *ot)
{
/* Identifiers. */
ot->name = "Sculpt";
@@ -8159,677 +5407,6 @@ static void SCULPT_OT_brush_stroke(wmOperatorType *ot)
"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))
-{
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- Object *ob = CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
-
- if (!ss) {
- return OPERATOR_FINISHED;
- }
- SCULPT_vertex_random_access_ensure(ss);
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
-
- MEM_SAFE_FREE(ss->persistent_base);
-
- const int totvert = SCULPT_vertex_count_get(ss);
- ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert,
- "layer persistent base");
-
- for (int i = 0; i < totvert; i++) {
- copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i));
- SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no);
- ss->persistent_base[i].disp = 0.0f;
- }
-
- 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";
-
- /* API callbacks. */
- ot->exec = sculpt_set_persistent_base_exec;
- ot->poll = SCULPT_mode_poll;
-
- 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);
-
- SCULPT_pbvh_clear(ob);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
-
- return OPERATOR_FINISHED;
-}
-
-/* The BVH gets less optimal more quickly with dynamic topology than
- * regular sculpting. There is no doubt more clever stuff we can do to
- * optimize it on the fly, but for now this gives the user a nicer way
- * to recalculate it than toggling modes. */
-static void SCULPT_OT_optimize(wmOperatorType *ot)
-{
- /* Identifiers. */
- ot->name = "Rebuild BVH";
- ot->idname = "SCULPT_OT_optimize";
- ot->description = "Recalculate the sculpt BVH to improve performance";
-
- /* API callbacks. */
- ot->exec = sculpt_optimize_exec;
- ot->poll = SCULPT_mode_poll;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-/********************* Dynamic topology symmetrize ********************/
-
-static bool sculpt_no_multires_poll(bContext *C)
-{
- Object *ob = CTX_data_active_object(C);
- if (SCULPT_mode_poll(C) && ob->sculpt && ob->sculpt->pbvh) {
- return BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_GRIDS;
- }
- return false;
-}
-
-static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
-{
- Main *bmain = CTX_data_main(C);
- Object *ob = CTX_data_active_object(C);
- const Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- SculptSession *ss = ob->sculpt;
- PBVH *pbvh = ss->pbvh;
- const float dist = RNA_float_get(op->ptr, "merge_tolerance");
-
- if (!pbvh) {
- return OPERATOR_CANCELLED;
- }
-
- switch (BKE_pbvh_type(pbvh)) {
- case PBVH_BMESH:
- /* Dyntopo Symmetrize. */
-
- /* 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(ob, "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);
-
- /* Symmetrize and re-triangulate. */
- BMO_op_callf(ss->bm,
- (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE),
- "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b",
- sd->symmetrize_direction,
- dist,
- true);
- 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);
-
- BM_mesh_toolflags_set(ss->bm, false);
-
- /* Finish undo. */
- BM_log_all_added(ss->bm, ss->bm_log);
- SCULPT_undo_push_end();
-
- break;
- case PBVH_FACES:
- /* Mesh Symmetrize. */
- ED_sculpt_undo_geometry_begin(ob, "mesh symmetrize");
- Mesh *mesh = ob->data;
-
- BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist);
-
- ED_sculpt_undo_geometry_end(ob);
- BKE_mesh_calc_normals(ob->data);
- BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
-
- break;
- case PBVH_GRIDS:
- return OPERATOR_CANCELLED;
- }
-
- /* Redraw. */
- SCULPT_pbvh_clear(ob);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
-
- 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";
-
- /* API callbacks. */
- ot->exec = sculpt_symmetrize_exec;
- ot->poll = sculpt_no_multires_poll;
-
- RNA_def_float(ot->srna,
- "merge_tolerance",
- 0.001f,
- 0.0f,
- FLT_MAX,
- "Merge Distance",
- "Distance within which symmetrical vertices are merged",
- 0.0f,
- 1.0f);
-}
-
-/**** Toggle operator for turning sculpt mode on or off ****/
-
-static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
-{
- /* Create persistent sculpt mode data. */
- BKE_sculpt_toolsettings_data_ensure(scene);
-
- /* Create sculpt mode session data. */
- if (ob->sculpt != NULL) {
- BKE_sculptsession_free(ob);
- }
- ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
- ob->sculpt->mode_type = OB_MODE_SCULPT;
-
- BKE_sculpt_ensure_orig_mesh_data(scene, ob);
-
- BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
-
- /* This function expects a fully evaluated depsgraph. */
- BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
-
- /* Here we can detect geometry that was just added to Sculpt Mode as it has the
- * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
- /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not
- * initialized, which is used is some operators that modify the mesh topology to perform certain
- * actions in the new polys. After these operations are finished, all polys should have a valid
- * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility
- * correctly. */
- /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new
- * objects, like moving the transform pivot position to the new area or masking existing
- * geometry. */
- SculptSession *ss = ob->sculpt;
- const int new_face_set = SCULPT_face_set_next_available_get(ss);
- for (int i = 0; i < ss->totfaces; i++) {
- if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
- ss->face_sets[i] = new_face_set;
- }
- }
-}
-
-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 sculpt mode. */
- ob->mode |= mode_flag;
-
- sculpt_init_session(bmain, depsgraph, scene, ob);
-
- 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(paint, SCULPT_mode_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) {
- MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
-
- 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(ob, "Dynamic topology enable");
- }
- SCULPT_dynamic_topology_enable_ex(bmain, 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, Depsgraph *depsgraph, 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);
- ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports);
-}
-
-void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
-{
- const int mode_flag = OB_MODE_SCULPT;
- Mesh *me = BKE_mesh_from_object(ob);
-
- multires_flush_sculpt_updates(ob);
-
- /* Not needed for now. */
-#if 0
- MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
- 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);
- }
-
- 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(bmain, depsgraph, scene, ob);
-
- /* Store so we know to re-enable when entering sculpt mode. */
- me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
- }
-
- /* Leave sculpt mode. */
- ob->mode &= ~mode_flag;
-
- BKE_sculptsession_free(ob);
-
- paint_cursor_delete_textures();
-
- /* 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);
-}
-
-void ED_object_sculptmode_exit(bContext *C, Depsgraph *depsgraph)
-{
- 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);
- ED_object_sculptmode_exit_ex(bmain, 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;
-
- 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(bmain, depsgraph, scene, ob);
- }
- else {
- if (depsgraph) {
- depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
- }
- ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports);
- BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint);
-
- if (ob->mode & mode_flag) {
- Mesh *me = ob->data;
- /* Dyntopo adds its own undo step. */
- if ((me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) == 0) {
- /* Without this the memfile undo step is used,
- * while it works it causes lag when undoing the first undo step, see T71564. */
- wmWindowManager *wm = CTX_wm_manager(C);
- if (wm->op_undo_depth <= 1) {
- SCULPT_undo_push_begin(ob, op->type->name);
- }
- }
- }
- }
-
- WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene);
-
- WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
-
- WM_toolsystem_update_from_context_view3d(C);
-
- 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";
-
- /* API callbacks. */
- ot->exec = sculpt_mode_toggle_exec;
- ot->poll = ED_operator_object_active_editable_mesh;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius)
-{
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- Object *ob = CTX_data_active_object(C);
-
- ss->preview_vert_index_count = 0;
- int totpoints = 0;
-
- /* This function is called from the cursor drawing code, so the PBVH may not be build yet. */
- if (!ss->pbvh) {
- return;
- }
-
- if (!ss->deform_modifiers_active) {
- return;
- }
-
- if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
- return;
- }
-
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
-
- if (!ss->pmap) {
- return;
- }
-
- float brush_co[3];
- copy_v3_v3(brush_co, SCULPT_active_vertex_co_get(ss));
-
- BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
-
- /* Assuming an average of 6 edges per vertex in a triangulated mesh. */
- const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2;
-
- if (ss->preview_vert_index_list == NULL) {
- ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines");
- }
-
- GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int));
- int active_v = SCULPT_active_vertex_get(ss);
- BLI_gsqueue_push(not_visited_vertices, &active_v);
-
- while (!BLI_gsqueue_is_empty(not_visited_vertices)) {
- int from_v;
- BLI_gsqueue_pop(not_visited_vertices, &from_v);
- SculptVertexNeighborIter ni;
- SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
- if (totpoints + (ni.size * 2) < max_preview_vertices) {
- int to_v = ni.index;
- ss->preview_vert_index_list[totpoints] = from_v;
- totpoints++;
- ss->preview_vert_index_list[totpoints] = to_v;
- totpoints++;
- if (BLI_BITMAP_TEST(visited_vertices, to_v)) {
- continue;
- }
- BLI_BITMAP_ENABLE(visited_vertices, to_v);
- const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v);
- if (len_squared_v3v3(brush_co, co) < radius * radius) {
- BLI_gsqueue_push(not_visited_vertices, &to_v);
- }
- }
- }
- SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
- }
-
- BLI_gsqueue_free(not_visited_vertices);
-
- MEM_freeN(visited_vertices);
-
- ss->preview_vert_index_count = totpoints;
-}
-
-static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_active_object(C);
-
- ID *data;
- data = ob->data;
- if (data && ID_IS_LINKED(data)) {
- return OPERATOR_CANCELLED;
- }
-
- if (ob->type != OB_MESH) {
- return OPERATOR_CANCELLED;
- }
-
- Mesh *mesh = ob->data;
-
- const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
- if (mloopcol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
-
- const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
- if (MPropCol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
-
- MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
- MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
-
- for (int i = 0; i < mesh->totpoly; i++) {
- MPoly *c_poly = &polys[i];
- for (int j = 0; j < c_poly->totloop; j++) {
- int loop_index = c_poly->loopstart + j;
- MLoop *c_loop = &loops[c_poly->loopstart + j];
- float srgb_color[4];
- linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color);
- loopcols[loop_index].r = (char)(srgb_color[0] * 255);
- loopcols[loop_index].g = (char)(srgb_color[1] * 255);
- loopcols[loop_index].b = (char)(srgb_color[2] * 255);
- loopcols[loop_index].a = (char)(srgb_color[3] * 255);
- }
- }
-
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Sculpt Vertex Color to Vertex Color";
- ot->description = "Copy the Sculpt Vertex Color to a regular color layer";
- ot->idname = "SCULPT_OT_vertex_to_loop_colors";
-
- /* api callbacks */
- ot->poll = SCULPT_vertex_colors_poll;
- ot->exec = vertex_to_loop_colors_exec;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
-{
- Object *ob = CTX_data_active_object(C);
-
- ID *data;
- data = ob->data;
- if (data && ID_IS_LINKED(data)) {
- return OPERATOR_CANCELLED;
- }
-
- if (ob->type != OB_MESH) {
- return OPERATOR_CANCELLED;
- }
-
- Mesh *mesh = ob->data;
-
- const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
- if (mloopcol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
-
- const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
- if (MPropCol_layer_n == -1) {
- return OPERATOR_CANCELLED;
- }
- MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
-
- MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
- MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
-
- for (int i = 0; i < mesh->totpoly; i++) {
- MPoly *c_poly = &polys[i];
- for (int j = 0; j < c_poly->totloop; j++) {
- int loop_index = c_poly->loopstart + j;
- MLoop *c_loop = &loops[c_poly->loopstart + j];
- vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
- vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
- vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
- vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f);
- srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color);
- }
- }
-
- DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Vertex Color to Sculpt Vertex Color";
- ot->description = "Copy the active loop color layer to the vertex color";
- ot->idname = "SCULPT_OT_loop_to_vertex_colors";
-
- /* api callbacks */
- ot->poll = SCULPT_vertex_colors_poll;
- ot->exec = loop_to_vertex_colors_exec;
-
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-}
-
-static int sculpt_sample_color_invoke(bContext *C,
- wmOperator *UNUSED(op),
- const wmEvent *UNUSED(e))
-{
- Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
- Scene *scene = CTX_data_scene(C);
- Object *ob = CTX_data_active_object(C);
- Brush *brush = BKE_paint_brush(&sd->paint);
- SculptSession *ss = ob->sculpt;
- int active_vertex = SCULPT_active_vertex_get(ss);
- const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex);
- if (!active_vertex_color) {
- return OPERATOR_CANCELLED;
- }
-
- float color_srgb[3];
- copy_v3_v3(color_srgb, active_vertex_color);
- IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb);
- BKE_brush_color_set(scene, brush, color_srgb);
-
- WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush);
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_sample_color(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Sample Color";
- ot->idname = "SCULPT_OT_sample_color";
- ot->description = "Sample the vertex color of the active vertex";
-
- /* api callbacks */
- ot->invoke = sculpt_sample_color_invoke;
- ot->poll = SCULPT_vertex_colors_poll;
-
- ot->flag = OPTYPE_REGISTER;
-}
-
/* Fake Neighbors. */
/* This allows the sculpt tools to work on meshes with multiple connected components as they had
* only one connected component. When initialized and enabled, the sculpt API will return extra
@@ -9105,356 +5682,10 @@ void SCULPT_fake_neighbors_free(Object *ob)
sculpt_pose_fake_neighbors_free(ss);
}
-/**
- * #sculpt_mask_by_color_delta_get returns values in the (0,1) range that are used to generate the
- * mask based on the difference between two colors (the active color and the color of any other
- * vertex). Ideally, a threshold of 0 should mask only the colors that are equal to the active
- * color and threshold of 1 should mask all colors. In order to avoid artifacts and produce softer
- * falloffs in the mask, the MASK_BY_COLOR_SLOPE defines the size of the transition values between
- * masked and unmasked vertices. The smaller this value is, the sharper the generated mask is going
- * to be.
- */
-#define MASK_BY_COLOR_SLOPE 0.25f
-
-static float sculpt_mask_by_color_delta_get(const float *color_a,
- const float *color_b,
- const float threshold,
- const bool invert)
-{
- float len = len_v3v3(color_a, color_b);
- /* Normalize len to the (0, 1) range. */
- len = len / M_SQRT3;
-
- if (len < threshold - MASK_BY_COLOR_SLOPE) {
- len = 1.0f;
- }
- else if (len >= threshold) {
- len = 0.0f;
- }
- else {
- len = (-len + threshold) / MASK_BY_COLOR_SLOPE;
- }
-
- if (invert) {
- return 1.0f - len;
- }
- return len;
-}
-
-static float sculpt_mask_by_color_final_mask_get(const float current_mask,
- const float new_mask,
- const bool invert,
- const bool preserve_mask)
-{
- if (preserve_mask) {
- if (invert) {
- return min_ff(current_mask, new_mask);
- }
- return max_ff(current_mask, new_mask);
- }
- return new_mask;
-}
-
-typedef struct MaskByColorContiguousFloodFillData {
- float threshold;
- bool invert;
- float *new_mask;
- float initial_color[3];
-} MaskByColorContiguousFloodFillData;
-
-static void do_mask_by_color_contiguous_update_nodes_cb(
- void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
-
- SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
- bool update_node = false;
-
- const bool invert = data->mask_by_color_invert;
- const bool preserve_mask = data->mask_by_color_preserve_mask;
-
- PBVHVertexIter vd;
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- const float current_mask = *vd.mask;
- const float new_mask = data->mask_by_color_floodfill[vd.index];
- *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
- if (current_mask == *vd.mask) {
- continue;
- }
- update_node = true;
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- BKE_pbvh_vertex_iter_end;
- if (update_node) {
- BKE_pbvh_node_mark_redraw(data->nodes[n]);
- }
-}
-
-static bool sculpt_mask_by_color_contiguous_floodfill_cb(
- SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
-{
- MaskByColorContiguousFloodFillData *data = userdata;
- const float *current_color = SCULPT_vertex_color_get(ss, to_v);
- float new_vertex_mask = sculpt_mask_by_color_delta_get(
- current_color, data->initial_color, data->threshold, data->invert);
- data->new_mask[to_v] = new_vertex_mask;
-
- if (is_duplicate) {
- data->new_mask[to_v] = data->new_mask[from_v];
- }
-
- float len = len_v3v3(current_color, data->initial_color);
- len = len / M_SQRT3;
- return len <= data->threshold;
-}
-
-static void sculpt_mask_by_color_contiguous(Object *object,
- const int vertex,
- const float threshold,
- const bool invert,
- const bool preserve_mask)
-{
- SculptSession *ss = object->sculpt;
- const int totvert = SCULPT_vertex_count_get(ss);
-
- float *new_mask = MEM_calloc_arrayN(totvert, sizeof(float), "new mask");
-
- if (invert) {
- for (int i = 0; i < totvert; i++) {
- new_mask[i] = 1.0f;
- }
- }
-
- SculptFloodFill flood;
- SCULPT_floodfill_init(ss, &flood);
- SCULPT_floodfill_add_initial(&flood, vertex);
-
- MaskByColorContiguousFloodFillData ffd;
- ffd.threshold = threshold;
- ffd.invert = invert;
- ffd.new_mask = new_mask;
- copy_v3_v3(ffd.initial_color, SCULPT_vertex_color_get(ss, vertex));
-
- SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd);
- SCULPT_floodfill_free(&flood);
-
- int totnode;
- PBVHNode **nodes;
- BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
-
- SculptThreadedTaskData data = {
- .ob = object,
- .nodes = nodes,
- .mask_by_color_floodfill = new_mask,
- .mask_by_color_vertex = vertex,
- .mask_by_color_threshold = threshold,
- .mask_by_color_invert = invert,
- .mask_by_color_preserve_mask = preserve_mask,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(
- 0, totnode, &data, do_mask_by_color_contiguous_update_nodes_cb, &settings);
-
- MEM_SAFE_FREE(nodes);
-
- MEM_freeN(new_mask);
-}
-
-static void do_mask_by_color_task_cb(void *__restrict userdata,
- const int n,
- const TaskParallelTLS *__restrict UNUSED(tls))
-{
- SculptThreadedTaskData *data = userdata;
- SculptSession *ss = data->ob->sculpt;
-
- SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
- bool update_node = false;
-
- const float threshold = data->mask_by_color_threshold;
- const bool invert = data->mask_by_color_invert;
- const bool preserve_mask = data->mask_by_color_preserve_mask;
- const float *active_color = SCULPT_vertex_color_get(ss, data->mask_by_color_vertex);
-
- PBVHVertexIter vd;
- BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
- const float current_mask = *vd.mask;
- const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert);
- *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
-
- if (current_mask == *vd.mask) {
- continue;
- }
- update_node = true;
- if (vd.mvert) {
- vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
- }
- }
- BKE_pbvh_vertex_iter_end;
- if (update_node) {
- BKE_pbvh_node_mark_redraw(data->nodes[n]);
- }
-}
-
-static void sculpt_mask_by_color_full_mesh(Object *object,
- const int vertex,
- const float threshold,
- const bool invert,
- const bool preserve_mask)
-{
- SculptSession *ss = object->sculpt;
-
- int totnode;
- PBVHNode **nodes;
- BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
-
- SculptThreadedTaskData data = {
- .ob = object,
- .nodes = nodes,
- .mask_by_color_vertex = vertex,
- .mask_by_color_threshold = threshold,
- .mask_by_color_invert = invert,
- .mask_by_color_preserve_mask = preserve_mask,
- };
-
- TaskParallelSettings settings;
- BKE_pbvh_parallel_range_settings(&settings, true, totnode);
- BLI_task_parallel_range(0, totnode, &data, do_mask_by_color_task_cb, &settings);
-
- MEM_SAFE_FREE(nodes);
-}
-
-static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
-{
- Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
- Object *ob = CTX_data_active_object(C);
- SculptSession *ss = ob->sculpt;
-
- BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
-
- /* Color data is not available in Multires. */
- if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
- return OPERATOR_CANCELLED;
- }
-
- if (!ss->vcol) {
- return OPERATOR_CANCELLED;
- }
-
- SCULPT_vertex_random_access_ensure(ss);
-
- /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
- * so it needs to be updated here. */
- SculptCursorGeometryInfo sgi;
- float mouse[2];
- mouse[0] = event->mval[0];
- mouse[1] = event->mval[1];
- SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
-
- SCULPT_undo_push_begin(ob, "Mask by color");
-
- const int active_vertex = SCULPT_active_vertex_get(ss);
- const float threshold = RNA_float_get(op->ptr, "threshold");
- const bool invert = RNA_boolean_get(op->ptr, "invert");
- const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
-
- if (RNA_boolean_get(op->ptr, "contiguous")) {
- sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask);
- }
- else {
- sculpt_mask_by_color_full_mesh(ob, active_vertex, threshold, invert, preserve_mask);
- }
-
- BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
- SCULPT_undo_push_end();
-
- SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
-
- return OPERATOR_FINISHED;
-}
-
-static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
-{
- /* identifiers */
- ot->name = "Mask by Color";
- ot->idname = "SCULPT_OT_mask_by_color";
- ot->description = "Creates a mask based on the sculpt vertex colors";
-
- /* api callbacks */
- ot->invoke = sculpt_mask_by_color_invoke;
- ot->poll = SCULPT_vertex_colors_poll;
-
- ot->flag = OPTYPE_REGISTER;
-
- ot->prop = RNA_def_boolean(
- ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas");
-
- ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask");
- ot->prop = RNA_def_boolean(
- ot->srna,
- "preserve_previous_mask",
- false,
- "Preserve Previous Mask",
- "Preserve the previous mask and add or subtract the new one generated by the colors");
-
- RNA_def_float(ot->srna,
- "threshold",
- 0.35f,
- 0.0f,
- 1.0f,
- "Threshold",
- "How much changes in color affect the mask generation",
- 0.0f,
- 1.0f);
-}
-
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operator Registration
* \{ */
-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_mesh_filter);
- WM_operatortype_append(SCULPT_OT_mask_filter);
- WM_operatortype_append(SCULPT_OT_dirty_mask);
- WM_operatortype_append(SCULPT_OT_mask_expand);
- WM_operatortype_append(SCULPT_OT_set_pivot_position);
- WM_operatortype_append(SCULPT_OT_face_sets_create);
- WM_operatortype_append(SCULPT_OT_face_sets_change_visibility);
- WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors);
- WM_operatortype_append(SCULPT_OT_face_sets_init);
- WM_operatortype_append(SCULPT_OT_cloth_filter);
- WM_operatortype_append(SCULPT_OT_face_sets_edit);
- WM_operatortype_append(SCULPT_OT_face_set_lasso_gesture);
- WM_operatortype_append(SCULPT_OT_face_set_box_gesture);
- WM_operatortype_append(SCULPT_OT_trim_box_gesture);
- WM_operatortype_append(SCULPT_OT_trim_lasso_gesture);
- WM_operatortype_append(SCULPT_OT_project_line_gesture);
-
- WM_operatortype_append(SCULPT_OT_sample_color);
- WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
- WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
- WM_operatortype_append(SCULPT_OT_color_filter);
- WM_operatortype_append(SCULPT_OT_mask_by_color);
- WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit);
- WM_operatortype_append(SCULPT_OT_mask_init);
-
- WM_operatortype_append(SCULPT_OT_expand);
-}
-
/** \} */
diff --git a/source/blender/editors/sculpt_paint/sculpt_brushes.c b/source/blender/editors/sculpt_paint/sculpt_brushes.c
new file mode 100644
index 00000000000..8842d93410c
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_brushes.c
@@ -0,0 +1,2847 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2006 by Nicholas Bishop
+ * All rights reserved.
+ * Implements the Sculpt Mode tools
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_blenlib.h"
+#include "BLI_dial_2d.h"
+#include "BLI_ghash.h"
+#include "BLI_gsqueue.h"
+#include "BLI_hash.h"
+#include "BLI_math.h"
+#include "BLI_math_color.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "PIL_time.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_customdata_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_brush.h"
+#include "BKE_ccg.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_image.h"
+#include "BKE_kelvinlet.h"
+#include "BKE_key.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_mesh_mirror.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_node.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_particle.h"
+#include "BKE_pbvh.h"
+#include "BKE_pointcache.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_subdiv_ccg.h"
+#include "BKE_subsurf.h"
+
+#include "DEG_depsgraph.h"
+
+#include "IMB_colormanagement.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "ED_view3d.h"
+
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* -------------------------------------------------------------------- */
+/** \name SculptProjectVector
+ *
+ * Fast-path for #project_plane_v3_v3v3
+ * \{ */
+
+typedef struct SculptProjectVector {
+ float plane[3];
+ float len_sq;
+ float len_sq_inv_neg;
+ bool is_valid;
+
+} SculptProjectVector;
+
+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;
+}
+
+/**
+ * \param plane: Direction, can be any length.
+ */
+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;
+}
+
+/**
+ * Calculate the projection.
+ */
+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);
+#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);
+#endif
+}
+
+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 (SCULPT_stroke_is_main_symmetry_pass(ss->cache) &&
+ (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache) ||
+ !(brush->flag & BRUSH_ORIGINAL_PLANE) || !(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.0f, 0.0f, 0.0f);
+ break;
+
+ case SCULPT_DISP_DIR_Y:
+ ARRAY_SET_ITEMS(r_area_no, 0.0f, 1.0f, 0.0f);
+ break;
+
+ case SCULPT_DISP_DIR_Z:
+ ARRAY_SET_ITEMS(r_area_no, 0.0f, 0.0f, 1.0f);
+ break;
+
+ case SCULPT_DISP_DIR_AREA:
+ SCULPT_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) {
+ SCULPT_calc_area_center(sd, ob, nodes, totnode, r_area_co);
+ }
+
+ /* For area normal. */
+ if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) &&
+ (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
+ copy_v3_v3(r_area_no, ss->cache->sculpt_normal);
+ }
+ else {
+ copy_v3_v3(ss->cache->sculpt_normal, r_area_no);
+ }
+
+ /* For flatten center. */
+ if ((!SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) &&
+ (brush->flag & BRUSH_ORIGINAL_PLANE)) {
+ copy_v3_v3(r_area_co, ss->cache->last_center);
+ }
+ else {
+ 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);
+ }
+}
+
+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];
+
+#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);
+#else
+ /* 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);
+
+ add_v3_v3(vec_rot, sculpt_co);
+ sub_v3_v3v3(r_delta, vec_rot, v_co);
+#endif
+}
+
+/**
+ * Align the grab delta to the brush normal.
+ *
+ * \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])
+{
+ /* 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;
+ }
+
+ 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 Sculpt Draw Brush
+ * \{ */
+
+static void do_draw_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ /* Offset vertex. */
+ const float fade = SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ mul_v3_v3fl(proxy[vd.i], offset, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_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;
+
+ /* Offset with as much as possible factored in already. */
+ float effective_normal[3];
+ SCULPT_tilt_effective_normal_get(ss, brush, effective_normal);
+ mul_v3_v3fl(offset, effective_normal, 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. */
+ BKE_curvemapping_init(brush->curve);
+
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .offset = offset,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_draw_brush_task_cb_ex, &settings);
+}
+
+/** \} */
+
+static void do_fill_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+
+ if (!SCULPT_plane_point_side(vd.co, test.plane_tool)) {
+ continue;
+ }
+
+ 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 (!SCULPT_plane_trim(ss->cache, brush, val)) {
+ continue;
+ }
+
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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;
+}
+
+void SCULPT_do_fill_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ const float radius = ss->cache->radius;
+
+ float area_no[3];
+ float area_co[3];
+ float offset = SCULPT_brush_plane_offset_get(sd, ss);
+
+ float displace;
+
+ float temp[3];
+
+ SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
+
+ SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor);
+
+ displace = radius * offset;
+
+ 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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ 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 TaskParallelTLS *__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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+ 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)) {
+ continue;
+ }
+
+ if (SCULPT_plane_point_side(vd.co, test.plane_tool)) {
+ continue;
+ }
+
+ 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 (!SCULPT_plane_trim(ss->cache, brush, val)) {
+ continue;
+ }
+
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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;
+}
+
+void SCULPT_do_scrape_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ const float radius = ss->cache->radius;
+
+ float area_no[3];
+ float area_co[3];
+ float offset = SCULPT_brush_plane_offset_get(sd, ss);
+
+ float displace;
+
+ float temp[3];
+
+ SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
+
+ SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor);
+
+ displace = -radius * offset;
+
+ 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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_scrape_brush_task_cb_ex, &settings);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sculpt Clay Thumb Brush
+ * \{ */
+
+static void do_clay_thumb_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__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;
+
+ PBVHVertexIter vd;
+ float(*proxy)[3];
+ const float bstrength = data->clay_strength;
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ float plane_tilt[4];
+ float normal_tilt[3];
+ float imat[4][4];
+
+ invert_m4_m4(imat, mat);
+ rotate_v3_v3v3fl(normal_tilt, area_no_sp, imat[0], DEG2RADF(-ss->cache->clay_thumb_front_angle));
+
+ /* Plane aligned to the geometry normal (back part of the brush). */
+ plane_from_point_normal_v3(test.plane_tool, area_co, area_no_sp);
+ /* Tilted plane (front part of the brush). */
+ plane_from_point_normal_v3(plane_tilt, area_co, normal_tilt);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ float local_co[3];
+ mul_v3_m4v3(local_co, mat, vd.co);
+ float intr[3], intr_tilt[3];
+ float val[3];
+
+ closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
+ closest_to_plane_normalized_v3(intr_tilt, plane_tilt, vd.co);
+
+ /* Mix the deformation of the aligned and the tilted plane based on the brush space vertex
+ * coordinates. */
+ /* We can also control the mix with a curve if it produces noticeable artifacts in the center
+ * of the brush. */
+ const float tilt_mix = local_co[1] > 0.0f ? 0.0f : 1.0f;
+ interp_v3_v3v3(intr, intr, intr_tilt, tilt_mix);
+ sub_v3_v3v3(val, intr_tilt, vd.co);
+
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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;
+}
+
+float SCULPT_clay_thumb_get_stabilized_pressure(StrokeCache *cache)
+{
+ float final_pressure = 0.0f;
+ for (int i = 0; i < SCULPT_CLAY_STABILIZER_LEN; i++) {
+ final_pressure += cache->clay_pressure_stabilizer[i];
+ }
+ return final_pressure / SCULPT_CLAY_STABILIZER_LEN;
+}
+
+void SCULPT_do_clay_thumb_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ const float radius = ss->cache->radius;
+ const float offset = SCULPT_brush_plane_offset_get(sd, ss);
+ const float displace = radius * (0.25f + offset);
+
+ /* Sampled geometry normal and area center. */
+ float area_no_sp[3];
+ float area_no[3];
+ float area_co[3];
+
+ float temp[3];
+ float mat[4][4];
+ float scale[4][4];
+ float tmat[4][4];
+
+ SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co);
+
+ if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
+ SCULPT_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 (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
+ ss->cache->clay_thumb_front_angle = 0.0f;
+ return;
+ }
+
+ /* Simulate the clay accumulation by increasing the plane angle as more samples are added to the
+ * stroke. */
+ if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) {
+ ss->cache->clay_thumb_front_angle += 0.8f;
+ ss->cache->clay_thumb_front_angle = clamp_f(ss->cache->clay_thumb_front_angle, 0.0f, 60.0f);
+ }
+
+ if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
+ return;
+ }
+
+ /* Displace the brush planes. */
+ copy_v3_v3(area_co, ss->cache->location);
+ mul_v3_v3v3(temp, area_no_sp, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ add_v3_v3(area_co, temp);
+
+ /* Initialize brush local-space matrix. */
+ cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
+ mat[0][3] = 0.0f;
+ cross_v3_v3v3(mat[1], area_no, mat[0]);
+ mat[1][3] = 0.0f;
+ copy_v3_v3(mat[2], area_no);
+ mat[2][3] = 0.0f;
+ copy_v3_v3(mat[3], ss->cache->location);
+ mat[3][3] = 1.0f;
+ normalize_m4(mat);
+
+ /* Scale brush local space matrix. */
+ scale_m4_fl(scale, ss->cache->radius);
+ mul_m4_m4m4(tmat, mat, scale);
+ invert_m4_m4(mat, tmat);
+
+ float clay_strength = ss->cache->bstrength *
+ SCULPT_clay_thumb_get_stabilized_pressure(ss->cache);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .area_no_sp = area_no_sp,
+ .area_co = ss->cache->location,
+ .mat = mat,
+ .clay_strength = clay_strength,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_clay_thumb_brush_task_cb_ex, &settings);
+}
+
+/** \} */
+
+static void do_flatten_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ 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 (SCULPT_plane_trim(ss->cache, brush, val)) {
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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;
+}
+
+void SCULPT_do_flatten_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ const float radius = ss->cache->radius;
+
+ float area_no[3];
+ float area_co[3];
+
+ float offset = SCULPT_brush_plane_offset_get(sd, ss);
+ float displace;
+ float temp[3];
+
+ SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
+
+ SCULPT_tilt_apply_to_normal(area_no, ss->cache, brush->tilt_strength_factor);
+
+ displace = radius * offset;
+
+ 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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_flatten_brush_task_cb_ex, &settings);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sculpt Clay Brush
+ * \{ */
+
+typedef struct ClaySampleData {
+ float plane_dist[2];
+} ClaySampleData;
+
+static void calc_clay_surface_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ ClaySampleData *csd = tls->userdata_chunk;
+ const float *area_no = data->area_no;
+ const float *area_co = data->area_co;
+ float plane[4];
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, brush->falloff_shape);
+
+ /* Apply the brush normal radius to the test before sampling. */
+ float test_radius = sqrtf(test.radius_squared);
+ test_radius *= brush->normal_radius_factor;
+ test.radius_squared = test_radius * test_radius;
+ plane_from_point_normal_v3(plane, area_co, area_no);
+
+ if (is_zero_v4(plane)) {
+ return;
+ }
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+
+ float plane_dist = dist_signed_to_plane_v3(vd.co, plane);
+ float plane_dist_abs = fabsf(plane_dist);
+ if (plane_dist > 0.0f) {
+ csd->plane_dist[0] = MIN2(csd->plane_dist[0], plane_dist_abs);
+ }
+ else {
+ csd->plane_dist[1] = MIN2(csd->plane_dist[1], plane_dist_abs);
+ }
+ BKE_pbvh_vertex_iter_end;
+ }
+}
+
+static void calc_clay_surface_reduce(const void *__restrict UNUSED(userdata),
+ void *__restrict chunk_join,
+ void *__restrict chunk)
+{
+ ClaySampleData *join = chunk_join;
+ ClaySampleData *csd = chunk;
+ join->plane_dist[0] = MIN2(csd->plane_dist[0], join->plane_dist[0]);
+ join->plane_dist[1] = MIN2(csd->plane_dist[1], join->plane_dist[1]);
+}
+
+static void do_clay_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__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 = fabsf(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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+
+ float intr[3];
+ float val[3];
+ closest_to_plane_normalized_v3(intr, test.plane_tool, vd.co);
+
+ sub_v3_v3v3(val, intr, vd.co);
+
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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;
+}
+
+void SCULPT_do_clay_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ const float radius = fabsf(ss->cache->radius);
+ const float initial_radius = fabsf(ss->cache->initial_radius);
+ bool flip = ss->cache->bstrength < 0.0f;
+
+ float offset = SCULPT_brush_plane_offset_get(sd, ss);
+ float displace;
+
+ float area_no[3];
+ float area_co[3];
+ float temp[3];
+
+ SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no, area_co);
+
+ SculptThreadedTaskData sample_data = {
+ .sd = NULL,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .totnode = totnode,
+ .area_no = area_no,
+ .area_co = ss->cache->location,
+ };
+
+ ClaySampleData csd = {{0}};
+
+ TaskParallelSettings sample_settings;
+ BKE_pbvh_parallel_range_settings(&sample_settings, true, totnode);
+ sample_settings.func_reduce = calc_clay_surface_reduce;
+ sample_settings.userdata_chunk = &csd;
+ sample_settings.userdata_chunk_size = sizeof(ClaySampleData);
+
+ BLI_task_parallel_range(0, totnode, &sample_data, calc_clay_surface_task_cb, &sample_settings);
+
+ float d_offset = (csd.plane_dist[0] + csd.plane_dist[1]);
+ d_offset = min_ff(radius, d_offset);
+ d_offset = d_offset / radius;
+ d_offset = 1.0f - d_offset;
+ displace = fabsf(initial_radius * (0.25f + offset + (d_offset * 0.15f)));
+ if (flip) {
+ displace = -displace;
+ }
+
+ mul_v3_v3v3(temp, area_no, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ copy_v3_v3(area_co, ss->cache->location);
+ add_v3_v3(area_co, temp);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .area_no = area_no,
+ .area_co = area_co,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ 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 TaskParallelTLS *__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;
+
+ PBVHVertexIter vd;
+ SculptBrushTest test;
+ float(*proxy)[3];
+ const bool flip = (ss->cache->bstrength < 0.0f);
+ const float bstrength = flip ? -ss->cache->bstrength : ss->cache->bstrength;
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!SCULPT_brush_test_cube(&test, vd.co, mat, brush->tip_roundness)) {
+ continue;
+ }
+
+ if (!plane_point_side_flip(vd.co, test.plane_tool, flip)) {
+ continue;
+ }
+
+ 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 (!SCULPT_plane_trim(ss->cache, brush, val)) {
+ continue;
+ }
+ /* The normal from the vertices is ignored, it causes glitch with planes, see: T44390. */
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ ss->cache->radius * test.dist,
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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;
+}
+
+void SCULPT_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.0f);
+ const float radius = flip ? -ss->cache->radius : ss->cache->radius;
+ const float offset = SCULPT_brush_plane_offset_get(sd, ss);
+ const float displace = radius * (0.18f + offset);
+
+ /* The sculpt-plane normal (whatever its set to). */
+ float area_no_sp[3];
+
+ /* Geometry normal */
+ float area_no[3];
+ float area_co[3];
+
+ float temp[3];
+ float mat[4][4];
+ float scale[4][4];
+ float tmat[4][4];
+
+ SCULPT_calc_brush_plane(sd, ob, nodes, totnode, area_no_sp, area_co);
+ SCULPT_tilt_apply_to_normal(area_no_sp, ss->cache, brush->tilt_strength_factor);
+
+ if (brush->sculpt_plane != SCULPT_DISP_DIR_AREA || (brush->flag & BRUSH_ORIGINAL_NORMAL)) {
+ SCULPT_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 (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
+ return;
+ }
+
+ if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
+ return;
+ }
+
+ mul_v3_v3v3(temp, area_no_sp, ss->cache->scale);
+ mul_v3_fl(temp, displace);
+ add_v3_v3(area_co, temp);
+
+ /* Clay Strips uses a cube test with falloff in the XY axis (not in Z) and a plane to deform the
+ * vertices. When in Add mode, vertices that are below the plane and inside the cube are move
+ * towards the plane. In this situation, there may be cases where a vertex is outside the cube
+ * but below the plane, so won't be deformed, causing artifacts. In order to prevent these
+ * artifacts, this displaces the test cube space in relation to the plane in order to
+ * deform more vertices that may be below it. */
+ /* The 0.7 and 1.25 factors are arbitrary and don't have any relation between them, they were set
+ * by doing multiple tests using the default "Clay Strips" brush preset. */
+ float area_co_displaced[3];
+ madd_v3_v3v3fl(area_co_displaced, area_co, area_no, -radius * 0.7f);
+
+ /* Initialize brush local-space matrix. */
+ cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
+ mat[0][3] = 0.0f;
+ cross_v3_v3v3(mat[1], area_no, mat[0]);
+ mat[1][3] = 0.0f;
+ copy_v3_v3(mat[2], area_no);
+ mat[2][3] = 0.0f;
+ copy_v3_v3(mat[3], area_co_displaced);
+ mat[3][3] = 1.0f;
+ normalize_m4(mat);
+
+ /* Scale brush local space matrix. */
+ scale_m4_fl(scale, ss->cache->radius);
+ mul_m4_m4m4(tmat, mat, scale);
+
+ /* Deform the local space in Z to scale the test cube. As the test cube does not have falloff in
+ * Z this does not produce artifacts in the falloff cube and allows to deform extra vertices
+ * during big deformation while keeping the surface as uniform as possible. */
+ mul_v3_fl(tmat[2], 1.25f);
+
+ 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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_clay_strips_brush_task_cb_ex, &settings);
+}
+
+static void do_snake_hook_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__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;
+
+ const bool do_elastic = brush->snake_hook_deform_type == BRUSH_SNAKE_HOOK_DEFORM_ELASTIC;
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ KelvinletParams params;
+ BKE_kelvinlet_init_params(&params, ss->cache->radius, bstrength, 1.0f, 0.4f);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!do_elastic && !sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+
+ float fade;
+ if (do_elastic) {
+ fade = 1.0f;
+ }
+ else {
+ fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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 (do_elastic) {
+ float disp[3];
+ BKE_kelvinlet_grab_triscale(disp, &params, vd.co, ss->cache->location, proxy[vd.i]);
+ mul_v3_fl(disp, bstrength * 20.0f);
+ if (vd.mask) {
+ mul_v3_fl(disp, 1.0f - *vd.mask);
+ }
+ mul_v3_fl(disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
+ copy_v3_v3(proxy[vd.i], disp);
+ }
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_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];
+
+ SculptProjectVector spvc;
+
+ copy_v3_v3(grab_delta, ss->cache->grab_delta_symmetry);
+
+ if (bstrength < 0.0f) {
+ negate_v3(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);
+ }
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .spvc = &spvc,
+ .grab_delta = grab_delta,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ 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 TaskParallelTLS *__restrict tls)
+{
+ 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;
+
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ orig_data.co,
+ sqrtf(test.dist),
+ orig_data.no,
+ NULL,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ mul_v3_v3fl(proxy[vd.i], cono, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_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];
+
+ 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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ 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 TaskParallelTLS *__restrict tls)
+{
+ 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;
+
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ float vec[3], rot[3][3];
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ orig_data.co,
+ sqrtf(test.dist),
+ orig_data.no,
+ NULL,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ 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 TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ Sculpt *sd = data->sd;
+ const Brush *brush = data->brush;
+
+ const bool use_persistent_base = ss->persistent_base && brush->flag & BRUSH_PERSISTENT;
+
+ PBVHVertexIter vd;
+ SculptOrigVertData orig_data;
+ const float bstrength = ss->cache->bstrength;
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ const float fade = SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ const int vi = vd.index;
+ float *disp_factor;
+ if (use_persistent_base) {
+ disp_factor = &ss->persistent_base[vi].disp;
+ }
+ else {
+ disp_factor = &ss->cache->layer_displacement_factor[vi];
+ }
+
+ /* When using persistent base, the layer brush (holding Control) invert mode resets the
+ * height of the layer to 0. This makes possible to clean edges of previously added layers
+ * on top of the base. */
+ /* The main direction of the layers is inverted using the regular brush strength with the
+ * brush direction property. */
+ if (use_persistent_base && ss->cache->invert) {
+ (*disp_factor) += fabsf(fade * bstrength * (*disp_factor)) *
+ ((*disp_factor) > 0.0f ? -1.0f : 1.0f);
+ }
+ else {
+ (*disp_factor) += fade * bstrength * (1.05f - fabsf(*disp_factor));
+ }
+ if (vd.mask) {
+ const float clamp_mask = 1.0f - *vd.mask;
+ *disp_factor = clamp_f(*disp_factor, -clamp_mask, clamp_mask);
+ }
+ else {
+ *disp_factor = clamp_f(*disp_factor, -1.0f, 1.0f);
+ }
+
+ float final_co[3];
+ float normal[3];
+
+ if (use_persistent_base) {
+ SCULPT_vertex_persistent_normal_get(ss, vi, normal);
+ mul_v3_fl(normal, brush->height);
+ madd_v3_v3v3fl(final_co, SCULPT_vertex_persistent_co_get(ss, vi), normal, *disp_factor);
+ }
+ else {
+ normal_short_to_float_v3(normal, orig_data.no);
+ mul_v3_fl(normal, brush->height);
+ madd_v3_v3v3fl(final_co, orig_data.co, normal, *disp_factor);
+ }
+
+ float vdisp[3];
+ sub_v3_v3v3(vdisp, final_co, vd.co);
+ mul_v3_fl(vdisp, fabsf(fade));
+ add_v3_v3v3(final_co, vd.co, vdisp);
+
+ SCULPT_clip(sd, ss, vd.co, final_co);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_layer_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ if (ss->cache->layer_displacement_factor == NULL) {
+ ss->cache->layer_displacement_factor = MEM_callocN(sizeof(float) * SCULPT_vertex_count_get(ss),
+ "layer displacement factor");
+ }
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_layer_brush_task_cb_ex, &settings);
+}
+
+static void do_inflate_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+ float val[3];
+
+ if (vd.fno) {
+ copy_v3_v3(val, vd.fno);
+ }
+ else {
+ normal_short_to_float_v3(val, vd.no);
+ }
+
+ 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;
+}
+
+void SCULPT_do_inflate_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_inflate_brush_task_cb_ex, &settings);
+}
+
+static void do_nudge_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ 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;
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ mul_v3_v3fl(proxy[vd.i], cono, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_nudge_brush_task_cb_ex, &settings);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Sculpt Crease & Blob Brush
+ * \{ */
+
+/**
+ * Used for 'SCULPT_TOOL_CREASE' and 'SCULPT_TOOL_BLOB'
+ */
+static void do_crease_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ /* Offset vertex. */
+ const float fade = SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ 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;
+}
+
+void SCULPT_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.0f) ? -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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ 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 TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ float(*stroke_xz)[3] = data->stroke_xz;
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ float x_object_space[3];
+ float z_object_space[3];
+ copy_v3_v3(x_object_space, stroke_xz[0]);
+ copy_v3_v3(z_object_space, stroke_xz[1]);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+ float disp_center[3];
+ float x_disp[3];
+ float z_disp[3];
+ /* Calculate displacement from the vertex to the brush center. */
+ sub_v3_v3v3(disp_center, test.location, vd.co);
+
+ /* Project the displacement into the X vector (aligned to the stroke). */
+ mul_v3_v3fl(x_disp, x_object_space, dot_v3v3(disp_center, x_object_space));
+
+ /* Project the displacement into the Z vector (aligned to the surface normal). */
+ mul_v3_v3fl(z_disp, z_object_space, dot_v3v3(disp_center, z_object_space));
+
+ /* Add the two projected vectors to calculate the final displacement.
+ * The Y component is removed. */
+ add_v3_v3v3(disp_center, x_disp, z_disp);
+
+ if (brush->falloff_shape == PAINT_FALLOFF_SHAPE_TUBE) {
+ project_plane_v3_v3v3(disp_center, disp_center, ss->cache->view_normal);
+ }
+ mul_v3_v3fl(proxy[vd.i], disp_center, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_pinch_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ float area_no[3];
+ float area_co[3];
+
+ float mat[4][4];
+ calc_sculpt_plane(sd, ob, nodes, totnode, area_no, area_co);
+
+ /* delay the first daub because grab delta is not setup */
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
+ return;
+ }
+
+ if (is_zero_v3(ss->cache->grab_delta_symmetry)) {
+ return;
+ }
+
+ /* Initialize `mat`. */
+ cross_v3_v3v3(mat[0], area_no, ss->cache->grab_delta_symmetry);
+ mat[0][3] = 0.0f;
+ cross_v3_v3v3(mat[1], area_no, mat[0]);
+ mat[1][3] = 0.0f;
+ copy_v3_v3(mat[2], area_no);
+ mat[2][3] = 0.0f;
+ copy_v3_v3(mat[3], ss->cache->location);
+ mat[3][3] = 1.0f;
+ normalize_m4(mat);
+
+ float stroke_xz[2][3];
+ normalize_v3_v3(stroke_xz[0], mat[0]);
+ normalize_v3_v3(stroke_xz[1], mat[2]);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .stroke_xz = stroke_xz,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ 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 TaskParallelTLS *__restrict tls)
+{
+ 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;
+
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ const bool grab_silhouette = brush->flag2 & BRUSH_GRAB_SILHOUETTE;
+
+ 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)) {
+ continue;
+ }
+ float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ orig_data.co,
+ sqrtf(test.dist),
+ orig_data.no,
+ NULL,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ if (grab_silhouette) {
+ float silhouette_test_dir[3];
+ normalize_v3_v3(silhouette_test_dir, grab_delta);
+ if (dot_v3v3(ss->cache->initial_normal, ss->cache->grab_delta_symmetry) < 0.0f) {
+ mul_v3_fl(silhouette_test_dir, -1.0f);
+ }
+ float vno[3];
+ normal_short_to_float_v3(vno, orig_data.no);
+ fade *= max_ff(dot_v3v3(vno, silhouette_test_dir), 0.0f);
+ }
+
+ mul_v3_v3fl(proxy[vd.i], grab_delta, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_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];
+
+ 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);
+ }
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .grab_delta = grab_delta,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_grab_brush_task_cb_ex, &settings);
+}
+
+static void do_elastic_deform_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float *grab_delta = data->grab_delta;
+ const float *location = ss->cache->location;
+
+ 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]);
+
+ proxy = BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n])->co;
+
+ float dir;
+ if (ss->cache->mouse[0] > ss->cache->initial_mouse[0]) {
+ dir = 1.0f;
+ }
+ else {
+ dir = -1.0f;
+ }
+
+ if (brush->elastic_deform_type == BRUSH_ELASTIC_DEFORM_TWIST) {
+ int symm = ss->cache->mirror_symmetry_pass;
+ if (ELEM(symm, 1, 2, 4, 7)) {
+ dir = -dir;
+ }
+ }
+
+ KelvinletParams params;
+ float force = len_v3(grab_delta) * dir * bstrength;
+ BKE_kelvinlet_init_params(
+ &params, ss->cache->radius, force, 1.0f, brush->elastic_deform_volume_preservation);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ SCULPT_orig_vert_data_update(&orig_data, &vd);
+ float final_disp[3];
+ switch (brush->elastic_deform_type) {
+ case BRUSH_ELASTIC_DEFORM_GRAB:
+ BKE_kelvinlet_grab(final_disp, &params, orig_data.co, location, grab_delta);
+ mul_v3_fl(final_disp, bstrength * 20.0f);
+ break;
+ case BRUSH_ELASTIC_DEFORM_GRAB_BISCALE: {
+ BKE_kelvinlet_grab_biscale(final_disp, &params, orig_data.co, location, grab_delta);
+ mul_v3_fl(final_disp, bstrength * 20.0f);
+ break;
+ }
+ case BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE: {
+ BKE_kelvinlet_grab_triscale(final_disp, &params, orig_data.co, location, grab_delta);
+ mul_v3_fl(final_disp, bstrength * 20.0f);
+ break;
+ }
+ case BRUSH_ELASTIC_DEFORM_SCALE:
+ BKE_kelvinlet_scale(
+ final_disp, &params, orig_data.co, location, ss->cache->sculpt_normal_symm);
+ break;
+ case BRUSH_ELASTIC_DEFORM_TWIST:
+ BKE_kelvinlet_twist(
+ final_disp, &params, orig_data.co, location, ss->cache->sculpt_normal_symm);
+ break;
+ }
+
+ if (vd.mask) {
+ mul_v3_fl(final_disp, 1.0f - *vd.mask);
+ }
+
+ mul_v3_fl(final_disp, SCULPT_automasking_factor_get(ss->cache->automasking, ss, vd.index));
+
+ copy_v3_v3(proxy[vd.i], final_disp);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_elastic_deform_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ 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);
+
+ 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,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_elastic_deform_brush_task_cb_ex, &settings);
+}
+/** \} */
+
+static void do_draw_sharp_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float *offset = data->offset;
+
+ PBVHVertexIter vd;
+ SculptOrigVertData orig_data;
+ float(*proxy)[3];
+
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ /* Offset vertex. */
+ const float fade = SCULPT_brush_strength_factor(ss,
+ brush,
+ orig_data.co,
+ sqrtf(test.dist),
+ orig_data.no,
+ NULL,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ mul_v3_v3fl(proxy[vd.i], offset, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_draw_sharp_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;
+
+ /* Offset with as much as possible factored in already. */
+ float effective_normal[3];
+ SCULPT_tilt_effective_normal_get(ss, brush, effective_normal);
+ mul_v3_v3fl(offset, effective_normal, 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. */
+ BKE_curvemapping_init(brush->curve);
+
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .offset = offset,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_draw_sharp_brush_task_cb_ex, &settings);
+}
+
+/* -------------------------------------------------------------------- */
+/** \name Sculpt Topology Brush
+ * \{ */
+
+static void do_topology_slide_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+
+ PBVHVertexIter vd;
+ SculptOrigVertData orig_data;
+ float(*proxy)[3];
+
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ const float fade = SCULPT_brush_strength_factor(ss,
+ brush,
+ orig_data.co,
+ sqrtf(test.dist),
+ orig_data.no,
+ NULL,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+ float current_disp[3];
+ float current_disp_norm[3];
+ float final_disp[3] = {0.0f, 0.0f, 0.0f};
+
+ switch (brush->slide_deform_type) {
+ case BRUSH_SLIDE_DEFORM_DRAG:
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ break;
+ case BRUSH_SLIDE_DEFORM_PINCH:
+ sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
+ break;
+ case BRUSH_SLIDE_DEFORM_EXPAND:
+ sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
+ break;
+ }
+
+ normalize_v3_v3(current_disp_norm, current_disp);
+ mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ float vertex_disp[3];
+ float vertex_disp_norm[3];
+ sub_v3_v3v3(vertex_disp, SCULPT_vertex_co_get(ss, ni.index), vd.co);
+ normalize_v3_v3(vertex_disp_norm, vertex_disp);
+ if (dot_v3v3(current_disp_norm, vertex_disp_norm) > 0.0f) {
+ madd_v3_v3fl(final_disp, vertex_disp_norm, dot_v3v3(current_disp, vertex_disp));
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ mul_v3_v3fl(proxy[vd.i], final_disp, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_relax_vertex(SculptSession *ss,
+ PBVHVertexIter *vd,
+ float factor,
+ bool filter_boundary_face_sets,
+ float *r_final_pos)
+{
+ float smooth_pos[3];
+ float final_disp[3];
+ float boundary_normal[3];
+ int avg_count = 0;
+ int neighbor_count = 0;
+ zero_v3(smooth_pos);
+ zero_v3(boundary_normal);
+ const bool is_boundary = SCULPT_vertex_is_boundary(ss, vd->index);
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd->index, ni) {
+ neighbor_count++;
+ if (!filter_boundary_face_sets ||
+ (filter_boundary_face_sets && !SCULPT_vertex_has_unique_face_set(ss, ni.index))) {
+
+ /* When the vertex to relax is boundary, use only connected boundary vertices for the average
+ * position. */
+ if (is_boundary) {
+ if (!SCULPT_vertex_is_boundary(ss, ni.index)) {
+ continue;
+ }
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ avg_count++;
+
+ /* Calculate a normal for the constraint plane using the edges of the boundary. */
+ float to_neighbor[3];
+ sub_v3_v3v3(to_neighbor, SCULPT_vertex_co_get(ss, ni.index), vd->co);
+ normalize_v3(to_neighbor);
+ add_v3_v3(boundary_normal, to_neighbor);
+ }
+ else {
+ add_v3_v3(smooth_pos, SCULPT_vertex_co_get(ss, ni.index));
+ avg_count++;
+ }
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ /* Don't modify corner vertices. */
+ if (neighbor_count <= 2) {
+ copy_v3_v3(r_final_pos, vd->co);
+ return;
+ }
+
+ if (avg_count > 0) {
+ mul_v3_fl(smooth_pos, 1.0f / avg_count);
+ }
+ else {
+ copy_v3_v3(r_final_pos, vd->co);
+ return;
+ }
+
+ float plane[4];
+ float smooth_closest_plane[3];
+ float vno[3];
+
+ if (is_boundary && avg_count == 2) {
+ normalize_v3_v3(vno, boundary_normal);
+ }
+ else {
+ SCULPT_vertex_normal_get(ss, vd->index, vno);
+ }
+
+ if (is_zero_v3(vno)) {
+ copy_v3_v3(r_final_pos, vd->co);
+ return;
+ }
+
+ plane_from_point_normal_v3(plane, vd->co, vno);
+ closest_to_plane_v3(smooth_closest_plane, plane, smooth_pos);
+ sub_v3_v3v3(final_disp, smooth_closest_plane, vd->co);
+
+ mul_v3_fl(final_disp, factor);
+ add_v3_v3v3(r_final_pos, vd->co, final_disp);
+}
+
+static void do_topology_relax_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = ss->cache->bstrength;
+
+ PBVHVertexIter vd;
+ SculptOrigVertData orig_data;
+
+ SCULPT_orig_vert_data_init(&orig_data, data->ob, data->nodes[n]);
+
+ BKE_pbvh_node_add_proxy(ss->pbvh, data->nodes[n]);
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ const float fade = SCULPT_brush_strength_factor(ss,
+ brush,
+ orig_data.co,
+ sqrtf(test.dist),
+ orig_data.no,
+ NULL,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ SCULPT_relax_vertex(ss, &vd, fade * bstrength, false, vd.co);
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_slide_relax_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) {
+ return;
+ }
+
+ BKE_curvemapping_init(brush->curve);
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ if (ss->cache->alt_smooth) {
+ SCULPT_boundary_info_ensure(ob);
+ for (int i = 0; i < 4; i++) {
+ BLI_task_parallel_range(0, totnode, &data, do_topology_relax_task_cb_ex, &settings);
+ }
+ }
+ else {
+ BLI_task_parallel_range(0, totnode, &data, do_topology_slide_task_cb_ex, &settings);
+ }
+}
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sculpt Multires Displacement Eraser Brush
+ * \{ */
+
+static void do_displacement_eraser_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = clamp_f(ss->cache->bstrength, 0.0f, 1.0f);
+
+ float(*proxy)[3] = 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float limit_co[3];
+ float disp[3];
+ SCULPT_vertex_limit_surface_get(ss, vd.index, limit_co);
+ sub_v3_v3v3(disp, limit_co, vd.co);
+ mul_v3_v3fl(proxy[vd.i], disp, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_displacement_eraser_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ BKE_curvemapping_init(brush->curve);
+
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_displacement_eraser_brush_task_cb_ex, &settings);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sculpt Multires Displacement Smear Brush
+ * \{ */
+
+static void do_displacement_smear_brush_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = clamp_f(ss->cache->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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ const float fade = bstrength * SCULPT_brush_strength_factor(ss,
+ brush,
+ vd.co,
+ sqrtf(test.dist),
+ vd.no,
+ vd.fno,
+ vd.mask ? *vd.mask : 0.0f,
+ vd.index,
+ thread_id);
+
+ float current_disp[3];
+ float current_disp_norm[3];
+ float interp_limit_surface_disp[3];
+
+ copy_v3_v3(interp_limit_surface_disp, ss->cache->prev_displacement[vd.index]);
+
+ switch (brush->smear_deform_type) {
+ case BRUSH_SMEAR_DEFORM_DRAG:
+ sub_v3_v3v3(current_disp, ss->cache->location, ss->cache->last_location);
+ break;
+ case BRUSH_SMEAR_DEFORM_PINCH:
+ sub_v3_v3v3(current_disp, ss->cache->location, vd.co);
+ break;
+ case BRUSH_SMEAR_DEFORM_EXPAND:
+ sub_v3_v3v3(current_disp, vd.co, ss->cache->location);
+ break;
+ }
+
+ normalize_v3_v3(current_disp_norm, current_disp);
+ mul_v3_v3fl(current_disp, current_disp_norm, ss->cache->bstrength);
+
+ float weights_accum = 1.0f;
+
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, vd.index, ni) {
+ float vertex_disp[3];
+ float vertex_disp_norm[3];
+ float neighbor_limit_co[3];
+ SCULPT_vertex_limit_surface_get(ss, ni.index, neighbor_limit_co);
+ sub_v3_v3v3(vertex_disp,
+ ss->cache->limit_surface_co[ni.index],
+ ss->cache->limit_surface_co[vd.index]);
+ const float *neighbor_limit_surface_disp = ss->cache->prev_displacement[ni.index];
+ normalize_v3_v3(vertex_disp_norm, vertex_disp);
+
+ if (dot_v3v3(current_disp_norm, vertex_disp_norm) >= 0.0f) {
+ continue;
+ }
+
+ const float disp_interp = clamp_f(
+ -dot_v3v3(current_disp_norm, vertex_disp_norm), 0.0f, 1.0f);
+ madd_v3_v3fl(interp_limit_surface_disp, neighbor_limit_surface_disp, disp_interp);
+ weights_accum += disp_interp;
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+
+ mul_v3_fl(interp_limit_surface_disp, 1.0f / weights_accum);
+
+ float new_co[3];
+ add_v3_v3v3(new_co, ss->cache->limit_surface_co[vd.index], interp_limit_surface_disp);
+ interp_v3_v3v3(vd.co, vd.co, new_co, fade);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+static void do_displacement_smear_store_prev_disp_task_cb_ex(
+ void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ sub_v3_v3v3(ss->cache->prev_displacement[vd.index],
+ SCULPT_vertex_co_get(ss, vd.index),
+ ss->cache->limit_surface_co[vd.index]);
+ }
+ BKE_pbvh_vertex_iter_end;
+}
+
+void SCULPT_do_displacement_smear_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+
+ BKE_curvemapping_init(brush->curve);
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ if (!ss->cache->prev_displacement) {
+ ss->cache->prev_displacement = MEM_malloc_arrayN(
+ totvert, sizeof(float[3]), "prev displacement");
+ ss->cache->limit_surface_co = MEM_malloc_arrayN(totvert, sizeof(float[3]), "limit surface co");
+ for (int i = 0; i < totvert; i++) {
+ SCULPT_vertex_limit_surface_get(ss, i, ss->cache->limit_surface_co[i]);
+ sub_v3_v3v3(ss->cache->prev_displacement[i],
+ SCULPT_vertex_co_get(ss, i),
+ ss->cache->limit_surface_co[i]);
+ }
+ }
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_displacement_smear_store_prev_disp_task_cb_ex, &settings);
+ BLI_task_parallel_range(0, totnode, &data, do_displacement_smear_brush_task_cb_ex, &settings);
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sculpt Topology Rake (Shared Utility)
+ * \{ */
+
+static void do_topology_rake_bmesh_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__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);
+ normalize_v3(direction);
+
+ /* Cancel if there's no grab data. */
+ if (is_zero_v3(direction)) {
+ return;
+ }
+
+ const float bstrength = clamp_f(data->strength, 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);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ 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)) {
+ continue;
+ }
+ const float fade =
+ bstrength *
+ SCULPT_brush_strength_factor(
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, *vd.mask, vd.index, thread_id) *
+ ss->cache->pressure;
+
+ float avg[3], val[3];
+
+ SCULPT_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;
+}
+
+void SCULPT_bmesh_topology_rake(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, const int totnode, float bstrength)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ const float strength = clamp_f(bstrength, 0.0f, 1.0f);
+
+ /* Interactions increase both strength and quality. */
+ const int iterations = 3;
+
+ int iteration;
+ const int count = iterations * strength + 1;
+ const float factor = iterations * strength / count;
+
+ for (iteration = 0; iteration <= count; iteration++) {
+
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ .strength = factor,
+ };
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+
+ BLI_task_parallel_range(0, totnode, &data, do_topology_rake_bmesh_task_cb_ex, &settings);
+ }
+}
+
+/** \} */
+
+/* -------------------------------------------------------------------- */
+/** \name Sculpt Mask Brush
+ * \{ */
+
+static void do_mask_brush_draw_task_cb_ex(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict tls)
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+ const Brush *brush = data->brush;
+ const float bstrength = ss->cache->bstrength;
+
+ PBVHVertexIter vd;
+
+ SculptBrushTest test;
+ SculptBrushTestFn sculpt_brush_test_sq_fn = SCULPT_brush_test_init_with_falloff_shape(
+ ss, &test, data->brush->falloff_shape);
+ const int thread_id = BLI_task_parallel_thread_id(tls);
+
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ if (!sculpt_brush_test_sq_fn(&test, vd.co)) {
+ continue;
+ }
+
+ const float fade = SCULPT_brush_strength_factor(
+ ss, brush, vd.co, sqrtf(test.dist), vd.no, vd.fno, 0.0f, vd.index, thread_id);
+
+ if (bstrength > 0.0f) {
+ (*vd.mask) += fade * bstrength * (1.0f - *vd.mask);
+ }
+ else {
+ (*vd.mask) += fade * bstrength * (*vd.mask);
+ }
+ *vd.mask = clamp_f(*vd.mask, 0.0f, 1.0f);
+
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ BKE_pbvh_vertex_iter_end;
+ }
+}
+
+void SCULPT_do_mask_brush_draw(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ /* Threaded loop over nodes. */
+ SculptThreadedTaskData data = {
+ .sd = sd,
+ .ob = ob,
+ .brush = brush,
+ .nodes = nodes,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_mask_brush_draw_task_cb_ex, &settings);
+}
+
+void SCULPT_do_mask_brush(Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode)
+{
+ SculptSession *ss = ob->sculpt;
+ Brush *brush = BKE_paint_brush(&sd->paint);
+
+ switch ((BrushMaskTool)brush->mask_tool) {
+ case BRUSH_MASK_DRAW:
+ SCULPT_do_mask_brush_draw(sd, ob, nodes, totnode);
+ break;
+ case BRUSH_MASK_SMOOTH:
+ SCULPT_smooth(sd, ob, nodes, totnode, ss->cache->bstrength, true);
+ break;
+ }
+}
+
+/** \} */
diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h
index 4dd2a786922..b85b00fb636 100644
--- a/source/blender/editors/sculpt_paint/sculpt_intern.h
+++ b/source/blender/editors/sculpt_paint/sculpt_intern.h
@@ -29,13 +29,13 @@
#include "DNA_meshdata_types.h"
#include "DNA_vec_types.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
#include "BLI_bitmap.h"
+#include "BLI_compiler_compat.h"
#include "BLI_gsqueue.h"
#include "BLI_threads.h"
-#include "BKE_paint.h"
-#include "BKE_pbvh.h"
-
struct AutomaskingCache;
struct KeyBlock;
struct Object;
@@ -300,6 +300,10 @@ void SCULPT_calc_brush_plane(struct Sculpt *sd,
void SCULPT_calc_area_normal(
Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3]);
+void SCULPT_calc_area_normal_and_center(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_no[3], float r_area_co[3]);
+void SCULPT_calc_area_center(
+ Sculpt *sd, Object *ob, PBVHNode **nodes, int totnode, float r_area_co[3]);
int SCULPT_nearest_vertex_get(struct Sculpt *sd,
struct Object *ob,
@@ -1506,3 +1510,115 @@ void SCULPT_OT_dyntopo_detail_size_edit(struct wmOperatorType *ot);
/* Dyntopo. */
void SCULPT_OT_dynamic_topology_toggle(struct wmOperatorType *ot);
+
+/* sculpt_brushes.c */
+
+float SCULPT_clay_thumb_get_stabilized_pressure(struct StrokeCache *cache);
+
+void SCULPT_do_draw_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+
+void SCULPT_do_fill_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_scrape_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_clay_thumb_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_flatten_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_clay_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_clay_strips_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_snake_hook_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_thumb_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_rotate_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_layer_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_inflate_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_nudge_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_crease_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_pinch_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_grab_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_elastic_deform_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_draw_sharp_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_slide_relax_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+
+void SCULPT_do_displacement_smear_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_displacement_eraser_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_mask_brush_draw(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+void SCULPT_do_mask_brush(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ int totnode);
+
+void SCULPT_bmesh_topology_rake(struct Sculpt *sd,
+ struct Object *ob,
+ struct PBVHNode **nodes,
+ const int totnode,
+ float bstrength);
+
+/* end sculpt_brushes.c */
+
+/* sculpt_ops.c */
+void SCULPT_OT_brush_stroke(struct wmOperatorType *ot);
+
+/* end sculpt_ops.c */
diff --git a/source/blender/editors/sculpt_paint/sculpt_ops.c b/source/blender/editors/sculpt_paint/sculpt_ops.c
new file mode 100644
index 00000000000..119d246a770
--- /dev/null
+++ b/source/blender/editors/sculpt_paint/sculpt_ops.c
@@ -0,0 +1,1141 @@
+/*
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2006 by Nicholas Bishop
+ * All rights reserved.
+ * Implements the Sculpt Mode tools
+ */
+
+/** \file
+ * \ingroup edsculpt
+ */
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_array.h"
+#include "BLI_blenlib.h"
+#include "BLI_dial_2d.h"
+#include "BLI_ghash.h"
+#include "BLI_gsqueue.h"
+#include "BLI_hash.h"
+#include "BLI_link_utils.h"
+#include "BLI_linklist.h"
+#include "BLI_linklist_stack.h"
+#include "BLI_listbase.h"
+#include "BLI_math.h"
+#include "BLI_math_color_blend.h"
+#include "BLI_memarena.h"
+#include "BLI_rand.h"
+#include "BLI_task.h"
+#include "BLI_utildefines.h"
+#include "atomic_ops.h"
+
+#include "BLT_translation.h"
+
+#include "PIL_time.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_customdata_types.h"
+#include "DNA_listBase.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_node_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "BKE_attribute.h"
+#include "BKE_brush.h"
+#include "BKE_ccg.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_image.h"
+#include "BKE_kelvinlet.h"
+#include "BKE_key.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_fair.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_mesh_mirror.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_node.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_particle.h"
+#include "BKE_pbvh.h"
+#include "BKE_pointcache.h"
+#include "BKE_report.h"
+#include "BKE_scene.h"
+#include "BKE_screen.h"
+#include "BKE_subdiv_ccg.h"
+#include "BKE_subsurf.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "IMB_colormanagement.h"
+
+#include "GPU_batch.h"
+#include "GPU_batch_presets.h"
+#include "GPU_immediate.h"
+#include "GPU_immediate_util.h"
+#include "GPU_matrix.h"
+#include "GPU_state.h"
+
+#include "WM_api.h"
+#include "WM_message.h"
+#include "WM_toolsystem.h"
+#include "WM_types.h"
+
+#include "ED_object.h"
+#include "ED_screen.h"
+#include "ED_sculpt.h"
+#include "ED_space_api.h"
+#include "ED_transform_snap_object_context.h"
+#include "ED_view3d.h"
+
+#include "paint_intern.h"
+#include "sculpt_intern.h"
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+#include "UI_interface.h"
+#include "UI_resources.h"
+
+#include "bmesh.h"
+#include "bmesh_tools.h"
+
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+
+/* 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))
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+
+ if (!ss) {
+ return OPERATOR_FINISHED;
+ }
+ SCULPT_vertex_random_access_ensure(ss);
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
+
+ MEM_SAFE_FREE(ss->persistent_base);
+
+ const int totvert = SCULPT_vertex_count_get(ss);
+ ss->persistent_base = MEM_mallocN(sizeof(SculptPersistentBase) * totvert,
+ "layer persistent base");
+
+ for (int i = 0; i < totvert; i++) {
+ copy_v3_v3(ss->persistent_base[i].co, SCULPT_vertex_co_get(ss, i));
+ SCULPT_vertex_normal_get(ss, i, ss->persistent_base[i].no);
+ ss->persistent_base[i].disp = 0.0f;
+ }
+
+ 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";
+
+ /* API callbacks. */
+ ot->exec = sculpt_set_persistent_base_exec;
+ ot->poll = SCULPT_mode_poll;
+
+ 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);
+
+ SCULPT_pbvh_clear(ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+ return OPERATOR_FINISHED;
+}
+
+/* The BVH gets less optimal more quickly with dynamic topology than
+ * regular sculpting. There is no doubt more clever stuff we can do to
+ * optimize it on the fly, but for now this gives the user a nicer way
+ * to recalculate it than toggling modes. */
+static void SCULPT_OT_optimize(wmOperatorType *ot)
+{
+ /* Identifiers. */
+ ot->name = "Rebuild BVH";
+ ot->idname = "SCULPT_OT_optimize";
+ ot->description = "Recalculate the sculpt BVH to improve performance";
+
+ /* API callbacks. */
+ ot->exec = sculpt_optimize_exec;
+ ot->poll = SCULPT_mode_poll;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/********************* Dynamic topology symmetrize ********************/
+
+static bool sculpt_no_multires_poll(bContext *C)
+{
+ Object *ob = CTX_data_active_object(C);
+ if (SCULPT_mode_poll(C) && ob->sculpt && ob->sculpt->pbvh) {
+ return BKE_pbvh_type(ob->sculpt->pbvh) != PBVH_GRIDS;
+ }
+ return false;
+}
+
+static int sculpt_symmetrize_exec(bContext *C, wmOperator *op)
+{
+ Main *bmain = CTX_data_main(C);
+ Object *ob = CTX_data_active_object(C);
+ const Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ SculptSession *ss = ob->sculpt;
+ PBVH *pbvh = ss->pbvh;
+ const float dist = RNA_float_get(op->ptr, "merge_tolerance");
+
+ if (!pbvh) {
+ return OPERATOR_CANCELLED;
+ }
+
+ switch (BKE_pbvh_type(pbvh)) {
+ case PBVH_BMESH:
+ /* Dyntopo Symmetrize. */
+
+ /* 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(ob, "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);
+
+ /* Symmetrize and re-triangulate. */
+ BMO_op_callf(ss->bm,
+ (BMO_FLAG_DEFAULTS & ~BMO_FLAG_RESPECT_HIDE),
+ "symmetrize input=%avef direction=%i dist=%f use_shapekey=%b",
+ sd->symmetrize_direction,
+ dist,
+ true);
+ 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);
+
+ BM_mesh_toolflags_set(ss->bm, false);
+
+ /* Finish undo. */
+ BM_log_all_added(ss->bm, ss->bm_log);
+ SCULPT_undo_push_end();
+
+ break;
+ case PBVH_FACES:
+ /* Mesh Symmetrize. */
+ ED_sculpt_undo_geometry_begin(ob, "mesh symmetrize");
+ Mesh *mesh = ob->data;
+
+ BKE_mesh_mirror_apply_mirror_on_axis(bmain, mesh, sd->symmetrize_direction, dist);
+
+ ED_sculpt_undo_geometry_end(ob);
+ BKE_mesh_calc_normals(ob->data);
+ BKE_mesh_batch_cache_dirty_tag(ob->data, BKE_MESH_BATCH_DIRTY_ALL);
+
+ break;
+ case PBVH_GRIDS:
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Redraw. */
+ SCULPT_pbvh_clear(ob);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+
+ 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";
+
+ /* API callbacks. */
+ ot->exec = sculpt_symmetrize_exec;
+ ot->poll = sculpt_no_multires_poll;
+
+ RNA_def_float(ot->srna,
+ "merge_tolerance",
+ 0.001f,
+ 0.0f,
+ FLT_MAX,
+ "Merge Distance",
+ "Distance within which symmetrical vertices are merged",
+ 0.0f,
+ 1.0f);
+}
+
+/**** Toggle operator for turning sculpt mode on or off ****/
+
+static void sculpt_init_session(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
+{
+ /* Create persistent sculpt mode data. */
+ BKE_sculpt_toolsettings_data_ensure(scene);
+
+ /* Create sculpt mode session data. */
+ if (ob->sculpt != NULL) {
+ BKE_sculptsession_free(ob);
+ }
+ ob->sculpt = MEM_callocN(sizeof(SculptSession), "sculpt session");
+ ob->sculpt->mode_type = OB_MODE_SCULPT;
+
+ BKE_sculpt_ensure_orig_mesh_data(scene, ob);
+
+ BKE_scene_graph_evaluated_ensure(depsgraph, bmain);
+
+ /* This function expects a fully evaluated depsgraph. */
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, false, false, false);
+
+ /* Here we can detect geometry that was just added to Sculpt Mode as it has the
+ * SCULPT_FACE_SET_NONE assigned, so we can create a new Face Set for it. */
+ /* In sculpt mode all geometry that is assigned to SCULPT_FACE_SET_NONE is considered as not
+ * initialized, which is used is some operators that modify the mesh topology to perform certain
+ * actions in the new polys. After these operations are finished, all polys should have a valid
+ * face set ID assigned (different from SCULPT_FACE_SET_NONE) to manage their visibility
+ * correctly. */
+ /* TODO(pablodp606): Based on this we can improve the UX in future tools for creating new
+ * objects, like moving the transform pivot position to the new area or masking existing
+ * geometry. */
+ SculptSession *ss = ob->sculpt;
+ const int new_face_set = SCULPT_face_set_next_available_get(ss);
+ for (int i = 0; i < ss->totfaces; i++) {
+ if (ss->face_sets[i] == SCULPT_FACE_SET_NONE) {
+ ss->face_sets[i] = new_face_set;
+ }
+ }
+}
+
+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 sculpt mode. */
+ ob->mode |= mode_flag;
+
+ sculpt_init_session(bmain, depsgraph, scene, ob);
+
+ 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(paint, SCULPT_mode_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) {
+ MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
+
+ 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(ob, "Dynamic topology enable");
+ }
+ SCULPT_dynamic_topology_enable_ex(bmain, 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, Depsgraph *depsgraph, 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);
+ ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, reports);
+}
+
+void ED_object_sculptmode_exit_ex(Main *bmain, Depsgraph *depsgraph, Scene *scene, Object *ob)
+{
+ const int mode_flag = OB_MODE_SCULPT;
+ Mesh *me = BKE_mesh_from_object(ob);
+
+ multires_flush_sculpt_updates(ob);
+
+ /* Not needed for now. */
+#if 0
+ MultiresModifierData *mmd = BKE_sculpt_multires_active(scene, ob);
+ 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);
+ }
+
+ 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(bmain, depsgraph, scene, ob);
+
+ /* Store so we know to re-enable when entering sculpt mode. */
+ me->flag |= ME_SCULPT_DYNAMIC_TOPOLOGY;
+ }
+
+ /* Leave sculpt mode. */
+ ob->mode &= ~mode_flag;
+
+ BKE_sculptsession_free(ob);
+
+ paint_cursor_delete_textures();
+
+ /* 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);
+}
+
+void ED_object_sculptmode_exit(bContext *C, Depsgraph *depsgraph)
+{
+ 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);
+ ED_object_sculptmode_exit_ex(bmain, 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;
+
+ 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(bmain, depsgraph, scene, ob);
+ }
+ else {
+ if (depsgraph) {
+ depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
+ }
+ ED_object_sculptmode_enter_ex(bmain, depsgraph, scene, ob, false, op->reports);
+ BKE_paint_toolslots_brush_validate(bmain, &ts->sculpt->paint);
+
+ if (ob->mode & mode_flag) {
+ Mesh *me = ob->data;
+ /* Dyntopo adds its own undo step. */
+ if ((me->flag & ME_SCULPT_DYNAMIC_TOPOLOGY) == 0) {
+ /* Without this the memfile undo step is used,
+ * while it works it causes lag when undoing the first undo step, see T71564. */
+ wmWindowManager *wm = CTX_wm_manager(C);
+ if (wm->op_undo_depth <= 1) {
+ SCULPT_undo_push_begin(ob, op->type->name);
+ }
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, scene);
+
+ WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
+
+ WM_toolsystem_update_from_context_view3d(C);
+
+ 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";
+
+ /* API callbacks. */
+ ot->exec = sculpt_mode_toggle_exec;
+ ot->poll = ED_operator_object_active_editable_mesh;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+void SCULPT_geometry_preview_lines_update(bContext *C, SculptSession *ss, float radius)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *ob = CTX_data_active_object(C);
+
+ ss->preview_vert_index_count = 0;
+ int totpoints = 0;
+
+ /* This function is called from the cursor drawing code, so the PBVH may not be build yet. */
+ if (!ss->pbvh) {
+ return;
+ }
+
+ if (!ss->deform_modifiers_active) {
+ return;
+ }
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) {
+ return;
+ }
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
+
+ if (!ss->pmap) {
+ return;
+ }
+
+ float brush_co[3];
+ copy_v3_v3(brush_co, SCULPT_active_vertex_co_get(ss));
+
+ BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(SCULPT_vertex_count_get(ss), "visited_vertices");
+
+ /* Assuming an average of 6 edges per vertex in a triangulated mesh. */
+ const int max_preview_vertices = SCULPT_vertex_count_get(ss) * 3 * 2;
+
+ if (ss->preview_vert_index_list == NULL) {
+ ss->preview_vert_index_list = MEM_callocN(max_preview_vertices * sizeof(int), "preview lines");
+ }
+
+ GSQueue *not_visited_vertices = BLI_gsqueue_new(sizeof(int));
+ int active_v = SCULPT_active_vertex_get(ss);
+ BLI_gsqueue_push(not_visited_vertices, &active_v);
+
+ while (!BLI_gsqueue_is_empty(not_visited_vertices)) {
+ int from_v;
+ BLI_gsqueue_pop(not_visited_vertices, &from_v);
+ SculptVertexNeighborIter ni;
+ SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, from_v, ni) {
+ if (totpoints + (ni.size * 2) < max_preview_vertices) {
+ int to_v = ni.index;
+ ss->preview_vert_index_list[totpoints] = from_v;
+ totpoints++;
+ ss->preview_vert_index_list[totpoints] = to_v;
+ totpoints++;
+ if (BLI_BITMAP_TEST(visited_vertices, to_v)) {
+ continue;
+ }
+ BLI_BITMAP_ENABLE(visited_vertices, to_v);
+ const float *co = SCULPT_vertex_co_for_grab_active_get(ss, to_v);
+ if (len_squared_v3v3(brush_co, co) < radius * radius) {
+ BLI_gsqueue_push(not_visited_vertices, &to_v);
+ }
+ }
+ }
+ SCULPT_VERTEX_NEIGHBORS_ITER_END(ni);
+ }
+
+ BLI_gsqueue_free(not_visited_vertices);
+
+ MEM_freeN(visited_vertices);
+
+ ss->preview_vert_index_count = totpoints;
+}
+
+static int vertex_to_loop_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ ID *data;
+ data = ob->data;
+ if (data && ID_IS_LINKED(data)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ob->type != OB_MESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Mesh *mesh = ob->data;
+
+ const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
+ if (mloopcol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
+
+ const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
+ if (MPropCol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+
+ MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+ MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+
+ for (int i = 0; i < mesh->totpoly; i++) {
+ MPoly *c_poly = &polys[i];
+ for (int j = 0; j < c_poly->totloop; j++) {
+ int loop_index = c_poly->loopstart + j;
+ MLoop *c_loop = &loops[c_poly->loopstart + j];
+ float srgb_color[4];
+ linearrgb_to_srgb_v4(srgb_color, vertcols[c_loop->v].color);
+ loopcols[loop_index].r = (char)(srgb_color[0] * 255);
+ loopcols[loop_index].g = (char)(srgb_color[1] * 255);
+ loopcols[loop_index].b = (char)(srgb_color[2] * 255);
+ loopcols[loop_index].a = (char)(srgb_color[3] * 255);
+ }
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_vertex_to_loop_colors(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Sculpt Vertex Color to Vertex Color";
+ ot->description = "Copy the Sculpt Vertex Color to a regular color layer";
+ ot->idname = "SCULPT_OT_vertex_to_loop_colors";
+
+ /* api callbacks */
+ ot->poll = SCULPT_vertex_colors_poll;
+ ot->exec = vertex_to_loop_colors_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int loop_to_vertex_colors_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *ob = CTX_data_active_object(C);
+
+ ID *data;
+ data = ob->data;
+ if (data && ID_IS_LINKED(data)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (ob->type != OB_MESH) {
+ return OPERATOR_CANCELLED;
+ }
+
+ Mesh *mesh = ob->data;
+
+ const int mloopcol_layer_n = CustomData_get_active_layer(&mesh->ldata, CD_MLOOPCOL);
+ if (mloopcol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MLoopCol *loopcols = CustomData_get_layer_n(&mesh->ldata, CD_MLOOPCOL, mloopcol_layer_n);
+
+ const int MPropCol_layer_n = CustomData_get_active_layer(&mesh->vdata, CD_PROP_COLOR);
+ if (MPropCol_layer_n == -1) {
+ return OPERATOR_CANCELLED;
+ }
+ MPropCol *vertcols = CustomData_get_layer_n(&mesh->vdata, CD_PROP_COLOR, MPropCol_layer_n);
+
+ MLoop *loops = CustomData_get_layer(&mesh->ldata, CD_MLOOP);
+ MPoly *polys = CustomData_get_layer(&mesh->pdata, CD_MPOLY);
+
+ for (int i = 0; i < mesh->totpoly; i++) {
+ MPoly *c_poly = &polys[i];
+ for (int j = 0; j < c_poly->totloop; j++) {
+ int loop_index = c_poly->loopstart + j;
+ MLoop *c_loop = &loops[c_poly->loopstart + j];
+ vertcols[c_loop->v].color[0] = (loopcols[loop_index].r / 255.0f);
+ vertcols[c_loop->v].color[1] = (loopcols[loop_index].g / 255.0f);
+ vertcols[c_loop->v].color[2] = (loopcols[loop_index].b / 255.0f);
+ vertcols[c_loop->v].color[3] = (loopcols[loop_index].a / 255.0f);
+ srgb_to_linearrgb_v4(vertcols[c_loop->v].color, vertcols[c_loop->v].color);
+ }
+ }
+
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GEOM | ND_DATA, ob->data);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_loop_to_vertex_colors(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Vertex Color to Sculpt Vertex Color";
+ ot->description = "Copy the active loop color layer to the vertex color";
+ ot->idname = "SCULPT_OT_loop_to_vertex_colors";
+
+ /* api callbacks */
+ ot->poll = SCULPT_vertex_colors_poll;
+ ot->exec = loop_to_vertex_colors_exec;
+
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int sculpt_sample_color_invoke(bContext *C,
+ wmOperator *UNUSED(op),
+ const wmEvent *UNUSED(e))
+{
+ Sculpt *sd = CTX_data_tool_settings(C)->sculpt;
+ Scene *scene = CTX_data_scene(C);
+ Object *ob = CTX_data_active_object(C);
+ Brush *brush = BKE_paint_brush(&sd->paint);
+ SculptSession *ss = ob->sculpt;
+ int active_vertex = SCULPT_active_vertex_get(ss);
+ const float *active_vertex_color = SCULPT_vertex_color_get(ss, active_vertex);
+ if (!active_vertex_color) {
+ return OPERATOR_CANCELLED;
+ }
+
+ float color_srgb[3];
+ copy_v3_v3(color_srgb, active_vertex_color);
+ IMB_colormanagement_scene_linear_to_srgb_v3(color_srgb);
+ BKE_brush_color_set(scene, brush, color_srgb);
+
+ WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_sample_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Sample Color";
+ ot->idname = "SCULPT_OT_sample_color";
+ ot->description = "Sample the vertex color of the active vertex";
+
+ /* api callbacks */
+ ot->invoke = sculpt_sample_color_invoke;
+ ot->poll = SCULPT_vertex_colors_poll;
+
+ ot->flag = OPTYPE_REGISTER;
+}
+
+/**
+ * #sculpt_mask_by_color_delta_get returns values in the (0,1) range that are used to generate the
+ * mask based on the difference between two colors (the active color and the color of any other
+ * vertex). Ideally, a threshold of 0 should mask only the colors that are equal to the active
+ * color and threshold of 1 should mask all colors. In order to avoid artifacts and produce softer
+ * falloffs in the mask, the MASK_BY_COLOR_SLOPE defines the size of the transition values between
+ * masked and unmasked vertices. The smaller this value is, the sharper the generated mask is going
+ * to be.
+ */
+#define MASK_BY_COLOR_SLOPE 0.25f
+
+static float sculpt_mask_by_color_delta_get(const float *color_a,
+ const float *color_b,
+ const float threshold,
+ const bool invert)
+{
+ float len = len_v3v3(color_a, color_b);
+ /* Normalize len to the (0, 1) range. */
+ len = len / M_SQRT3;
+
+ if (len < threshold - MASK_BY_COLOR_SLOPE) {
+ len = 1.0f;
+ }
+ else if (len >= threshold) {
+ len = 0.0f;
+ }
+ else {
+ len = (-len + threshold) / MASK_BY_COLOR_SLOPE;
+ }
+
+ if (invert) {
+ return 1.0f - len;
+ }
+ return len;
+}
+
+static float sculpt_mask_by_color_final_mask_get(const float current_mask,
+ const float new_mask,
+ const bool invert,
+ const bool preserve_mask)
+{
+ if (preserve_mask) {
+ if (invert) {
+ return min_ff(current_mask, new_mask);
+ }
+ return max_ff(current_mask, new_mask);
+ }
+ return new_mask;
+}
+
+typedef struct MaskByColorContiguousFloodFillData {
+ float threshold;
+ bool invert;
+ float *new_mask;
+ float initial_color[3];
+} MaskByColorContiguousFloodFillData;
+
+static void do_mask_by_color_contiguous_update_nodes_cb(
+ void *__restrict userdata, const int n, const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
+ bool update_node = false;
+
+ const bool invert = data->mask_by_color_invert;
+ const bool preserve_mask = data->mask_by_color_preserve_mask;
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ const float current_mask = *vd.mask;
+ const float new_mask = data->mask_by_color_floodfill[vd.index];
+ *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
+ if (current_mask == *vd.mask) {
+ continue;
+ }
+ update_node = true;
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+ if (update_node) {
+ BKE_pbvh_node_mark_redraw(data->nodes[n]);
+ }
+}
+
+static bool sculpt_mask_by_color_contiguous_floodfill_cb(
+ SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata)
+{
+ MaskByColorContiguousFloodFillData *data = userdata;
+ const float *current_color = SCULPT_vertex_color_get(ss, to_v);
+ float new_vertex_mask = sculpt_mask_by_color_delta_get(
+ current_color, data->initial_color, data->threshold, data->invert);
+ data->new_mask[to_v] = new_vertex_mask;
+
+ if (is_duplicate) {
+ data->new_mask[to_v] = data->new_mask[from_v];
+ }
+
+ float len = len_v3v3(current_color, data->initial_color);
+ len = len / M_SQRT3;
+ return len <= data->threshold;
+}
+
+static void sculpt_mask_by_color_contiguous(Object *object,
+ const int vertex,
+ const float threshold,
+ const bool invert,
+ const bool preserve_mask)
+{
+ SculptSession *ss = object->sculpt;
+ const int totvert = SCULPT_vertex_count_get(ss);
+
+ float *new_mask = MEM_calloc_arrayN(totvert, sizeof(float), "new mask");
+
+ if (invert) {
+ for (int i = 0; i < totvert; i++) {
+ new_mask[i] = 1.0f;
+ }
+ }
+
+ SculptFloodFill flood;
+ SCULPT_floodfill_init(ss, &flood);
+ SCULPT_floodfill_add_initial(&flood, vertex);
+
+ MaskByColorContiguousFloodFillData ffd;
+ ffd.threshold = threshold;
+ ffd.invert = invert;
+ ffd.new_mask = new_mask;
+ copy_v3_v3(ffd.initial_color, SCULPT_vertex_color_get(ss, vertex));
+
+ SCULPT_floodfill_execute(ss, &flood, sculpt_mask_by_color_contiguous_floodfill_cb, &ffd);
+ SCULPT_floodfill_free(&flood);
+
+ int totnode;
+ PBVHNode **nodes;
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ SculptThreadedTaskData data = {
+ .ob = object,
+ .nodes = nodes,
+ .mask_by_color_floodfill = new_mask,
+ .mask_by_color_vertex = vertex,
+ .mask_by_color_threshold = threshold,
+ .mask_by_color_invert = invert,
+ .mask_by_color_preserve_mask = preserve_mask,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(
+ 0, totnode, &data, do_mask_by_color_contiguous_update_nodes_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+
+ MEM_freeN(new_mask);
+}
+
+static void do_mask_by_color_task_cb(void *__restrict userdata,
+ const int n,
+ const TaskParallelTLS *__restrict UNUSED(tls))
+{
+ SculptThreadedTaskData *data = userdata;
+ SculptSession *ss = data->ob->sculpt;
+
+ SCULPT_undo_push_node(data->ob, data->nodes[n], SCULPT_UNDO_MASK);
+ bool update_node = false;
+
+ const float threshold = data->mask_by_color_threshold;
+ const bool invert = data->mask_by_color_invert;
+ const bool preserve_mask = data->mask_by_color_preserve_mask;
+ const float *active_color = SCULPT_vertex_color_get(ss, data->mask_by_color_vertex);
+
+ PBVHVertexIter vd;
+ BKE_pbvh_vertex_iter_begin (ss->pbvh, data->nodes[n], vd, PBVH_ITER_UNIQUE) {
+ const float current_mask = *vd.mask;
+ const float new_mask = sculpt_mask_by_color_delta_get(active_color, vd.col, threshold, invert);
+ *vd.mask = sculpt_mask_by_color_final_mask_get(current_mask, new_mask, invert, preserve_mask);
+
+ if (current_mask == *vd.mask) {
+ continue;
+ }
+ update_node = true;
+ if (vd.mvert) {
+ vd.mvert->flag |= ME_VERT_PBVH_UPDATE;
+ }
+ }
+ BKE_pbvh_vertex_iter_end;
+ if (update_node) {
+ BKE_pbvh_node_mark_redraw(data->nodes[n]);
+ }
+}
+
+static void sculpt_mask_by_color_full_mesh(Object *object,
+ const int vertex,
+ const float threshold,
+ const bool invert,
+ const bool preserve_mask)
+{
+ SculptSession *ss = object->sculpt;
+
+ int totnode;
+ PBVHNode **nodes;
+ BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode);
+
+ SculptThreadedTaskData data = {
+ .ob = object,
+ .nodes = nodes,
+ .mask_by_color_vertex = vertex,
+ .mask_by_color_threshold = threshold,
+ .mask_by_color_invert = invert,
+ .mask_by_color_preserve_mask = preserve_mask,
+ };
+
+ TaskParallelSettings settings;
+ BKE_pbvh_parallel_range_settings(&settings, true, totnode);
+ BLI_task_parallel_range(0, totnode, &data, do_mask_by_color_task_cb, &settings);
+
+ MEM_SAFE_FREE(nodes);
+}
+
+static int sculpt_mask_by_color_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C);
+ Object *ob = CTX_data_active_object(C);
+ SculptSession *ss = ob->sculpt;
+
+ BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false);
+
+ /* Color data is not available in Multires. */
+ if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (!ss->vcol) {
+ return OPERATOR_CANCELLED;
+ }
+
+ SCULPT_vertex_random_access_ensure(ss);
+
+ /* Tools that are not brushes do not have the brush gizmo to update the vertex as the mouse move,
+ * so it needs to be updated here. */
+ SculptCursorGeometryInfo sgi;
+ float mouse[2];
+ mouse[0] = event->mval[0];
+ mouse[1] = event->mval[1];
+ SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false);
+
+ SCULPT_undo_push_begin(ob, "Mask by color");
+
+ const int active_vertex = SCULPT_active_vertex_get(ss);
+ const float threshold = RNA_float_get(op->ptr, "threshold");
+ const bool invert = RNA_boolean_get(op->ptr, "invert");
+ const bool preserve_mask = RNA_boolean_get(op->ptr, "preserve_previous_mask");
+
+ if (RNA_boolean_get(op->ptr, "contiguous")) {
+ sculpt_mask_by_color_contiguous(ob, active_vertex, threshold, invert, preserve_mask);
+ }
+ else {
+ sculpt_mask_by_color_full_mesh(ob, active_vertex, threshold, invert, preserve_mask);
+ }
+
+ BKE_pbvh_update_vertex_data(ss->pbvh, PBVH_UpdateMask);
+ SCULPT_undo_push_end();
+
+ SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK);
+
+ return OPERATOR_FINISHED;
+}
+
+static void SCULPT_OT_mask_by_color(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Mask by Color";
+ ot->idname = "SCULPT_OT_mask_by_color";
+ ot->description = "Creates a mask based on the sculpt vertex colors";
+
+ /* api callbacks */
+ ot->invoke = sculpt_mask_by_color_invoke;
+ ot->poll = SCULPT_vertex_colors_poll;
+
+ ot->flag = OPTYPE_REGISTER;
+
+ ot->prop = RNA_def_boolean(
+ ot->srna, "contiguous", false, "Contiguous", "Mask only contiguous color areas");
+
+ ot->prop = RNA_def_boolean(ot->srna, "invert", false, "Invert", "Invert the generated mask");
+ ot->prop = RNA_def_boolean(
+ ot->srna,
+ "preserve_previous_mask",
+ false,
+ "Preserve Previous Mask",
+ "Preserve the previous mask and add or subtract the new one generated by the colors");
+
+ RNA_def_float(ot->srna,
+ "threshold",
+ 0.35f,
+ 0.0f,
+ 1.0f,
+ "Threshold",
+ "How much changes in color affect the mask generation",
+ 0.0f,
+ 1.0f);
+}
+
+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_mesh_filter);
+ WM_operatortype_append(SCULPT_OT_mask_filter);
+ WM_operatortype_append(SCULPT_OT_dirty_mask);
+ WM_operatortype_append(SCULPT_OT_mask_expand);
+ WM_operatortype_append(SCULPT_OT_set_pivot_position);
+ WM_operatortype_append(SCULPT_OT_face_sets_create);
+ WM_operatortype_append(SCULPT_OT_face_sets_change_visibility);
+ WM_operatortype_append(SCULPT_OT_face_sets_randomize_colors);
+ WM_operatortype_append(SCULPT_OT_face_sets_init);
+ WM_operatortype_append(SCULPT_OT_cloth_filter);
+ WM_operatortype_append(SCULPT_OT_face_sets_edit);
+ WM_operatortype_append(SCULPT_OT_face_set_lasso_gesture);
+ WM_operatortype_append(SCULPT_OT_face_set_box_gesture);
+ WM_operatortype_append(SCULPT_OT_trim_box_gesture);
+ WM_operatortype_append(SCULPT_OT_trim_lasso_gesture);
+ WM_operatortype_append(SCULPT_OT_project_line_gesture);
+
+ WM_operatortype_append(SCULPT_OT_sample_color);
+ WM_operatortype_append(SCULPT_OT_loop_to_vertex_colors);
+ WM_operatortype_append(SCULPT_OT_vertex_to_loop_colors);
+ WM_operatortype_append(SCULPT_OT_color_filter);
+ WM_operatortype_append(SCULPT_OT_mask_by_color);
+ WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit);
+ WM_operatortype_append(SCULPT_OT_mask_init);
+
+ WM_operatortype_append(SCULPT_OT_expand);
+}
diff --git a/source/blender/editors/space_file/asset_catalog_tree_view.cc b/source/blender/editors/space_file/asset_catalog_tree_view.cc
index 86c4b78dea4..f6c449a0584 100644
--- a/source/blender/editors/space_file/asset_catalog_tree_view.cc
+++ b/source/blender/editors/space_file/asset_catalog_tree_view.cc
@@ -679,7 +679,7 @@ using namespace blender::ed::asset_browser;
FileAssetCatalogFilterSettingsHandle *file_create_asset_catalog_filter_settings()
{
- AssetCatalogFilterSettings *filter_settings = OBJECT_GUARDED_NEW(AssetCatalogFilterSettings);
+ AssetCatalogFilterSettings *filter_settings = MEM_new<AssetCatalogFilterSettings>(__func__);
return reinterpret_cast<FileAssetCatalogFilterSettingsHandle *>(filter_settings);
}
@@ -688,7 +688,8 @@ void file_delete_asset_catalog_filter_settings(
{
AssetCatalogFilterSettings **filter_settings = reinterpret_cast<AssetCatalogFilterSettings **>(
filter_settings_handle);
- OBJECT_GUARDED_SAFE_DELETE(*filter_settings, AssetCatalogFilterSettings);
+ MEM_delete(*filter_settings);
+ *filter_settings = nullptr;
}
bool file_set_asset_catalog_filter_settings(
diff --git a/source/blender/editors/space_info/info_stats.cc b/source/blender/editors/space_info/info_stats.cc
index bcf26743030..005ae0214cd 100644
--- a/source/blender/editors/space_info/info_stats.cc
+++ b/source/blender/editors/space_info/info_stats.cc
@@ -43,6 +43,7 @@
#include "BLT_translation.h"
+#include "BKE_action.h"
#include "BKE_armature.h"
#include "BKE_blender_version.h"
#include "BKE_context.h"
@@ -351,7 +352,7 @@ static void stats_object_pose(const Object *ob, SceneStats *stats)
LISTBASE_FOREACH (bPoseChannel *, pchan, &ob->pose->chanbase) {
stats->totbone++;
if (pchan->bone && (pchan->bone->flag & BONE_SELECTED)) {
- if (pchan->bone->layer & arm->layer) {
+ if (BKE_pose_is_layer_visible(arm, pchan)) {
stats->totbonesel++;
}
}
diff --git a/source/blender/editors/space_node/drawnode.cc b/source/blender/editors/space_node/drawnode.cc
index bbfd886ce56..90fd6e7d657 100644
--- a/source/blender/editors/space_node/drawnode.cc
+++ b/source/blender/editors/space_node/drawnode.cc
@@ -36,6 +36,7 @@
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_tree_update.h"
#include "BKE_scene.h"
#include "BKE_tracking.h"
@@ -1558,7 +1559,8 @@ static void node_property_update_default(Main *bmain, Scene *UNUSED(scene), Poin
{
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
bNode *node = (bNode *)ptr->data;
- ED_node_tag_update_nodetree(bmain, ntree, node);
+ BKE_ntree_update_tag_node_property(ntree, node);
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
static void node_socket_template_properties_update(bNodeType *ntype, bNodeSocketTemplate *stemp)
diff --git a/source/blender/editors/space_node/link_drag_search.cc b/source/blender/editors/space_node/link_drag_search.cc
index e1ba36e81c0..a2dd32b7cc6 100644
--- a/source/blender/editors/space_node/link_drag_search.cc
+++ b/source/blender/editors/space_node/link_drag_search.cc
@@ -29,6 +29,8 @@
#include "WM_api.h"
+#include "ED_node.h"
+
#include "node_intern.hh"
using blender::nodes::SocketLinkOperation;
@@ -77,7 +79,7 @@ static void add_group_input_node_fn(nodes::LinkSearchOpParams &params)
bNode &group_input = params.add_node("NodeGroupInput");
/* This is necessary to create the new sockets in the other input nodes. */
- ntreeUpdateTree(CTX_data_main(&params.C), &params.node_tree);
+ ED_node_tree_propagate_change(&params.C, CTX_data_main(&params.C), &params.node_tree);
/* Hide the new input in all other group input nodes, to avoid making them taller. */
LISTBASE_FOREACH (bNode *, node, &params.node_tree.nodes) {
@@ -203,9 +205,7 @@ static void link_drag_search_exec_fn(bContext *C, void *arg1, void *arg2)
/* Ideally it would be possible to tag the node tree in some way so it updates only after the
* translate operation is finished, but normally moving nodes around doesn't cause updates. */
- ntreeUpdateTree(&bmain, snode.edittree);
- snode_notify(*C, snode);
- snode_dag_update(*C, snode);
+ ED_node_tree_propagate_change(C, &bmain, snode.edittree);
/* Start translation operator with the new node. */
wmOperatorType *ot = WM_operatortype_find("TRANSFORM_OT_translate", true);
@@ -288,4 +288,4 @@ void invoke_node_link_drag_add_menu(bContext &C,
UI_popup_block_invoke_ex(&C, create_search_popup_block, storage, nullptr, false);
}
-} // namespace blender::ed::space_node \ No newline at end of file
+} // namespace blender::ed::space_node
diff --git a/source/blender/editors/space_node/node_add.cc b/source/blender/editors/space_node/node_add.cc
index c6a5e8e68c0..0bb090f9a5f 100644
--- a/source/blender/editors/space_node/node_add.cc
+++ b/source/blender/editors/space_node/node_add.cc
@@ -36,6 +36,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_tree_update.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_texture.h"
@@ -83,15 +84,8 @@ bNode *node_add_node(const bContext &C, const char *idname, int type, float locx
nodeSetSelected(node, true);
- ntreeUpdateTree(&bmain, snode.edittree);
ED_node_set_active(&bmain, &snode, snode.edittree, node, nullptr);
-
- snode_update(snode, node);
-
- if (snode.nodetree->type == NTREE_TEXTURE) {
- ntreeTexCheckCyclics(snode.edittree);
- }
-
+ BKE_ntree_update_main_tree(&bmain, snode.edittree, nullptr);
return node;
}
@@ -281,10 +275,7 @@ static int add_reroute_exec(bContext *C, wmOperator *op)
BLI_freelistN(&input_links);
/* always last */
- ntreeUpdateTree(CTX_data_main(C), &ntree);
- snode_notify(*C, snode);
- snode_dag_update(*C, snode);
-
+ ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
return OPERATOR_FINISHED;
}
@@ -385,12 +376,7 @@ static int node_add_group_exec(bContext *C, wmOperator *op)
id_us_plus(group_node->id);
nodeSetActive(ntree, group_node);
- ntreeUpdateTree(bmain, node_group);
- ntreeUpdateTree(bmain, ntree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
-
+ ED_node_tree_propagate_change(C, bmain, nullptr);
return OPERATOR_FINISHED;
}
@@ -479,12 +465,7 @@ static int node_add_object_exec(bContext *C, wmOperator *op)
id_us_plus(&object->id);
nodeSetActive(ntree, object_node);
- ntreeUpdateTree(bmain, ntree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
-
- ED_node_tag_update_nodetree(bmain, ntree, object_node);
+ ED_node_tree_propagate_change(C, bmain, ntree);
DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
@@ -587,14 +568,9 @@ static int node_add_texture_exec(bContext *C, wmOperator *op)
id_us_plus(&texture->id);
nodeSetActive(ntree, texture_node);
- ntreeUpdateTree(bmain, ntree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, bmain, ntree);
DEG_relations_tag_update(bmain);
- ED_node_tag_update_nodetree(bmain, ntree, texture_node);
-
return OPERATOR_FINISHED;
}
@@ -701,14 +677,9 @@ static int node_add_collection_exec(bContext *C, wmOperator *op)
id_us_plus(&collection->id);
nodeSetActive(ntree, collection_node);
- ntreeUpdateTree(bmain, ntree);
-
- snode_notify(*C, snode);
- snode_dag_update(*C, snode);
+ ED_node_tree_propagate_change(C, bmain, ntree);
DEG_relations_tag_update(bmain);
- ED_node_tag_update_nodetree(bmain, ntree, collection_node);
-
return OPERATOR_FINISHED;
}
@@ -834,8 +805,7 @@ static int node_add_file_exec(bContext *C, wmOperator *op)
WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima);
}
- snode_notify(*C, snode);
- snode_dag_update(*C, snode);
+ ED_node_tree_propagate_change(C, bmain, snode.edittree);
DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
@@ -937,8 +907,7 @@ static int node_add_mask_exec(bContext *C, wmOperator *op)
node->id = mask;
id_us_plus(mask);
- snode_notify(*C, snode);
- snode_dag_update(*C, snode);
+ ED_node_tree_propagate_change(C, bmain, snode.edittree);
DEG_relations_tag_update(bmain);
return OPERATOR_FINISHED;
diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc
index d68f16f6197..635ef41d859 100644
--- a/source/blender/editors/space_node/node_draw.cc
+++ b/source/blender/editors/space_node/node_draw.cc
@@ -176,35 +176,6 @@ void ED_node_tag_update_id(ID *id)
}
}
-void ED_node_tag_update_nodetree(Main *bmain, bNodeTree *ntree, bNode *node)
-{
- if (!ntree) {
- return;
- }
-
- bool do_tag_update = true;
- if (node != nullptr) {
- if (!node_connected_to_output(*bmain, *ntree, *node)) {
- do_tag_update = false;
- }
- }
-
- /* Look through all datablocks to support groups. */
- if (do_tag_update) {
- FOREACH_NODETREE_BEGIN (bmain, tntree, id) {
- /* Check if nodetree uses the group. */
- if (ntreeHasTree(tntree, ntree)) {
- ED_node_tag_update_id(id);
- }
- }
- FOREACH_NODETREE_END;
- }
-
- if (ntree->type == NTREE_TEXTURE) {
- ntreeTexCheckCyclics(ntree);
- }
-}
-
static bool compare_nodes(const bNode *a, const bNode *b)
{
/* These tell if either the node or any of the parent nodes is selected.
@@ -365,6 +336,10 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
PointerRNA nodeptr;
RNA_pointer_create(&ntree.id, &RNA_Node, &node, &nodeptr);
+ const bool node_options = node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS);
+ const bool inputs_first = node.inputs.first &&
+ !(node.outputs.first || (node.flag & NODE_PREVIEW) || node_options);
+
/* Get "global" coordinates. */
float2 loc = node_to_view(node, float2(0));
/* Round the node origin because text contents are always pixel-aligned. */
@@ -377,7 +352,7 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
dy -= NODE_DY;
/* Add a little bit of padding above the top socket. */
- if (node.outputs.first || node.inputs.first) {
+ if (node.outputs.first || inputs_first) {
dy -= NODE_DYS / 2;
}
@@ -478,7 +453,7 @@ static void node_update_basis(const bContext &C, bNodeTree &ntree, bNode &node,
}
/* Buttons rect? */
- if (node.typeinfo->draw_buttons && (node.flag & NODE_OPTIONS)) {
+ if (node_options) {
dy -= NODE_DYS / 2;
uiLayout *layout = UI_block_layout(&block,
@@ -2414,9 +2389,11 @@ static void node_update_nodetree(const bContext &C,
bNode &node = *nodes[i];
uiBlock &block = *blocks[i];
if (node.type == NODE_FRAME) {
- frame_node_prepare_for_draw(node, nodes);
+ /* Frame sizes are calculated after all other nodes have calculating their #totr. */
+ continue;
}
- else if (node.type == NODE_REROUTE) {
+
+ if (node.type == NODE_REROUTE) {
reroute_node_prepare_for_draw(node);
}
else {
@@ -2428,6 +2405,13 @@ static void node_update_nodetree(const bContext &C,
}
}
}
+
+ /* Now calculate the size of frame nodes, which can depend on the size of other nodes. */
+ for (const int i : nodes.index_range()) {
+ if (nodes[i]->type == NODE_FRAME) {
+ frame_node_prepare_for_draw(*nodes[i], nodes);
+ }
+ }
}
static void frame_node_draw_label(const bNodeTree &ntree,
diff --git a/source/blender/editors/space_node/node_edit.cc b/source/blender/editors/space_node/node_edit.cc
index fb90e2bfe50..ba2327c742b 100644
--- a/source/blender/editors/space_node/node_edit.cc
+++ b/source/blender/editors/space_node/node_edit.cc
@@ -38,6 +38,7 @@
#include "BKE_main.h"
#include "BKE_material.h"
#include "BKE_node.h"
+#include "BKE_node_tree_update.h"
#include "BKE_report.h"
#include "BKE_scene.h"
#include "BKE_workspace.h"
@@ -383,31 +384,11 @@ bool composite_node_editable(bContext *C)
return false;
}
-void snode_dag_update(bContext &C, SpaceNode &snode)
+static void send_notifiers_after_tree_change(ID *id, bNodeTree *ntree)
{
- Main *bmain = CTX_data_main(&C);
+ WM_main_add_notifier(NC_NODE | NA_EDITED, nullptr);
- /* for groups, update all ID's using this */
- if ((snode.edittree->id.flag & LIB_EMBEDDED_DATA) == 0) {
- FOREACH_NODETREE_BEGIN (bmain, tntree, id) {
- if (ntreeHasTree(tntree, snode.edittree)) {
- DEG_id_tag_update(id, 0);
- }
- }
- FOREACH_NODETREE_END;
- }
-
- DEG_id_tag_update(snode.id, 0);
- DEG_id_tag_update(&snode.nodetree->id, 0);
-}
-
-void snode_notify(bContext &C, SpaceNode &snode)
-{
- ID *id = snode.id;
-
- WM_event_add_notifier(&C, NC_NODE | NA_EDITED, nullptr);
-
- if (ED_node_is_shader(&snode)) {
+ if (ntree->type == NTREE_SHADER) {
if (GS(id->name) == ID_MA) {
WM_main_add_notifier(NC_MATERIAL | ND_SHADING, id);
}
@@ -418,17 +399,37 @@ void snode_notify(bContext &C, SpaceNode &snode)
WM_main_add_notifier(NC_WORLD | ND_WORLD, id);
}
}
- else if (ED_node_is_compositor(&snode)) {
- WM_event_add_notifier(&C, NC_SCENE | ND_NODES, id);
+ else if (ntree->type == NTREE_COMPOSIT) {
+ WM_main_add_notifier(NC_SCENE | ND_NODES, id);
}
- else if (ED_node_is_texture(&snode)) {
- WM_event_add_notifier(&C, NC_TEXTURE | ND_NODES, id);
+ else if (ntree->type == NTREE_TEXTURE) {
+ WM_main_add_notifier(NC_TEXTURE | ND_NODES, id);
}
- else if (ED_node_is_geometry(&snode)) {
+ else if (ntree->type == NTREE_GEOMETRY) {
WM_main_add_notifier(NC_OBJECT | ND_MODIFIER, id);
}
}
+void ED_node_tree_propagate_change(const bContext *C, Main *bmain, bNodeTree *root_ntree)
+{
+ if (C != nullptr) {
+ SpaceNode *snode = CTX_wm_space_node(C);
+ if (snode != nullptr && root_ntree != nullptr) {
+ send_notifiers_after_tree_change(snode->id, root_ntree);
+ }
+ }
+
+ NodeTreeUpdateExtraParams params = {nullptr};
+ params.tree_changed_fn = [](ID *id, bNodeTree *ntree, void *UNUSED(user_data)) {
+ send_notifiers_after_tree_change(id, ntree);
+ };
+ params.tree_output_changed_fn = [](ID *UNUSED(id), bNodeTree *ntree, void *UNUSED(user_data)) {
+ DEG_id_tag_update(&ntree->id, ID_RECALC_NTREE_OUTPUT);
+ };
+
+ BKE_ntree_update_main_tree(bmain, root_ntree, &params);
+}
+
void ED_node_set_tree_type(SpaceNode *snode, bNodeTreeType *typeinfo)
{
if (typeinfo) {
@@ -477,7 +478,7 @@ void ED_node_shader_default(const bContext *C, ID *id)
}
ma->nodetree = ntreeCopyTree(bmain, ma_default->nodetree);
- ntreeUpdateTree(bmain, ma->nodetree);
+ BKE_ntree_update_main_tree(bmain, ma->nodetree, nullptr);
}
else if (ELEM(GS(id->name), ID_WO, ID_LA)) {
/* Emission */
@@ -517,7 +518,7 @@ void ED_node_shader_default(const bContext *C, ID *id)
output->locx = 300.0f;
output->locy = 300.0f;
nodeSetActive(ntree, output);
- ntreeUpdateTree(bmain, ntree);
+ BKE_ntree_update_main_tree(bmain, ntree, nullptr);
}
else {
printf("ED_node_shader_default called on wrong ID type.\n");
@@ -555,7 +556,7 @@ void ED_node_composit_default(const bContext *C, struct Scene *sce)
bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
nodeAddLink(sce->nodetree, in, fromsock, out, tosock);
- ntreeUpdateTree(CTX_data_main(C), sce->nodetree);
+ BKE_ntree_update_main_tree(CTX_data_main(C), sce->nodetree, nullptr);
}
void ED_node_texture_default(const bContext *C, Tex *tex)
@@ -583,7 +584,7 @@ void ED_node_texture_default(const bContext *C, Tex *tex)
bNodeSocket *tosock = (bNodeSocket *)out->inputs.first;
nodeAddLink(tex->nodetree, in, fromsock, out, tosock);
- ntreeUpdateTree(CTX_data_main(C), tex->nodetree);
+ BKE_ntree_update_main_tree(CTX_data_main(C), tex->nodetree, nullptr);
}
/**
@@ -628,28 +629,6 @@ void snode_set_context(const bContext &C)
}
}
-void snode_update(SpaceNode &snode, bNode *node)
-{
- /* XXX this only updates nodes in the current node space tree path.
- * The function supposedly should update any potential group node linking to changed tree,
- * this really requires a working depsgraph ...
- */
-
- /* update all edited group nodes */
- bNodeTreePath *path = (bNodeTreePath *)snode.treepath.last;
- if (path) {
- bNodeTree *ngroup = path->nodetree;
- for (path = path->prev; path; path = path->prev) {
- nodeUpdateID(path->nodetree, (ID *)ngroup);
- ngroup = path->nodetree;
- }
- }
-
- if (node) {
- nodeUpdate(snode.edittree, node);
- }
-}
-
void ED_node_set_active(
Main *bmain, SpaceNode *snode, bNodeTree *ntree, bNode *node, bool *r_active_texture_changed)
{
@@ -697,14 +676,10 @@ void ED_node_set_active(
}
node->flag |= NODE_DO_OUTPUT;
- if (was_output == 0) {
- ED_node_tag_update_nodetree(bmain, ntree, node);
- }
- }
- else if (do_update) {
- ED_node_tag_update_nodetree(bmain, ntree, node);
}
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
+
if ((node->flag & NODE_ACTIVE_TEXTURE) && !was_active_texture) {
/* If active texture changed, free glsl materials. */
LISTBASE_FOREACH (Material *, ma, &bmain->materials) {
@@ -750,7 +725,7 @@ void ED_node_set_active(
if (r_active_texture_changed) {
*r_active_texture_changed = true;
}
- ED_node_tag_update_nodetree(bmain, ntree, node);
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
WM_main_add_notifier(NC_IMAGE, nullptr);
}
@@ -767,7 +742,7 @@ void ED_node_set_active(
node->flag |= NODE_DO_OUTPUT;
if (was_output == 0) {
- ED_node_tag_update_nodetree(bmain, ntree, node);
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
/* Adding a node doesn't link this yet. */
@@ -782,11 +757,11 @@ void ED_node_set_active(
}
node->flag |= NODE_DO_OUTPUT;
- ED_node_tag_update_nodetree(bmain, ntree, node);
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
}
else if (do_update) {
- ED_node_tag_update_nodetree(bmain, ntree, node);
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
}
else if (ntree->type == NTREE_TEXTURE) {
@@ -1302,7 +1277,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
SpaceNode *snode = CTX_wm_space_node(C);
bNodeTree *ntree = snode->edittree;
const bool keep_inputs = RNA_boolean_get(op->ptr, "keep_inputs");
- bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
@@ -1310,9 +1284,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
if (node->flag & SELECT) {
BKE_node_copy_store_new_pointers(ntree, node, LIB_ID_COPY_DEFAULT);
-
- /* To ensure redraws or re-renders happen. */
- ED_node_tag_update_id(snode->id);
}
/* make sure we don't copy new nodes again! */
@@ -1378,8 +1349,6 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
nodeSetSelected(node, false);
node->flag &= ~(NODE_ACTIVE | NODE_ACTIVE_TEXTURE);
nodeSetSelected(newnode, true);
-
- do_tag_update |= (do_tag_update || node_connected_to_output(*bmain, *ntree, *newnode));
}
/* make sure we don't copy new nodes again! */
@@ -1388,13 +1357,7 @@ static int node_duplicate_exec(bContext *C, wmOperator *op)
}
}
- ntreeUpdateTree(CTX_data_main(C), snode->edittree);
-
- snode_notify(*C, *snode);
- if (do_tag_update) {
- snode_dag_update(*C, *snode);
- }
-
+ ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@@ -1485,8 +1448,7 @@ static int node_read_viewlayers_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@@ -1653,7 +1615,7 @@ static int node_preview_toggle_exec(bContext *C, wmOperator *UNUSED(op))
node_flag_toggle_exec(snode, NODE_PREVIEW);
- snode_notify(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
return OPERATOR_FINISHED;
}
@@ -1732,7 +1694,7 @@ static int node_socket_toggle_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- ntreeUpdateTree(CTX_data_main(C), snode->edittree);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
@@ -1760,23 +1722,16 @@ static int node_mute_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
LISTBASE_FOREACH (bNode *, node, &snode->edittree->nodes) {
if ((node->flag & SELECT) && !node->typeinfo->no_muting) {
node->flag ^= NODE_MUTED;
- snode_update(*snode, node);
- do_tag_update |= (do_tag_update ||
- node_connected_to_output(*bmain, *snode->edittree, *node));
}
}
- snode_notify(*C, *snode);
- if (do_tag_update) {
- snode_dag_update(*C, *snode);
- }
+ ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@@ -1802,24 +1757,16 @@ static int node_delete_exec(bContext *C, wmOperator *UNUSED(op))
{
Main *bmain = CTX_data_main(C);
SpaceNode *snode = CTX_wm_space_node(C);
- bool do_tag_update = false;
ED_preview_kill_jobs(CTX_wm_manager(C), bmain);
LISTBASE_FOREACH_MUTABLE (bNode *, node, &snode->edittree->nodes) {
if (node->flag & SELECT) {
- do_tag_update |= (do_tag_update ||
- node_connected_to_output(*bmain, *snode->edittree, *node));
nodeRemoveNode(bmain, snode->edittree, node, true);
}
}
- ntreeUpdateTree(CTX_data_main(C), snode->edittree);
-
- snode_notify(*C, *snode);
- if (do_tag_update) {
- snode_dag_update(*C, *snode);
- }
+ ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@@ -1863,10 +1810,7 @@ static int node_switch_view_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- ntreeUpdateTree(CTX_data_main(C), snode->edittree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
return OPERATOR_FINISHED;
}
@@ -1901,10 +1845,7 @@ static int node_delete_reconnect_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- ntreeUpdateTree(CTX_data_main(C), snode->edittree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, bmain, snode->edittree);
return OPERATOR_FINISHED;
}
@@ -1951,7 +1892,7 @@ static int node_output_file_add_socket_exec(bContext *C, wmOperator *op)
RNA_string_get(op->ptr, "file_path", file_path);
ntreeCompositOutputFileAddSocket(ntree, node, file_path, &scene->r.im_format);
- snode_notify(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
return OPERATOR_FINISHED;
}
@@ -2000,7 +1941,7 @@ static int node_output_file_remove_active_socket_exec(bContext *C, wmOperator *U
return OPERATOR_CANCELLED;
}
- snode_notify(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
return OPERATOR_FINISHED;
}
@@ -2067,7 +2008,7 @@ static int node_output_file_move_active_socket_exec(bContext *C, wmOperator *op)
nimf->active_input++;
}
- snode_notify(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
return OPERATOR_FINISHED;
}
@@ -2313,10 +2254,7 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op)
}
Main *bmain = CTX_data_main(C);
- ntreeUpdateTree(bmain, snode->edittree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, bmain, snode->edittree);
/* Pasting nodes can create arbitrary new relations, because nodes can reference IDs. */
DEG_relations_tag_update(bmain);
@@ -2384,10 +2322,7 @@ static int ntree_socket_add_exec(bContext *C, wmOperator *op)
/* make the new socket active */
sock->flag |= SELECT;
- ntreeUpdateTree(CTX_data_main(C), ntree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode->edittree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
@@ -2434,10 +2369,7 @@ static int ntree_socket_remove_exec(bContext *C, wmOperator *op)
active_sock->flag |= SELECT;
}
- ntreeUpdateTree(CTX_data_main(C), ntree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
@@ -2488,7 +2420,7 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op)
/* Need the extra update here because the loop above does not check for valid links in the node
* group we're currently editing. */
- ntree->update |= NTREE_UPDATE_GROUP | NTREE_UPDATE_LINKS;
+ BKE_ntree_update_tag_interface(ntree);
/* Deactivate sockets. */
LISTBASE_FOREACH (bNodeSocket *, socket_iter, sockets) {
@@ -2497,10 +2429,7 @@ static int ntree_socket_change_type_exec(bContext *C, wmOperator *op)
/* Make the new socket active. */
iosock->flag |= SELECT;
- ntreeUpdateTree(main, ntree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, main, ntree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
@@ -2611,11 +2540,8 @@ static int ntree_socket_move_exec(bContext *C, wmOperator *op)
}
}
- ntree->update |= NTREE_UPDATE_GROUP;
- ntreeUpdateTree(CTX_data_main(C), ntree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ BKE_ntree_update_tag_interface(ntree);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
@@ -2846,7 +2772,7 @@ static int viewer_border_exec(bContext *C, wmOperator *op)
btree->flag |= NTREE_VIEWER_BORDER;
}
- snode_notify(*C, *snode);
+ ED_node_tree_propagate_change(C, bmain, btree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
}
else {
@@ -2886,7 +2812,7 @@ static int clear_viewer_border_exec(bContext *C, wmOperator *UNUSED(op))
bNodeTree *btree = snode->nodetree;
btree->flag &= ~NTREE_VIEWER_BORDER;
- snode_notify(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), btree);
WM_event_add_notifier(C, NC_NODE | ND_DISPLAY, nullptr);
return OPERATOR_FINISHED;
@@ -2931,7 +2857,7 @@ static int node_cryptomatte_add_socket_exec(bContext *C, wmOperator *UNUSED(op))
ntreeCompositCryptomatteAddSocket(ntree, node);
- snode_notify(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
return OPERATOR_FINISHED;
}
@@ -2977,7 +2903,7 @@ static int node_cryptomatte_remove_socket_exec(bContext *C, wmOperator *UNUSED(o
return OPERATOR_CANCELLED;
}
- snode_notify(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), ntree);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc
index 4890c3e39cf..6f96e08d749 100644
--- a/source/blender/editors/space_node/node_geometry_attribute_search.cc
+++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc
@@ -125,8 +125,8 @@ void node_geometry_add_attribute_search_button(const bContext &UNUSED(C),
0.0f,
"");
- AttributeSearchData *data = OBJECT_GUARDED_NEW(
- AttributeSearchData, {&node_tree, &node, (bNodeSocket *)socket_ptr.data});
+ AttributeSearchData *data = MEM_new<AttributeSearchData>(
+ __func__, AttributeSearchData{&node_tree, &node, (bNodeSocket *)socket_ptr.data});
UI_but_func_search_set_results_are_suggestions(but, true);
UI_but_func_search_set_sep_string(but, UI_MENU_ARROW_SEP);
diff --git a/source/blender/editors/space_node/node_group.cc b/source/blender/editors/space_node/node_group.cc
index 704ffe1e478..290ed172a48 100644
--- a/source/blender/editors/space_node/node_group.cc
+++ b/source/blender/editors/space_node/node_group.cc
@@ -40,6 +40,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_node_tree_update.h"
#include "BKE_report.h"
#include "DEG_depsgraph_build.h"
@@ -258,6 +259,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
/* migrate node */
BLI_remlink(&wgroup->nodes, node);
BLI_addtail(&ntree->nodes, node);
+ BKE_ntree_update_tag_node_new(ntree, node);
/* ensure unique node name in the node tree */
nodeUniqueName(ntree, node);
@@ -284,6 +286,7 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
LISTBASE_FOREACH_MUTABLE (bNodeLink *, link, &wgroup->links) {
BLI_remlink(&wgroup->links, link);
BLI_addtail(&ntree->links, link);
+ BKE_ntree_update_tag_link_added(ntree, link);
}
bNodeLink *glinks_last = (bNodeLink *)ntree->links.last;
@@ -393,8 +396,6 @@ static int node_group_ungroup(Main *bmain, bNodeTree *ntree, bNode *gnode)
/* delete the group instance and dereference group tree */
nodeRemoveNode(bmain, ntree, gnode, true);
- ntree->update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
-
return 1;
}
@@ -412,16 +413,13 @@ static int node_group_ungroup_exec(bContext *C, wmOperator *op)
}
if (gnode->id && node_group_ungroup(bmain, snode->edittree, gnode)) {
- ntreeUpdateTree(bmain, snode->nodetree);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr);
}
else {
BKE_report(op->reports, RPT_WARNING, "Cannot ungroup");
return OPERATOR_CANCELLED;
}
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
-
return OPERATOR_FINISHED;
}
@@ -558,9 +556,9 @@ static bool node_group_separate_selected(
}
}
- ntree.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
+ BKE_ntree_update_tag_all(&ntree);
if (!make_copy) {
- ngroup.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
+ BKE_ntree_update_tag_all(&ngroup);
}
return true;
@@ -614,10 +612,7 @@ static int node_group_separate_exec(bContext *C, wmOperator *op)
/* switch to parent tree */
ED_node_tree_pop(snode);
- ntreeUpdateTree(CTX_data_main(C), snode->nodetree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), nullptr);
return OPERATOR_FINISHED;
}
@@ -812,6 +807,8 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
/* change node-collection membership */
BLI_remlink(&ntree.nodes, node);
BLI_addtail(&ngroup->nodes, node);
+ BKE_ntree_update_tag_node_removed(&ntree);
+ BKE_ntree_update_tag_node_new(ngroup, node);
/* ensure unique node name in the ngroup */
nodeUniqueName(ngroup, node);
@@ -983,11 +980,6 @@ static void node_group_make_insert_selected(const bContext &C, bNodeTree &ntree,
}
}
}
-
- /* update of the group tree */
- ngroup->update |= NTREE_UPDATE | NTREE_UPDATE_LINKS;
- /* update of the tree containing the group instance node */
- ntree.update |= NTREE_UPDATE_NODES | NTREE_UPDATE_LINKS;
}
static bNode *node_group_make_from_selected(const bContext &C,
@@ -1016,9 +1008,6 @@ static bNode *node_group_make_from_selected(const bContext &C,
node_group_make_insert_selected(C, ntree, gnode);
- /* update of the tree containing the group instance node */
- ntree.update |= NTREE_UPDATE_NODES;
-
return gnode;
}
@@ -1047,14 +1036,10 @@ static int node_group_make_exec(bContext *C, wmOperator *op)
LISTBASE_FOREACH (bNode *, node, &ngroup->nodes) {
sort_multi_input_socket_links(snode, *node, nullptr, nullptr);
}
- ntreeUpdateTree(bmain, ngroup);
}
}
- ntreeUpdateTree(bmain, &ntree);
-
- snode_notify(*C, snode);
- snode_dag_update(*C, snode);
+ ED_node_tree_propagate_change(C, bmain, nullptr);
/* We broke relations in node tree, need to rebuild them in the graphs. */
DEG_relations_tag_update(bmain);
@@ -1107,12 +1092,7 @@ static int node_group_insert_exec(bContext *C, wmOperator *op)
nodeSetActive(ntree, gnode);
ED_node_tree_push(snode, ngroup, gnode);
- ntreeUpdateTree(bmain, ngroup);
-
- ntreeUpdateTree(bmain, ntree);
-
- snode_notify(*C, *snode);
- snode_dag_update(*C, *snode);
+ ED_node_tree_propagate_change(C, bmain, nullptr);
return OPERATOR_FINISHED;
}
diff --git a/source/blender/editors/space_node/node_intern.hh b/source/blender/editors/space_node/node_intern.hh
index 2e55bb0cb28..52dde965114 100644
--- a/source/blender/editors/space_node/node_intern.hh
+++ b/source/blender/editors/space_node/node_intern.hh
@@ -235,7 +235,6 @@ void sort_multi_input_socket_links(SpaceNode &snode,
bNode &node,
bNodeLink *drag_link,
const blender::float2 *cursor);
-bool node_connected_to_output(Main &bmain, bNodeTree &ntree, bNode &node);
void NODE_OT_link(wmOperatorType *ot);
void NODE_OT_link_make(wmOperatorType *ot);
@@ -252,12 +251,8 @@ void NODE_OT_link_viewer(wmOperatorType *ot);
void NODE_OT_insert_offset(wmOperatorType *ot);
-void snode_notify(bContext &C, SpaceNode &snode);
-void snode_dag_update(bContext &C, SpaceNode &snode);
void snode_set_context(const bContext &C);
-void snode_update(SpaceNode &snode, bNode *node);
-/** Operator poll callback. */
bool composite_node_active(bContext *C);
/** Operator poll callback. */
bool composite_node_editable(bContext *C);
diff --git a/source/blender/editors/space_node/node_relationships.cc b/source/blender/editors/space_node/node_relationships.cc
index 90b53258d5e..c441cf14683 100644
--- a/source/blender/editors/space_node/node_relationships.cc
+++ b/source/blender/editors/space_node/node_relationships.cc
@@ -34,6 +34,7 @@
#include "BKE_lib_id.h"
#include "BKE_main.h"
#include "BKE_node.h"
+#include "BKE_node_tree_update.h"
#include "BKE_screen.h"
#include "ED_node.h" /* own include */
@@ -73,121 +74,6 @@ using blender::StringRefNull;
using blender::Vector;
/* -------------------------------------------------------------------- */
-/** \name Relations Helpers
- * \{ */
-
-static bool ntree_has_drivers(bNodeTree &ntree)
-{
- const AnimData *adt = BKE_animdata_from_id(&ntree.id);
- if (adt == nullptr) {
- return false;
- }
- return !BLI_listbase_is_empty(&adt->drivers);
-}
-
-static bool ntree_check_nodes_connected_dfs(bNodeTree &ntree, bNode &from, bNode &to)
-{
- if (from.flag & NODE_TEST) {
- return false;
- }
- from.flag |= NODE_TEST;
- LISTBASE_FOREACH (bNodeLink *, link, &ntree.links) {
- if (link->fromnode == &from) {
- if (link->tonode == &to) {
- return true;
- }
-
- if (ntree_check_nodes_connected_dfs(ntree, *link->tonode, to)) {
- return true;
- }
- }
- }
- return false;
-}
-
-static bool ntree_check_nodes_connected(bNodeTree &ntree, bNode &from, bNode &to)
-{
- if (&from == &to) {
- return true;
- }
- ntreeNodeFlagSet(&ntree, NODE_TEST, false);
- return ntree_check_nodes_connected_dfs(ntree, from, to);
-}
-
-static bool node_group_has_output_dfs(bNode &node)
-{
- bNodeTree *ntree = (bNodeTree *)node.id;
- if (ntree->id.tag & LIB_TAG_DOIT) {
- return false;
- }
- ntree->id.tag |= LIB_TAG_DOIT;
- LISTBASE_FOREACH (bNode *, current_node, &ntree->nodes) {
- if (current_node->type == NODE_GROUP) {
- if (current_node->id && node_group_has_output_dfs(*current_node)) {
- return true;
- }
- }
- if (current_node->flag & NODE_DO_OUTPUT && current_node->type != NODE_GROUP_OUTPUT) {
- return true;
- }
- }
- return false;
-}
-
-static bool node_group_has_output(Main &bmain, bNode &node)
-{
- BLI_assert(ELEM(node.type, NODE_GROUP, NODE_CUSTOM_GROUP));
- bNodeTree *ntree = (bNodeTree *)node.id;
- if (ntree == nullptr) {
- return false;
- }
- BKE_main_id_tag_listbase(&bmain.nodetrees, LIB_TAG_DOIT, false);
- return node_group_has_output_dfs(node);
-}
-
-bool node_connected_to_output(Main &bmain, bNodeTree &ntree, bNode &node)
-{
- /* Special case for drivers: if node tree has any drivers we assume it is
- * always to be tagged for update when node changes. Otherwise we will be
- * doomed to do some deep and nasty deep search of indirect dependencies,
- * which will be too complicated without real benefit.
- */
- if (ntree_has_drivers(ntree)) {
- return true;
- }
- LISTBASE_FOREACH (bNode *, current_node, &ntree.nodes) {
- /* Special case for group nodes -- if modified node connected to a group
- * with active output inside we consider refresh is needed.
- *
- * We could make check more grained here by taking which socket the node
- * is connected to and so eventually.
- */
- if (ELEM(current_node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
- if (current_node->id != nullptr && ntree_has_drivers((bNodeTree &)current_node->id)) {
- return true;
- }
- if (ntree_check_nodes_connected(ntree, node, *current_node) &&
- node_group_has_output(bmain, *current_node)) {
- return true;
- }
- }
- if (current_node->flag & NODE_DO_OUTPUT) {
- if (ntree_check_nodes_connected(ntree, node, *current_node)) {
- return true;
- }
- }
- if (current_node->type == GEO_NODE_VIEWER) {
- if (ntree_check_nodes_connected(ntree, node, *current_node)) {
- return true;
- }
- }
- }
- return false;
-}
-
-/** \} */
-
-/* -------------------------------------------------------------------- */
/** \name Add Node
* \{ */
@@ -214,7 +100,7 @@ static void clear_picking_highlight(ListBase *links)
}
}
-static bNodeLink *create_drag_link(Main &bmain, SpaceNode &snode, bNode &node, bNodeSocket &sock)
+static bNodeLink *create_drag_link(bNode &node, bNodeSocket &sock)
{
bNodeLink *oplink = (bNodeLink *)MEM_callocN(sizeof(bNodeLink), __func__);
if (sock.in_out == SOCK_OUT) {
@@ -226,27 +112,17 @@ static bNodeLink *create_drag_link(Main &bmain, SpaceNode &snode, bNode &node, b
oplink->tosock = &sock;
}
oplink->flag |= NODE_LINK_VALID;
- oplink->flag &= ~NODE_LINK_TEST;
- if (node_connected_to_output(bmain, *snode.edittree, node)) {
- oplink->flag |= NODE_LINK_TEST;
- }
oplink->flag |= NODE_LINK_DRAGGED;
return oplink;
}
-static void pick_link(const bContext &C,
- wmOperator &op,
- bNodeLinkDrag &nldrag,
- SpaceNode &snode,
- bNode *node,
- bNodeLink &link_to_pick)
+static void pick_link(
+ wmOperator &op, bNodeLinkDrag &nldrag, SpaceNode &snode, bNode *node, bNodeLink &link_to_pick)
{
clear_picking_highlight(&snode.edittree->links);
RNA_boolean_set(op.ptr, "has_link_picked", true);
- Main *bmain = CTX_data_main(&C);
- bNodeLink *link = create_drag_link(
- *bmain, snode, *link_to_pick.fromnode, *link_to_pick.fromsock);
+ bNodeLink *link = create_drag_link(*link_to_pick.fromnode, *link_to_pick.fromsock);
nldrag.links.append(link);
nodeRemLink(snode.edittree, &link_to_pick);
@@ -258,7 +134,7 @@ static void pick_link(const bContext &C,
/* Send changed event to original link->tonode. */
if (node) {
- snode_update(snode, node);
+ BKE_ntree_update_tag_node_property(snode.edittree, node);
}
}
@@ -324,7 +200,7 @@ static void pick_input_link_by_link_intersect(const bContext &C,
ED_area_tag_redraw(CTX_wm_area(&C));
if (!node_find_indicated_socket(*snode, &node, &socket, cursor, SOCK_IN)) {
- pick_link(C, op, nldrag, *snode, node, *link_to_pick);
+ pick_link(op, nldrag, *snode, node, *link_to_pick);
}
}
}
@@ -566,7 +442,7 @@ static void snode_autoconnect(Main &bmain,
}
if (numlinks > 0) {
- ntreeUpdateTree(&bmain, ntree);
+ BKE_ntree_update_main_tree(&bmain, ntree, nullptr);
}
}
@@ -640,7 +516,7 @@ static bNodeSocket *node_link_viewer_get_socket(bNodeTree &ntree,
(eNodeSocketDatatype)src_socket.type);
BLI_assert(data_type != CD_AUTO_FROM_NAME);
storage->data_type = data_type;
- nodeUpdate(&ntree, &viewer_node);
+ viewer_node.typeinfo->updatefunc(&ntree, &viewer_node);
return viewer_socket;
}
}
@@ -810,7 +686,7 @@ static int link_socket_to_viewer(const bContext &C,
else {
link_to_change->fromnode = &bnode_to_view;
link_to_change->fromsock = &bsocket_to_view;
- btree.update |= NTREE_UPDATE_LINKS;
+ BKE_ntree_update_tag_link_changed(&btree);
}
remove_links_to_unavailable_viewer_sockets(btree, *viewer_bnode);
@@ -819,10 +695,7 @@ static int link_socket_to_viewer(const bContext &C,
ED_spreadsheet_context_paths_set_geometry_node(CTX_data_main(&C), &snode, viewer_bnode);
}
- ntreeUpdateTree(CTX_data_main(&C), &btree);
- snode_update(snode, viewer_bnode);
- DEG_id_tag_update(&btree.id, 0);
-
+ BKE_ntree_update_main_tree(CTX_data_main(&C), &btree, nullptr);
return OPERATOR_FINISHED;
}
@@ -863,7 +736,7 @@ static int node_active_link_viewer_exec(bContext *C, wmOperator *UNUSED(op))
return OPERATOR_CANCELLED;
}
- snode_notify(*C, snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
return OPERATOR_FINISHED;
}
@@ -1041,17 +914,10 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links)
SpaceNode &snode = *CTX_wm_space_node(&C);
bNodeTree &ntree = *snode.edittree;
bNodeLinkDrag *nldrag = (bNodeLinkDrag *)op.customdata;
- bool do_tag_update = false;
/* avoid updates while applying links */
ntree.is_updating = true;
for (bNodeLink *link : nldrag->links) {
- /* See note below, but basically TEST flag means that the link
- * was connected to output (or to a node which affects the
- * output).
- */
- do_tag_update |= (link->flag & NODE_LINK_TEST) != 0;
-
link->flag &= ~NODE_LINK_DRAGGED;
if (apply_links && link->tosock && link->fromsock) {
@@ -1067,18 +933,10 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links)
/* add link to the node tree */
BLI_addtail(&ntree.links, link);
-
- ntree.update |= NTREE_UPDATE_LINKS;
-
- /* tag tonode for update */
- link->tonode->update |= NODE_UPDATE;
+ BKE_ntree_update_tag_link_added(&ntree, link);
/* we might need to remove a link */
node_remove_extra_links(snode, *link);
-
- if (link->tonode) {
- do_tag_update |= (do_tag_update || node_connected_to_output(*bmain, ntree, *link->tonode));
- }
}
else {
nodeRemLink(&ntree, link);
@@ -1086,11 +944,7 @@ static void node_link_exit(bContext &C, wmOperator &op, const bool apply_links)
}
ntree.is_updating = false;
- ntreeUpdateTree(bmain, &ntree);
- snode_notify(C, snode);
- if (do_tag_update) {
- snode_dag_update(C, snode);
- }
+ ED_node_tree_propagate_change(&C, bmain, &ntree);
/* Ensure draglink tooltip is disabled. */
draw_draglink_tooltip_deactivate(*CTX_wm_region(&C), *nldrag);
@@ -1250,8 +1104,7 @@ static int node_link_modal(bContext *C, wmOperator *op, const wmEvent *event)
return OPERATOR_RUNNING_MODAL;
}
-static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain,
- SpaceNode &snode,
+static std::unique_ptr<bNodeLinkDrag> node_link_init(SpaceNode &snode,
float2 cursor,
const bool detach)
{
@@ -1276,17 +1129,6 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain,
oplink->flag |= NODE_LINK_VALID;
oplink->flag |= NODE_LINK_DRAGGED;
- /* The link could be disconnected and in that case we
- * wouldn't be able to check whether tag update is
- * needed or not when releasing mouse button. So we
- * cache whether the link affects output or not here
- * using TEST flag.
- */
- oplink->flag &= ~NODE_LINK_TEST;
- if (node_connected_to_output(bmain, *snode.edittree, *link->tonode)) {
- oplink->flag |= NODE_LINK_TEST;
- }
-
nldrag->links.append(oplink);
nodeRemLink(snode.edittree, link);
}
@@ -1296,7 +1138,7 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain,
/* dragged links are fixed on output side */
nldrag->in_out = SOCK_OUT;
/* create a new link */
- nldrag->links.append(create_drag_link(bmain, snode, *node, *sock));
+ nldrag->links.append(create_drag_link(*node, *sock));
}
return nldrag;
}
@@ -1329,17 +1171,13 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain,
oplink->next = oplink->prev = nullptr;
oplink->flag |= NODE_LINK_VALID;
oplink->flag |= NODE_LINK_DRAGGED;
- oplink->flag &= ~NODE_LINK_TEST;
- if (node_connected_to_output(bmain, *snode.edittree, *link_to_pick->tonode)) {
- oplink->flag |= NODE_LINK_TEST;
- }
nldrag->links.append(oplink);
nodeRemLink(snode.edittree, link_to_pick);
/* send changed event to original link->tonode */
if (node) {
- snode_update(snode, node);
+ BKE_ntree_update_tag_node_property(snode.edittree, node);
}
}
}
@@ -1347,7 +1185,7 @@ static std::unique_ptr<bNodeLinkDrag> node_link_init(Main &bmain,
/* dragged links are fixed on input side */
nldrag->in_out = SOCK_IN;
/* create a new link */
- nldrag->links.append(create_drag_link(bmain, snode, *node, *sock));
+ nldrag->links.append(create_drag_link(*node, *sock));
}
return nldrag;
}
@@ -1370,7 +1208,7 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event)
ED_preview_kill_jobs(CTX_wm_manager(C), &bmain);
- std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(bmain, snode, cursor, detach);
+ std::unique_ptr<bNodeLinkDrag> nldrag = node_link_init(snode, cursor, detach);
if (nldrag) {
UI_view2d_edge_pan_operator_init(C, &nldrag->pan_data, op);
@@ -1473,9 +1311,7 @@ static int node_make_link_exec(bContext *C, wmOperator *op)
node_deselect_all_input_sockets(snode, false);
node_deselect_all_output_sockets(snode, false);
- ntreeUpdateTree(CTX_data_main(C), snode.edittree);
- snode_notify(*C, snode);
- snode_dag_update(*C, snode);
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
return OPERATOR_FINISHED;
}
@@ -1532,7 +1368,6 @@ static int cut_links_exec(bContext *C, wmOperator *op)
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
ARegion &region = *CTX_wm_region(C);
- bool do_tag_update = false;
int i = 0;
float mcoords[256][2];
@@ -1567,23 +1402,14 @@ static int cut_links_exec(bContext *C, wmOperator *op)
found = true;
}
- do_tag_update |= (do_tag_update ||
- node_connected_to_output(bmain, *snode.edittree, *link->tonode));
-
- snode_update(snode, link->tonode);
bNode *to_node = link->tonode;
nodeRemLink(snode.edittree, link);
sort_multi_input_socket_links(snode, *to_node, nullptr, nullptr);
}
}
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
if (found) {
- ntreeUpdateTree(CTX_data_main(C), snode.edittree);
- snode_notify(*C, snode);
- if (do_tag_update) {
- snode_dag_update(*C, snode);
- }
-
return OPERATOR_FINISHED;
}
@@ -1629,7 +1455,6 @@ static int mute_links_exec(bContext *C, wmOperator *op)
Main &bmain = *CTX_data_main(C);
SpaceNode &snode = *CTX_wm_space_node(C);
ARegion &region = *CTX_wm_region(C);
- bool do_tag_update = false;
int i = 0;
float mcoords[256][2];
@@ -1671,10 +1496,6 @@ static int mute_links_exec(bContext *C, wmOperator *op)
}
if (node_links_intersect(*link, mcoords, i)) {
- do_tag_update |= (do_tag_update ||
- node_connected_to_output(bmain, *snode.edittree, *link->tonode));
-
- snode_update(snode, link->tonode);
nodeMuteLinkToggle(snode.edittree, link);
}
}
@@ -1687,12 +1508,7 @@ static int mute_links_exec(bContext *C, wmOperator *op)
link->flag &= ~NODE_LINK_TEST;
}
- ntreeUpdateTree(CTX_data_main(C), snode.edittree);
- snode_notify(*C, snode);
- if (do_tag_update) {
- snode_dag_update(*C, snode);
- }
-
+ ED_node_tree_propagate_change(C, CTX_data_main(C), snode.edittree);
return OPERATOR_FINISHED;
}
@@ -1743,11 +1559,7 @@ static int detach_links_exec(bContext *C, wmOperator *UNUSED(op))
}
}
- ntreeUpdateTree(CTX_data_main(C), &ntree);
-
- snode_notify(*C, snode);
- snode_dag_update(*C, snode);
-
+ ED_node_tree_propagate_change(C, CTX_data_main(C), &ntree);
return OPERATOR_FINISHED;
}
@@ -2663,10 +2475,7 @@ void ED_node_link_insert(Main *bmain, ScrArea *area)
snode->runtime->iofsd = iofsd;
}
- ntreeUpdateTree(bmain, snode->edittree); /* needed for pointers */
- snode_update(*snode, select);
- ED_node_tag_update_id((ID *)snode->edittree);
- ED_node_tag_update_id(snode->id);
+ ED_node_tree_propagate_change(nullptr, bmain, snode->edittree);
}
}
}
diff --git a/source/blender/editors/space_node/node_templates.cc b/source/blender/editors/space_node/node_templates.cc
index 386178596af..defb1e82c3e 100644
--- a/source/blender/editors/space_node/node_templates.cc
+++ b/source/blender/editors/space_node/node_templates.cc
@@ -37,6 +37,7 @@
#include "BKE_context.h"
#include "BKE_lib_id.h"
#include "BKE_main.h"
+#include "BKE_node_tree_update.h"
#include "RNA_access.h"
@@ -84,7 +85,7 @@ static void node_link_item_apply(Main *bmain, bNode *node, NodeLinkItem *item)
{
if (ELEM(node->type, NODE_GROUP, NODE_CUSTOM_GROUP)) {
node->id = (ID *)item->ngroup;
- ntreeUpdateTree(bmain, item->ngroup);
+ BKE_ntree_update_main_tree(bmain, item->ngroup, nullptr);
}
else {
/* nothing to do for now */
@@ -179,10 +180,8 @@ static void node_socket_disconnect(Main *bmain,
nodeRemLink(ntree, sock_to->link);
sock_to->flag |= SOCK_COLLAPSED;
- nodeUpdate(ntree, node_to);
- ntreeUpdateTree(bmain, ntree);
-
- ED_node_tag_update_nodetree(bmain, ntree, node_to);
+ BKE_ntree_update_tag_node_property(ntree, node_to);
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
/* remove all nodes connected to this socket, if they aren't connected to other nodes */
@@ -195,10 +194,8 @@ static void node_socket_remove(Main *bmain, bNodeTree *ntree, bNode *node_to, bN
node_remove_linked(bmain, ntree, sock_to->link->fromnode);
sock_to->flag |= SOCK_COLLAPSED;
- nodeUpdate(ntree, node_to);
- ntreeUpdateTree(bmain, ntree);
-
- ED_node_tag_update_nodetree(bmain, ntree, node_to);
+ BKE_ntree_update_tag_node_property(ntree, node_to);
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
/* add new node connected to this socket, or replace an existing one */
@@ -299,11 +296,9 @@ static void node_socket_add_replace(const bContext *C,
node_remove_linked(bmain, ntree, node_prev);
}
- nodeUpdate(ntree, node_from);
- nodeUpdate(ntree, node_to);
- ntreeUpdateTree(CTX_data_main(C), ntree);
-
- ED_node_tag_update_nodetree(CTX_data_main(C), ntree, node_to);
+ BKE_ntree_update_tag_node_property(ntree, node_from);
+ BKE_ntree_update_tag_node_property(ntree, node_to);
+ ED_node_tree_propagate_change(nullptr, bmain, ntree);
}
/****************************** Node Link Menu *******************************/
diff --git a/source/blender/editors/space_node/space_node.cc b/source/blender/editors/space_node/space_node.cc
index 4cc0bed1928..96c6dd89447 100644
--- a/source/blender/editors/space_node/space_node.cc
+++ b/source/blender/editors/space_node/space_node.cc
@@ -313,6 +313,19 @@ static void node_init(struct wmWindowManager *UNUSED(wm), ScrArea *area)
}
}
+static bool any_node_uses_id(const bNodeTree *ntree, const ID *id)
+{
+ if (ELEM(nullptr, ntree, id)) {
+ return false;
+ }
+ LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
+ if (node->id == id) {
+ return true;
+ }
+ }
+ return false;
+}
+
static void node_area_listener(const wmSpaceTypeListenerParams *params)
{
ScrArea *area = params->area;
@@ -436,10 +449,9 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
case NC_IMAGE:
if (wmn->action == NA_EDITED) {
if (ED_node_is_compositor(snode)) {
- /* note that nodeUpdateID is already called by BKE_image_signal() on all
- * scenes so really this is just to know if the images is used in the compo else
- * painting on images could become very slow when the compositor is open. */
- if (nodeUpdateID(snode->nodetree, (ID *)wmn->reference)) {
+ /* Without this check drawing on an image could become very slow when the compositor is
+ * open. */
+ if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) {
ED_area_tag_refresh(area);
}
}
@@ -449,7 +461,7 @@ static void node_area_listener(const wmSpaceTypeListenerParams *params)
case NC_MOVIECLIP:
if (wmn->action == NA_EDITED) {
if (ED_node_is_compositor(snode)) {
- if (nodeUpdateID(snode->nodetree, (ID *)wmn->reference)) {
+ if (any_node_uses_id(snode->nodetree, (ID *)wmn->reference)) {
ED_area_tag_refresh(area);
}
}
diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c
index a4d5f2635d4..85fee590447 100644
--- a/source/blender/editors/space_outliner/outliner_dragdrop.c
+++ b/source/blender/editors/space_outliner/outliner_dragdrop.c
@@ -123,7 +123,7 @@ static ID *outliner_ID_drop_find(bContext *C, const wmEvent *event, short idcode
/* Find tree element to drop into, with additional before and after reorder support. */
static TreeElement *outliner_drop_insert_find(bContext *C,
- const wmEvent *event,
+ const int xy[2],
TreeElementInsertType *r_insert_type)
{
SpaceOutliner *space_outliner = CTX_wm_space_outliner(C);
@@ -136,8 +136,11 @@ static TreeElement *outliner_drop_insert_find(bContext *C,
return NULL;
}
- UI_view2d_region_to_view(
- &region->v2d, event->mval[0], event->mval[1], &view_mval[0], &view_mval[1]);
+ int mval[2];
+ mval[0] = xy[0] - region->winrct.xmin;
+ mval[1] = xy[1] - region->winrct.ymin;
+
+ UI_view2d_region_to_view(&region->v2d, mval[0], mval[1], &view_mval[0], &view_mval[1]);
te_hovered = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]);
if (te_hovered) {
@@ -216,10 +219,10 @@ static bool is_pchan_element(TreeElement *te)
}
static TreeElement *outliner_drop_insert_collection_find(bContext *C,
- const wmEvent *event,
+ const int xy[2],
TreeElementInsertType *r_insert_type)
{
- TreeElement *te = outliner_drop_insert_find(C, event, r_insert_type);
+ TreeElement *te = outliner_drop_insert_find(C, xy, r_insert_type);
if (!te) {
return NULL;
}
@@ -707,7 +710,7 @@ static bool datastack_drop_init(bContext *C, const wmEvent *event, StackDropData
return false;
}
- TreeElement *te_target = outliner_drop_insert_find(C, event, &drop_data->insert_type);
+ TreeElement *te_target = outliner_drop_insert_find(C, event->xy, &drop_data->insert_type);
if (!te_target) {
return false;
}
@@ -1088,14 +1091,12 @@ static Collection *collection_parent_from_ID(ID *id)
return NULL;
}
-static bool collection_drop_init(bContext *C,
- wmDrag *drag,
- const wmEvent *event,
- CollectionDrop *data)
+static bool collection_drop_init(
+ bContext *C, wmDrag *drag, const int xy[2], const bool is_link, CollectionDrop *data)
{
/* Get collection to drop into. */
TreeElementInsertType insert_type;
- TreeElement *te = outliner_drop_insert_collection_find(C, event, &insert_type);
+ TreeElement *te = outliner_drop_insert_collection_find(C, xy, &insert_type);
if (!te) {
return false;
}
@@ -1123,7 +1124,7 @@ static bool collection_drop_init(bContext *C,
/* Get collection to drag out of. */
ID *parent = drag_id->from_parent;
Collection *from_collection = collection_parent_from_ID(parent);
- if (event->ctrl) {
+ if (is_link) {
from_collection = NULL;
}
@@ -1164,7 +1165,7 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
&space_outliner->tree, TSE_HIGHLIGHTED_ANY | TSE_DRAG_ANY, false);
CollectionDrop data;
- if (!event->shift && collection_drop_init(C, drag, event, &data)) {
+ if (!event->shift && collection_drop_init(C, drag, event->xy, event->ctrl, &data)) {
TreeElement *te = data.te;
TreeStoreElem *tselem = TREESTORE(te);
if (!data.from || event->ctrl) {
@@ -1201,13 +1202,14 @@ static bool collection_drop_poll(bContext *C, wmDrag *drag, const wmEvent *event
static char *collection_drop_tooltip(bContext *C,
wmDrag *drag,
- const int UNUSED(xy[2]),
+ const int xy[2],
wmDropBox *UNUSED(drop))
{
- wmWindowManager *wm = CTX_wm_manager(C);
- const wmEvent *event = wm->winactive ? wm->winactive->eventstate : NULL;
+ wmWindow *win = CTX_wm_window(C);
+ const wmEvent *event = win ? win->eventstate : NULL;
+
CollectionDrop data;
- if (event && !event->shift && collection_drop_init(C, drag, event, &data)) {
+ if (event && !event->shift && collection_drop_init(C, drag, xy, event->ctrl, &data)) {
TreeElement *te = data.te;
if (!data.from || event->ctrl) {
return BLI_strdup(TIP_("Link inside Collection"));
@@ -1260,7 +1262,7 @@ static int collection_drop_invoke(bContext *C, wmOperator *UNUSED(op), const wmE
wmDrag *drag = lb->first;
CollectionDrop data;
- if (!collection_drop_init(C, drag, event, &data)) {
+ if (!collection_drop_init(C, drag, event->xy, event->ctrl, &data)) {
return OPERATOR_CANCELLED;
}
diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c
index 9d4b14a1f57..ba4759826cd 100644
--- a/source/blender/editors/space_outliner/outliner_edit.c
+++ b/source/blender/editors/space_outliner/outliner_edit.c
@@ -38,6 +38,7 @@
#include "BLT_translation.h"
+#include "BKE_action.h"
#include "BKE_animsys.h"
#include "BKE_appdir.h"
#include "BKE_armature.h"
@@ -48,6 +49,7 @@
#include "BKE_lib_query.h"
#include "BKE_lib_remap.h"
#include "BKE_main.h"
+#include "BKE_object.h"
#include "BKE_report.h"
#include "BKE_workspace.h"
@@ -654,7 +656,7 @@ static int outliner_id_remap_invoke(bContext *C, wmOperator *op, const wmEvent *
outliner_id_remap_find_tree_element(C, op, &space_outliner->tree, fmval[1]);
}
- return WM_operator_props_dialog_popup(C, op, 200);
+ return WM_operator_props_dialog_popup(C, op, 400);
}
static const EnumPropertyItem *outliner_id_itemf(bContext *C,
@@ -703,6 +705,9 @@ void OUTLINER_OT_id_remap(wmOperatorType *ot)
prop = RNA_def_enum(ot->srna, "id_type", rna_enum_id_type_items, ID_OB, "ID Type", "");
RNA_def_property_translation_context(prop, BLT_I18NCONTEXT_ID_ID);
+ /* Changing ID type wont make sense, would return early with "Invalid old/new ID pair" anyways.
+ */
+ RNA_def_property_flag(prop, PROP_HIDDEN);
prop = RNA_def_enum(ot->srna, "old_id", DummyRNA_NULL_items, 0, "Old ID", "Old ID to replace");
RNA_def_property_enum_funcs_runtime(prop, NULL, NULL, outliner_id_itemf);
@@ -1262,7 +1267,8 @@ static TreeElement *outliner_show_active_get_element(bContext *C,
TreeElement *te_obact = te;
if (obact->mode & OB_MODE_POSE) {
- bPoseChannel *pchan = CTX_data_active_pose_bone(C);
+ Object *obpose = BKE_object_pose_armature_get(obact);
+ bPoseChannel *pchan = BKE_pose_channel_active(obpose, false);
if (pchan) {
te = outliner_find_posechannel(&te_obact->subtree, pchan);
}
diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c
index 616953e720a..9f31e55439d 100644
--- a/source/blender/editors/space_sequencer/sequencer_add.c
+++ b/source/blender/editors/space_sequencer/sequencer_add.c
@@ -627,6 +627,19 @@ static void seq_build_proxy(bContext *C, Sequence *seq)
ED_area_tag_redraw(CTX_wm_area(C));
}
+static void sequencer_add_movie_clamp_sound_strip_length(Scene *scene,
+ ListBase *seqbase,
+ Sequence *seq_movie,
+ Sequence *seq_sound)
+{
+ if (ELEM(NULL, seq_movie, seq_sound) || seq_sound->len <= seq_movie->len) {
+ return;
+ }
+
+ SEQ_transform_set_right_handle_frame(seq_sound, SEQ_transform_get_right_handle_frame(seq_movie));
+ SEQ_time_update_sequence(scene, seqbase, seq_sound);
+}
+
static void sequencer_add_movie_multiple_strips(bContext *C,
wmOperator *op,
SeqLoadData *load_data)
@@ -689,6 +702,7 @@ static void sequencer_add_movie_multiple_strips(bContext *C,
else {
load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp;
}
+ sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound);
seq_load_apply_generic_options(C, op, seq_sound);
seq_load_apply_generic_options(C, op, seq_movie);
seq_build_proxy(C, seq_movie);
@@ -740,6 +754,7 @@ static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoa
load_data->start_frame += audio_frame_offset;
seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data, audio_skip);
}
+ sequencer_add_movie_clamp_sound_strip_length(scene, ed->seqbasep, seq_movie, seq_sound);
seq_load_apply_generic_options(C, op, seq_sound);
seq_load_apply_generic_options(C, op, seq_movie);
seq_build_proxy(C, seq_movie);
diff --git a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
index 61cc70830af..7526a61bf4a 100644
--- a/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
+++ b/source/blender/editors/space_spreadsheet/space_spreadsheet.cc
@@ -114,7 +114,7 @@ static void spreadsheet_free(SpaceLink *sl)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)sl;
- delete sspreadsheet->runtime;
+ MEM_delete(sspreadsheet->runtime);
LISTBASE_FOREACH_MUTABLE (SpreadsheetRowFilter *, row_filter, &sspreadsheet->row_filters) {
spreadsheet_row_filter_free(row_filter);
@@ -131,7 +131,7 @@ static void spreadsheet_init(wmWindowManager *UNUSED(wm), ScrArea *area)
{
SpaceSpreadsheet *sspreadsheet = (SpaceSpreadsheet *)area->spacedata.first;
if (sspreadsheet->runtime == nullptr) {
- sspreadsheet->runtime = new SpaceSpreadsheet_Runtime();
+ sspreadsheet->runtime = MEM_new<SpaceSpreadsheet_Runtime>(__func__);
}
}
@@ -140,10 +140,11 @@ static SpaceLink *spreadsheet_duplicate(SpaceLink *sl)
const SpaceSpreadsheet *sspreadsheet_old = (SpaceSpreadsheet *)sl;
SpaceSpreadsheet *sspreadsheet_new = (SpaceSpreadsheet *)MEM_dupallocN(sspreadsheet_old);
if (sspreadsheet_old->runtime) {
- sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime(*sspreadsheet_old->runtime);
+ sspreadsheet_new->runtime = MEM_new<SpaceSpreadsheet_Runtime>(__func__,
+ *sspreadsheet_old->runtime);
}
else {
- sspreadsheet_new->runtime = new SpaceSpreadsheet_Runtime();
+ sspreadsheet_new->runtime = MEM_new<SpaceSpreadsheet_Runtime>(__func__);
}
BLI_listbase_clear(&sspreadsheet_new->row_filters);
diff --git a/source/blender/editors/space_topbar/space_topbar.c b/source/blender/editors/space_topbar/space_topbar.c
index 41eddd32854..7f0f30624cb 100644
--- a/source/blender/editors/space_topbar/space_topbar.c
+++ b/source/blender/editors/space_topbar/space_topbar.c
@@ -35,6 +35,7 @@
#include "BKE_context.h"
#include "BKE_global.h"
#include "BKE_screen.h"
+#include "BKE_undo_system.h"
#include "ED_screen.h"
#include "ED_space_api.h"
@@ -236,6 +237,59 @@ static void recent_files_menu_register(void)
WM_menutype_add(mt);
}
+static void undo_history_draw_menu(const bContext *C, Menu *menu)
+{
+ wmWindowManager *wm = CTX_wm_manager(C);
+ if (wm->undo_stack == NULL) {
+ return;
+ }
+ int undo_step_count = 0;
+ for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next) {
+ if (us->skip) {
+ continue;
+ }
+ undo_step_count += 1;
+ }
+
+ uiLayout *split = uiLayoutSplit(menu->layout, 0.0f, false);
+ uiLayout *column = NULL;
+
+ const int col_size = 20 + (undo_step_count / 12);
+ int i = 0;
+
+ undo_step_count = 0;
+ for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next, i++) {
+ if (us->skip) {
+ continue;
+ }
+ if (!(undo_step_count % col_size)) {
+ column = uiLayoutColumn(split, false);
+ }
+ const bool is_active = (us == wm->undo_stack->step_active);
+ uiLayout *row = uiLayoutRow(column, false);
+ uiLayoutSetEnabled(row, !is_active);
+ uiItemIntO(row,
+ IFACE_(us->name),
+ is_active ? ICON_LAYER_ACTIVE : ICON_NONE,
+ "ED_OT_undo_history",
+ "item",
+ i);
+ undo_step_count += 1;
+ }
+}
+
+static void undo_history_menu_register(void)
+{
+ MenuType *mt;
+
+ mt = MEM_callocN(sizeof(MenuType), __func__);
+ strcpy(mt->idname, "TOPBAR_MT_undo_history");
+ strcpy(mt->label, N_("Undo History"));
+ strcpy(mt->translation_context, BLT_I18NCONTEXT_DEFAULT_BPYRNA);
+ mt->draw = undo_history_draw_menu;
+ WM_menutype_add(mt);
+}
+
void ED_spacetype_topbar(void)
{
SpaceType *st = MEM_callocN(sizeof(SpaceType), "spacetype topbar");
@@ -278,6 +332,7 @@ void ED_spacetype_topbar(void)
BLI_addhead(&st->regiontypes, art);
recent_files_menu_register();
+ undo_history_menu_register();
BKE_spacetype_register(st);
}
diff --git a/source/blender/editors/space_view3d/view3d_buttons.c b/source/blender/editors/space_view3d/view3d_buttons.c
index b79303551a1..5451aa5a2e0 100644
--- a/source/blender/editors/space_view3d/view3d_buttons.c
+++ b/source/blender/editors/space_view3d/view3d_buttons.c
@@ -1552,7 +1552,7 @@ static void v3d_posearmature_buts(uiLayout *layout, Object *ob)
PointerRNA pchanptr;
uiLayout *col;
- pchan = BKE_pose_channel_active(ob);
+ pchan = BKE_pose_channel_active_if_layer_visible(ob);
if (!pchan) {
uiItemL(layout, IFACE_("No Bone Active"), ICON_NONE);
diff --git a/source/blender/editors/space_view3d/view3d_edit.c b/source/blender/editors/space_view3d/view3d_edit.c
index 830f7cbeff1..80089815284 100644
--- a/source/blender/editors/space_view3d/view3d_edit.c
+++ b/source/blender/editors/space_view3d/view3d_edit.c
@@ -3216,7 +3216,7 @@ static int view_lock_to_active_exec(bContext *C, wmOperator *UNUSED(op))
if (obact->mode & OB_MODE_POSE) {
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
Object *obact_eval = DEG_get_evaluated_object(depsgraph, obact);
- bPoseChannel *pcham_act = BKE_pose_channel_active(obact_eval);
+ bPoseChannel *pcham_act = BKE_pose_channel_active_if_layer_visible(obact_eval);
if (pcham_act) {
BLI_strncpy(v3d->ob_center_bone, pcham_act->name, sizeof(v3d->ob_center_bone));
}
diff --git a/source/blender/editors/space_view3d/view3d_gizmo_armature.c b/source/blender/editors/space_view3d/view3d_gizmo_armature.c
index 83d3286c8b3..31ae8a92f81 100644
--- a/source/blender/editors/space_view3d/view3d_gizmo_armature.c
+++ b/source/blender/editors/space_view3d/view3d_gizmo_armature.c
@@ -134,7 +134,7 @@ static bool WIDGETGROUP_armature_spline_poll(const bContext *C, wmGizmoGroupType
if (ob) {
const bArmature *arm = ob->data;
if (arm->drawtype == ARM_B_BONE) {
- bPoseChannel *pchan = BKE_pose_channel_active(ob);
+ bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
if (pchan && pchan->bone->segments > 1) {
return true;
}
@@ -148,7 +148,7 @@ static void WIDGETGROUP_armature_spline_setup(const bContext *C, wmGizmoGroup *g
{
ViewLayer *view_layer = CTX_data_view_layer(C);
Object *ob = BKE_object_pose_armature_get(OBACT(view_layer));
- bPoseChannel *pchan = BKE_pose_channel_active(ob);
+ bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
const wmGizmoType *gzt_move = WM_gizmotype_find("GIZMO_GT_move_3d", true);
@@ -187,7 +187,7 @@ static void WIDGETGROUP_armature_spline_refresh(const bContext *C, wmGizmoGroup
}
struct BoneSplineWidgetGroup *bspline_group = gzgroup->customdata;
- bPoseChannel *pchan = BKE_pose_channel_active(ob);
+ bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
/* Handles */
for (int i = 0; i < ARRAY_SIZE(bspline_group->handles); i++) {
diff --git a/source/blender/editors/transform/transform_convert.c b/source/blender/editors/transform/transform_convert.c
index 8f3d13176a3..4107cc3a71c 100644
--- a/source/blender/editors/transform/transform_convert.c
+++ b/source/blender/editors/transform/transform_convert.c
@@ -914,6 +914,9 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
case TC_SEQ_DATA:
special_aftertrans_update__sequencer(C, t);
break;
+ case TC_SEQ_IMAGE_DATA:
+ special_aftertrans_update__sequencer_image(C, t);
+ break;
case TC_TRACKING_DATA:
special_aftertrans_update__movieclip(C, t);
break;
@@ -930,7 +933,6 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
case TC_OBJECT_TEXSPACE:
case TC_PAINT_CURVE_VERTS:
case TC_PARTICLE_VERTS:
- case TC_SEQ_IMAGE_DATA:
case TC_NONE:
default:
break;
diff --git a/source/blender/editors/transform/transform_convert.h b/source/blender/editors/transform/transform_convert.h
index 5ed8182857d..12f3b39927e 100644
--- a/source/blender/editors/transform/transform_convert.h
+++ b/source/blender/editors/transform/transform_convert.h
@@ -304,6 +304,7 @@ void special_aftertrans_update__sequencer(bContext *C, TransInfo *t);
/* transform_convert_sequencer_image.c */
void createTransSeqImageData(TransInfo *t);
void recalcData_sequencer_image(TransInfo *t);
+void special_aftertrans_update__sequencer_image(bContext *C, TransInfo *t);
/* transform_convert_tracking.c */
void createTransTrackingData(bContext *C, TransInfo *t);
diff --git a/source/blender/editors/transform/transform_convert_armature.c b/source/blender/editors/transform/transform_convert_armature.c
index 63aada0f797..5d0a3bd9dd1 100644
--- a/source/blender/editors/transform/transform_convert_armature.c
+++ b/source/blender/editors/transform/transform_convert_armature.c
@@ -427,7 +427,7 @@ static short pose_grab_with_ik(Main *bmain, Object *ob)
/* Rule: allow multiple Bones
* (but they must be selected, and only one ik-solver per chain should get added) */
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
- if (pchan->bone->layer & arm->layer) {
+ if (BKE_pose_is_layer_visible(arm, pchan)) {
if (pchan->bone->flag & (BONE_SELECTED | BONE_TRANSFORM_MIRROR)) {
/* Rule: no IK for solitary (unconnected) bones. */
for (bonec = pchan->bone->childbase.first; bonec; bonec = bonec->next) {
diff --git a/source/blender/editors/transform/transform_convert_mask.c b/source/blender/editors/transform/transform_convert_mask.c
index 1a25cfd1efb..e26172cd764 100644
--- a/source/blender/editors/transform/transform_convert_mask.c
+++ b/source/blender/editors/transform/transform_convert_mask.c
@@ -472,12 +472,7 @@ void special_aftertrans_update__mask(bContext *C, TransInfo *t)
}
if (t->scene->nodetree) {
- /* tracks can be used for stabilization nodes,
- * flush update for such nodes */
- // if (nodeUpdateID(t->scene->nodetree, &mask->id))
- {
- WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id);
- }
+ WM_event_add_notifier(C, NC_MASK | ND_DATA, &mask->id);
}
/* TODO: don't key all masks. */
diff --git a/source/blender/editors/transform/transform_convert_node.c b/source/blender/editors/transform/transform_convert_node.c
index da11666d445..e8cdfaf1f40 100644
--- a/source/blender/editors/transform/transform_convert_node.c
+++ b/source/blender/editors/transform/transform_convert_node.c
@@ -31,6 +31,7 @@
#include "BKE_context.h"
#include "BKE_node.h"
+#include "BKE_node_tree_update.h"
#include "BKE_report.h"
#include "ED_node.h"
@@ -246,7 +247,7 @@ void special_aftertrans_update__node(bContext *C, TransInfo *t)
nodeRemoveNode(bmain, ntree, node, true);
}
}
- ntreeUpdateTree(bmain, ntree);
+ ED_node_tree_propagate_change(C, bmain, ntree);
}
}
diff --git a/source/blender/editors/transform/transform_convert_sequencer_image.c b/source/blender/editors/transform/transform_convert_sequencer_image.c
index d5a59885014..3a5770c2863 100644
--- a/source/blender/editors/transform/transform_convert_sequencer_image.c
+++ b/source/blender/editors/transform/transform_convert_sequencer_image.c
@@ -38,8 +38,12 @@
#include "SEQ_transform.h"
#include "SEQ_utils.h"
+#include "ED_keyframing.h"
+
#include "UI_view2d.h"
+#include "RNA_access.h"
+
#include "transform.h"
#include "transform_convert.h"
@@ -215,3 +219,44 @@ void recalcData_sequencer_image(TransInfo *t)
SEQ_relations_invalidate_cache_preprocessed(t->scene, seq);
}
}
+
+void special_aftertrans_update__sequencer_image(bContext *UNUSED(C), TransInfo *t)
+{
+ if (t->state == TRANS_CANCEL) {
+ return;
+ }
+
+ TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
+ TransData *td = NULL;
+ TransData2D *td2d = NULL;
+ int i;
+
+ PointerRNA ptr;
+ PropertyRNA *prop;
+
+ for (i = 0, td = tc->data, td2d = tc->data_2d; i < tc->data_len; i++, td++, td2d++) {
+ TransDataSeq *tdseq = td->extra;
+ Sequence *seq = tdseq->seq;
+ StripTransform *transform = seq->strip->transform;
+ Scene *scene = t->scene;
+
+ RNA_pointer_create(&scene->id, &RNA_SequenceTransform, transform, &ptr);
+
+ if (t->mode == TFM_ROTATION) {
+ prop = RNA_struct_find_property(&ptr, "rotation");
+ ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
+ }
+ if (t->mode == TFM_TRANSLATION) {
+ prop = RNA_struct_find_property(&ptr, "offset_x");
+ ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
+ prop = RNA_struct_find_property(&ptr, "offset_y");
+ ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
+ }
+ if (t->mode == TFM_RESIZE) {
+ prop = RNA_struct_find_property(&ptr, "scale_x");
+ ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
+ prop = RNA_struct_find_property(&ptr, "scale_y");
+ ED_autokeyframe_property(t->context, scene, &ptr, prop, -1, CFRA);
+ }
+ }
+}
diff --git a/source/blender/editors/transform/transform_convert_tracking.c b/source/blender/editors/transform/transform_convert_tracking.c
index dc37f2796bf..f2d0fb7ac2f 100644
--- a/source/blender/editors/transform/transform_convert_tracking.c
+++ b/source/blender/editors/transform/transform_convert_tracking.c
@@ -29,8 +29,10 @@
#include "BLI_math.h"
#include "BKE_context.h"
+#include "BKE_main.h"
#include "BKE_movieclip.h"
#include "BKE_node.h"
+#include "BKE_node_tree_update.h"
#include "BKE_tracking.h"
#include "ED_clip.h"
@@ -793,8 +795,12 @@ void special_aftertrans_update__movieclip(bContext *C, TransInfo *t)
/* Tracks can be used for stabilization nodes,
* flush update for such nodes.
*/
- nodeUpdateID(t->scene->nodetree, &clip->id);
- WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
+ if (t->context != NULL) {
+ Main *bmain = CTX_data_main(C);
+ BKE_ntree_update_tag_id_changed(bmain, &clip->id);
+ BKE_ntree_update_main(bmain, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_NODES, NULL);
+ }
}
}
diff --git a/source/blender/editors/transform/transform_mode_timetranslate.c b/source/blender/editors/transform/transform_mode_timetranslate.c
index 5f2a2e472c5..84ca5d3ae54 100644
--- a/source/blender/editors/transform/transform_mode_timetranslate.c
+++ b/source/blender/editors/transform/transform_mode_timetranslate.c
@@ -62,27 +62,28 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR])
float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival;
float val = ival + t->values_final[0];
- float snap_val = val;
- snapFrameTransform(t, autosnap, ival, val, &snap_val);
+ snapFrameTransform(t, autosnap, ival, val, &val);
+ float delta_x = val - ival;
if (ELEM(autosnap, SACTSNAP_SECOND, SACTSNAP_TSTEP)) {
/* Convert to seconds. */
const Scene *scene = t->scene;
const double secf = FPS;
- snap_val /= secf;
+ delta_x /= secf;
+ val /= secf;
}
if (autosnap == SACTSNAP_FRAME) {
- BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f (%.4f)", snap_val, val);
+ BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f (%.4f)", delta_x, val);
}
else if (autosnap == SACTSNAP_SECOND) {
- BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f sec (%.4f)", snap_val, val);
+ BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.2f sec (%.4f)", delta_x, val);
}
else if (autosnap == SACTSNAP_TSTEP) {
- BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f sec", snap_val);
+ BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f sec", delta_x);
}
else {
- BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", snap_val);
+ BLI_snprintf(&tvec[0], NUM_STR_REP_LEN, "%.4f", delta_x);
}
}
diff --git a/source/blender/editors/transform/transform_mode_translate.c b/source/blender/editors/transform/transform_mode_translate.c
index 19d0c6d39a3..b8b043c650f 100644
--- a/source/blender/editors/transform/transform_mode_translate.c
+++ b/source/blender/editors/transform/transform_mode_translate.c
@@ -225,11 +225,12 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_
if (t->spacetype == SPACE_GRAPH) {
/* WORKAROUND:
* Special case where snapping is done in #recalData.
- * Update the header based on the first element. */
+ * Update the header based on the #center_local. */
const short autosnap = getAnimEdit_SnapMode(t);
- float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->data->ival;
+ float ival = TRANS_DATA_CONTAINER_FIRST_OK(t)->center_local[0];
float val = ival + dvec[0];
- snapFrameTransform(t, autosnap, ival, val, &dvec[0]);
+ snapFrameTransform(t, autosnap, ival, val, &val);
+ dvec[0] = val - ival;
}
if (t->con.mode & CON_APPLY) {
diff --git a/source/blender/editors/transform/transform_orientations.c b/source/blender/editors/transform/transform_orientations.c
index fa2485c33c2..5ac5bccd69c 100644
--- a/source/blender/editors/transform/transform_orientations.c
+++ b/source/blender/editors/transform/transform_orientations.c
@@ -513,7 +513,7 @@ short ED_transform_calc_orientation_from_type_ex(const Scene *scene,
if (ob) {
if (ob->mode & OB_MODE_POSE) {
- const bPoseChannel *pchan = BKE_pose_channel_active(ob);
+ const bPoseChannel *pchan = BKE_pose_channel_active_if_layer_visible(ob);
if (pchan && gimbal_axis_pose(ob, pchan, r_mat)) {
break;
}
@@ -1224,7 +1224,7 @@ int getTransformOrientation_ex(ViewLayer *view_layer,
float imat[3][3], mat[3][3];
bool ok = false;
- if (activeOnly && (pchan = BKE_pose_channel_active(ob))) {
+ if (activeOnly && (pchan = BKE_pose_channel_active_if_layer_visible(ob))) {
add_v3_v3(normal, pchan->pose_mat[2]);
add_v3_v3(plane, pchan->pose_mat[1]);
ok = true;
diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c
index aca99f81e7b..c6fa5acfff5 100644
--- a/source/blender/editors/undo/ed_undo.c
+++ b/source/blender/editors/undo/ed_undo.c
@@ -64,6 +64,7 @@
#include "RNA_access.h"
#include "RNA_define.h"
+#include "RNA_enum_types.h"
#include "UI_interface.h"
#include "UI_resources.h"
@@ -759,81 +760,16 @@ void ED_undo_operator_repeat_cb_evt(bContext *C, void *arg_op, int UNUSED(arg_un
/* -------------------------------------------------------------------- */
/** \name Undo History Operator
+ *
+ * See `TOPBAR_MT_undo_history` which is used to access this operator.
* \{ */
-/* create enum based on undo items */
-static const EnumPropertyItem *rna_undo_itemf(bContext *C, int *totitem)
-{
- EnumPropertyItem item_tmp = {0}, *item = NULL;
- int i = 0;
-
- wmWindowManager *wm = CTX_wm_manager(C);
- if (wm->undo_stack == NULL) {
- return NULL;
- }
-
- for (UndoStep *us = wm->undo_stack->steps.first; us; us = us->next, i++) {
- if (us->skip == false) {
- item_tmp.identifier = us->name;
- item_tmp.name = IFACE_(us->name);
- if (us == wm->undo_stack->step_active) {
- item_tmp.icon = ICON_LAYER_ACTIVE;
- }
- else {
- item_tmp.icon = ICON_NONE;
- }
- item_tmp.value = i;
- RNA_enum_item_add(&item, totitem, &item_tmp);
- }
- }
- RNA_enum_item_end(&item, totitem);
-
- return item;
-}
-
-static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
-{
- int totitem = 0;
-
- {
- const EnumPropertyItem *item = rna_undo_itemf(C, &totitem);
-
- if (totitem > 0) {
- uiPopupMenu *pup = UI_popup_menu_begin(
- C, WM_operatortype_name(op->type, op->ptr), ICON_NONE);
- uiLayout *layout = UI_popup_menu_layout(pup);
- uiLayout *split = uiLayoutSplit(layout, 0.0f, false);
- uiLayout *column = NULL;
- const int col_size = 20 + totitem / 12;
- int i, c;
- bool add_col = true;
-
- for (c = 0, i = totitem; i--;) {
- if (add_col && !(c % col_size)) {
- column = uiLayoutColumn(split, false);
- add_col = false;
- }
- if (item[i].identifier) {
- uiItemIntO(column, item[i].name, item[i].icon, op->type->idname, "item", item[i].value);
- c++;
- add_col = true;
- }
- }
-
- MEM_freeN((void *)item);
-
- UI_popup_menu_end(C, pup);
- }
- }
- return OPERATOR_CANCELLED;
-}
-
/* NOTE: also check #ed_undo_step() in top if you change notifiers. */
static int undo_history_exec(bContext *C, wmOperator *op)
{
PropertyRNA *prop = RNA_struct_find_property(op->ptr, "item");
if (RNA_property_is_set(op->ptr, prop)) {
- int item = RNA_property_int_get(op->ptr, prop);
+ const int item = RNA_property_int_get(op->ptr, prop);
const int ret = ed_undo_step_by_index(C, item, op->reports);
if (ret & OPERATOR_FINISHED) {
ed_undo_refresh_for_op(C);
@@ -845,14 +781,15 @@ static int undo_history_exec(bContext *C, wmOperator *op)
return OPERATOR_CANCELLED;
}
-static bool undo_history_poll(bContext *C)
+static int undo_history_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
{
- if (!ed_undo_is_init_and_screenactive_poll(C)) {
- return false;
+ PropertyRNA *prop = RNA_struct_find_property(op->ptr, "item");
+ if (RNA_property_is_set(op->ptr, prop)) {
+ return undo_history_exec(C, op);
}
- UndoStack *undo_stack = CTX_wm_manager(C)->undo_stack;
- /* More than just original state entry. */
- return BLI_listbase_count_at_most(&undo_stack->steps, 2) > 1;
+
+ WM_menu_name_call(C, "TOPBAR_MT_undo_history", WM_OP_INVOKE_DEFAULT);
+ return OPERATOR_FINISHED;
}
void ED_OT_undo_history(wmOperatorType *ot)
@@ -865,7 +802,7 @@ void ED_OT_undo_history(wmOperatorType *ot)
/* api callbacks */
ot->invoke = undo_history_invoke;
ot->exec = undo_history_exec;
- ot->poll = undo_history_poll;
+ ot->poll = ed_undo_is_init_and_screenactive_poll;
RNA_def_int(ot->srna, "item", 0, 0, INT_MAX, "Item", "", 0, INT_MAX);
}
diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c
index 342afa847b4..64e1fa2f768 100644
--- a/source/blender/editors/uvedit/uvedit_ops.c
+++ b/source/blender/editors/uvedit/uvedit_ops.c
@@ -180,7 +180,7 @@ void ED_object_assign_active_image(Main *bmain, Object *ob, int mat_nr, Image *i
if (node && is_image_texture_node(node)) {
node->id = &ima->id;
- ED_node_tag_update_nodetree(bmain, ma->nodetree, node);
+ ED_node_tree_propagate_change(NULL, bmain, ma->nodetree);
}
}