UI: Gray out Scene Auto-Masking option if equivalent Brush option is used #102971 #106126

Open
Henry-Chang wants to merge 11 commits from Henry-Chang/blender:gray-out-automasking into main

When changing the target branch, be careful to rebase the branch in your fork to match. See documentation.
14 changed files with 268 additions and 28 deletions

View File

@ -7854,42 +7854,63 @@ class VIEW3D_PT_sculpt_automasking(Panel):
layout = self.layout
tool_settings = context.tool_settings
brush = tool_settings.sculpt.brush
sculpt = tool_settings.sculpt
layout.label(text="Auto-Masking")
col = layout.column(align=True)
col.prop(sculpt, "use_automasking_topology", text="Topology")
col.prop(sculpt, "use_automasking_face_sets", text="Face Sets")
sub = col.column()
sub.active = not brush.use_automasking_topology
sub.prop(sculpt, "use_automasking_topology", text="Topology")
sub = col.column()
sub.active = not brush.use_automasking_face_sets
sub.prop(sculpt, "use_automasking_face_sets", text="Face Sets")
col.separator()
col = layout.column(align=True)
col.prop(sculpt, "use_automasking_boundary_edges", text="Mesh Boundary")
col.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary")
sub = col.column()
sub.active = not brush.use_automasking_boundary_edges
sub.prop(sculpt, "use_automasking_boundary_edges", text="Mesh Boundary")
sub = col.column()
sub.active = not brush.use_automasking_boundary_face_sets
sub.prop(sculpt, "use_automasking_boundary_face_sets", text="Face Sets Boundary")
if sculpt.use_automasking_boundary_edges or sculpt.use_automasking_boundary_face_sets:
col.prop(sculpt.brush, "automasking_boundary_edges_propagation_steps")
sub = col.column()
sub.active = not brush.use_automasking_boundary_edges or not brush.use_automasking_boundary_face_sets
sub.prop(sculpt.brush, "automasking_boundary_edges_propagation_steps")
col.separator()
col = layout.column(align=True)
row = col.row()
row.prop(sculpt, "use_automasking_cavity", text="Cavity")
subrow = row.column(align=True)
subrow.active = not brush.use_automasking_cavity and not brush.use_automasking_cavity_inverted
subrow.prop(sculpt, "use_automasking_cavity", text="Cavity")
is_cavity_active = sculpt.use_automasking_cavity or sculpt.use_automasking_cavity_inverted
if is_cavity_active:
props = row.operator("sculpt.mask_from_cavity", text="Create Mask")
subrow = row.column(align=True)
subrow.enabled = not brush.use_automasking_cavity and not brush.use_automasking_cavity_inverted
props = subrow.operator("sculpt.scene_mask_from_cavity", text="Create Mask")
props.settings_source = "SCENE"
col.prop(sculpt, "use_automasking_cavity_inverted", text="Cavity (inverted)")
subcol = col.column(align=True)
subcol.active = not brush.use_automasking_cavity and not brush.use_automasking_cavity_inverted
subcol.prop(sculpt, "use_automasking_cavity_inverted", text="Cavity (inverted)")
if is_cavity_active:
col = layout.column(align=True)
col.active = not brush.use_automasking_cavity_inverted and not brush.use_automasking_cavity
col.prop(sculpt, "automasking_cavity_factor", text="Factor")
col.prop(sculpt, "automasking_cavity_blur_steps", text="Blur")
col = layout.column()
col.active = not brush.use_automasking_cavity_inverted and not brush.use_automasking_cavity
col.prop(sculpt, "use_automasking_custom_cavity_curve", text="Custom Curve")
if sculpt.use_automasking_custom_cavity_curve:
@ -7898,20 +7919,23 @@ class VIEW3D_PT_sculpt_automasking(Panel):
col.separator()
col = layout.column(align=True)
col.active = not brush.use_automasking_view_normal
col.prop(sculpt, "use_automasking_view_normal", text="View Normal")
if sculpt.use_automasking_view_normal:
col.prop(sculpt, "use_automasking_view_occlusion", text="Occlusion")
subcol = col.column(align=True)
subcol.active = not sculpt.use_automasking_view_occlusion
subcol.prop(sculpt, "automasking_view_normal_limit", text="Limit")
subcol.prop(sculpt, "automasking_view_normal_falloff", text="Falloff")
subcol.prop(sculpt, "automasking_scene_view_normal_limit", text="Limit")
subcol.prop(sculpt, "automasking_scene_view_normal_falloff", text="Falloff")
col = layout.column()
col.active = not brush.use_automasking_start_normal
col.prop(sculpt, "use_automasking_start_normal", text="Area Normal")
if sculpt.use_automasking_start_normal:
col = layout.column(align=True)
col.active = not brush.use_automasking_start_normal
col.prop(sculpt, "automasking_start_normal_limit", text="Limit")
col.prop(sculpt, "automasking_start_normal_falloff", text="Falloff")

View File

@ -2078,6 +2078,8 @@ void BKE_sculpt_toolsettings_data_ensure(Scene *scene)
sd->automasking_start_normal_limit = 20.0f / 180.0f * M_PI;
sd->automasking_start_normal_falloff = 0.25f;
sd->automasking_scene_view_normal_limit = 90.0f / 180.0f * M_PI;
Review

Why is this change done here, it seems unrelated to the patch?

Why is this change done here, it seems unrelated to the patch?
Review

Wait I see that these properties were introduced just now. Then my question is, what are these properties doing? That's not immediately clear to me.

Wait I see that these properties were introduced just now. Then my question is, what are these properties doing? That's not immediately clear to me.
Review

This is due specifically to the Occlusion toggle in View Normal setting. When Occlusion is selected, it will make the Limit and Falloff scroll bars in Brush Panel inactive. If the disabled_info is set at the RNA level, a "Disabled:" message would erroneously show up when hovering over View Normal Limit and View Normal Falloff in the Brush Panel. This would not be needed if disabled_info is set at the Python layout definition level.

This is due specifically to the Occlusion toggle in View Normal setting. When Occlusion is selected, it will make the Limit and Falloff scroll bars in Brush Panel inactive. If the disabled_info is set at the RNA level, a "Disabled:" message would erroneously show up when hovering over View Normal Limit and View Normal Falloff in the Brush Panel. This would not be needed if disabled_info is set at the Python layout definition level.
sd->automasking_scene_view_normal_falloff = 0.25f;
sd->automasking_view_normal_limit = 90.0f / 180.0f * M_PI;
sd->automasking_view_normal_falloff = 0.25f;
}

View File

@ -896,6 +896,7 @@ void UI_but_dragflag_enable(uiBut *but, int flag);
void UI_but_dragflag_disable(uiBut *but, int flag);
void UI_but_disable(uiBut *but, const char *disabled_hint);
void UI_but_set_disabled_hint(uiBut *but, const char *disabled_hint);
void UI_but_type_set_menu_from_pulldown(uiBut *but);

View File

@ -4715,6 +4715,10 @@ static uiBut *ui_def_but_rna(uiBlock *block,
UI_but_disable(but, info);
}
if (but->rnapoin.data && RNA_property_is_inactive_info(&but->rnapoin, prop, &info)) {
UI_but_set_disabled_hint(but, info);
}
if (proptype == PROP_POINTER) {
/* If the button shows an ID, automatically set it as focused in context so operators can
* access it. */
@ -5904,10 +5908,8 @@ void UI_but_dragflag_disable(uiBut *but, int flag)
but->dragflag &= ~flag;
}
void UI_but_disable(uiBut *but, const char *disabled_hint)
void UI_but_set_disabled_hint(uiBut *but, const char *disabled_hint)
{
UI_but_flag_enable(but, UI_BUT_DISABLED);
/* Only one disabled hint at a time currently. Don't override the previous one here. */
if (but->disabled_info && but->disabled_info[0]) {
return;
@ -5916,6 +5918,13 @@ void UI_but_disable(uiBut *but, const char *disabled_hint)
but->disabled_info = disabled_hint;
}
void UI_but_disable(uiBut *but, const char *disabled_hint)
{
UI_but_flag_enable(but, UI_BUT_DISABLED);
UI_but_set_disabled_hint(but, disabled_hint);
}
void UI_but_type_set_menu_from_pulldown(uiBut *but)
{
BLI_assert(but->type == UI_BTYPE_PULLDOWN);

View File

@ -897,8 +897,8 @@ static uiTooltipData *ui_tooltip_data_from_button_or_extra_icon(bContext *C,
MEM_freeN(str);
}
/* Button is disabled, we may be able to tell user why. */
if ((but->flag & UI_BUT_DISABLED) || extra_icon) {
/* Button is disabled or set to inactive, we may be able to tell user why. */
if ((but->flag & UI_BUT_DISABLED) || (but->flag & UI_BUT_INACTIVE) || extra_icon) {
Review

Can be shortened:

if ((but->flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) || extra_icon) {
  ...
}
Can be shortened: ```C if ((but->flag & (UI_BUT_DISABLED | UI_BUT_INACTIVE)) || extra_icon) { ... } ```
Review

Understood, would update.

Understood, would update.
const char *disabled_msg = nullptr;
bool disabled_msg_free = false;

View File

@ -4273,6 +4273,19 @@ bool SCULPT_mode_poll(bContext *C)
return ob && ob->mode & OB_MODE_SCULPT;
}
bool SCULPT_mode_poll_cavity_automask(bContext *C)
{
ToolSettings *tool_settings = CTX_data_tool_settings(C);
int automasking_flags = tool_settings->sculpt->paint.brush->automasking_flags;
if (SCULPT_mode_poll(C) && !(automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
Review

I find it generally more readable if error conditions are checked individually first. I.e.:

if (!SCULPT_mode_poll(C) {
  return false;
}
if (automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL) {
  CTX_wm_operator_poll_msg_set(C, ...);
  return false
}

return true;

This minimizes the amount of information you have to keep in mind or understand when reading/changing code. (Also this would set the wrong poll message outside of sculpt mode, one more reason to keep error condition checks separate.)

I find it generally more readable if error conditions are checked individually first. I.e.: ```C if (!SCULPT_mode_poll(C) { return false; } if (automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL) { CTX_wm_operator_poll_msg_set(C, ...); return false } return true; ``` This minimizes the amount of information you have to keep in mind or understand when reading/changing code. (Also this would set the wrong poll message outside of sculpt mode, one more reason to keep error condition checks separate.)
Review

Makes sense, would update.

Makes sense, would update.
return true;
} else {
CTX_wm_operator_poll_msg_set(C, "The active brush already has the same auto-masking enabled.");
return false;
}
}
bool SCULPT_mode_poll_view3d(bContext *C)
{
return (SCULPT_mode_poll(C) && CTX_wm_region_view3d(C));

View File

@ -843,6 +843,7 @@ struct ExpandCache {
* \{ */
bool SCULPT_mode_poll(bContext *C);
bool SCULPT_mode_poll_cavity_automask(bContext *C);
bool SCULPT_mode_poll_view3d(bContext *C);
/**
* Checks for a brush, not just sculpt mode.

View File

@ -1290,6 +1290,13 @@ static void SCULPT_OT_mask_from_cavity(wmOperatorType *ot)
RNA_def_boolean(ot->srna, "invert", false, "Cavity (Inverted)", "");
}
static void SCULPT_OT_scene_mask_from_cavity(wmOperatorType *ot)
{
SCULPT_OT_mask_from_cavity(ot);
ot->idname = "SCULPT_OT_scene_mask_from_cavity";
ot->poll = SCULPT_mode_poll_cavity_automask;
}
static int sculpt_reveal_all_exec(bContext *C, wmOperator *op)
{
Object *ob = CTX_data_active_object(C);
@ -1424,6 +1431,7 @@ void ED_operatortypes_sculpt(void)
WM_operatortype_append(SCULPT_OT_mask_init);
WM_operatortype_append(SCULPT_OT_expand);
WM_operatortype_append(SCULPT_OT_scene_mask_from_cavity);
WM_operatortype_append(SCULPT_OT_mask_from_cavity);
WM_operatortype_append(SCULPT_OT_reveal_all);
}

View File

@ -1089,6 +1089,7 @@ typedef struct Sculpt {
float automasking_start_normal_limit, automasking_start_normal_falloff;
float automasking_view_normal_limit, automasking_view_normal_falloff;
float automasking_scene_view_normal_limit, automasking_scene_view_normal_falloff;
struct CurveMapping *automasking_cavity_curve;
/** For use by operators. */

View File

@ -274,6 +274,7 @@ bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop);
* that can be exposed in UI.
*/
bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info);
bool RNA_property_is_inactive_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info);
/**
* Same as RNA_property_editable(), except this checks individual items in an array.
*/

View File

@ -187,6 +187,15 @@ typedef enum PropertyFlag {
* for pointers and collections.
*/
PROP_EDITABLE = (1 << 0),
/**
* Inactive specifically only refers to the front end
* where UI elements are grayed out but can still be
* toggled. It is specifically used to add Disabled
* message for when UI is set to inactive through Python
* script. It is completely unrelated to anything that
* is performed in the backend.
*/
PROP_INACTIVE = (1 << 9),
/**
* This property is editable even if it is lib linked,
* meaning it will get lost on reload, but it's useful

View File

@ -1935,6 +1935,32 @@ int RNA_property_ui_icon(const PropertyRNA *prop)
return rna_ensure_property((PropertyRNA *)prop)->icon;
}
static bool rna_property_is_inactive_do(PointerRNA *ptr,
PropertyRNA *prop_orig,
const int index,
const char **r_info)
{
PropertyRNA *prop = rna_ensure_property(prop_orig);
const char *info = "";
const int flag = (prop->itemeditable != nullptr && index >= 0) ?
prop->itemeditable(ptr, index) :
(prop->editable != nullptr ? prop->editable(ptr, &info) : prop->flag);
if (r_info != nullptr) {
*r_info = info;
}
/* Return true if the property itself is inative. */
if ((flag & PROP_INACTIVE) != 0) {
if (r_info != nullptr && (*r_info)[0] == '\0') {
*r_info = N_("This property is for internal use only and can't be edited");
}
return true;
}
return false;
}
static bool rna_property_editable_do(PointerRNA *ptr,
PropertyRNA *prop_orig,
const int index,
@ -2001,6 +2027,11 @@ bool RNA_property_editable(PointerRNA *ptr, PropertyRNA *prop)
return rna_property_editable_do(ptr, prop, -1, nullptr);
}
bool RNA_property_is_inactive_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info)
{
return rna_property_is_inactive_do(ptr, prop, -1, r_info);
}
bool RNA_property_editable_info(PointerRNA *ptr, PropertyRNA *prop, const char **r_info)
{
return rna_property_editable_do(ptr, prop, -1, r_info);

View File

@ -3078,6 +3078,7 @@ static void rna_def_brush(BlenderRNA *brna)
"Propagation Steps",
"Distance where boundary edge automasking is going to protect vertices "
"from the fully masked edge");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_mesh_and_face_sets_boundary_editable");
RNA_def_property_update(prop, 0, "rna_Brush_update");
prop = RNA_def_property(srna, "auto_smooth_factor", PROP_FLOAT, PROP_FACTOR);
@ -3839,3 +3840,13 @@ void RNA_def_brush(BlenderRNA *brna)
}
#endif
static int rna_Scene_automasking_mesh_and_face_sets_boundary_editable(struct PointerRNA *ptr, const char **r_info) {
Brush *br = (Brush*)ptr->data;
if (br && (br->automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES) && (br->automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
*r_info = "The active brush already has the same auto-masking enabled.";
return PROP_INACTIVE | PROP_EDITABLE;
}
return br ? PROP_EDITABLE: 0;
}

View File

@ -862,25 +862,54 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_flag(prop, PROP_CONTEXT_UPDATE);
RNA_def_property_update(prop, NC_OBJECT | ND_DRAW, "rna_Sculpt_update");
const EnumPropertyItem *entry = rna_enum_brush_automasking_flag_items;
do {
prop = RNA_def_property(srna, entry->identifier, PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", entry->value);
RNA_def_property_ui_text(prop, entry->name, entry->description);
prop = RNA_def_property(srna, "use_automasking_topology", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_TOPOLOGY);
RNA_def_property_ui_text(prop, "Topology", "Affect only vertices connected to the active vertex under the brush");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_topology_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
if (entry->value == BRUSH_AUTOMASKING_CAVITY_NORMAL) {
RNA_def_property_boolean_funcs(prop, NULL, "rna_Sculpt_automasking_cavity_set");
}
else if (entry->value == BRUSH_AUTOMASKING_CAVITY_INVERTED) {
RNA_def_property_boolean_funcs(prop, NULL, "rna_Sculpt_automasking_invert_cavity_set");
}
prop = RNA_def_property(srna, "use_automasking_face_sets", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_FACE_SETS);
RNA_def_property_ui_text(prop, "Face Sets", "Affect only vertices that share Face Sets with the active vertex");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_face_sets_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
} while ((++entry)->identifier);
prop = RNA_def_property(srna, "use_automasking_boundary_edges", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_BOUNDARY_EDGES);
RNA_def_property_ui_text(prop, "Mesh Boundary Auto-Masking", "Do not affect non manifold boundary edges");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_mesh_boundary_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_boundary_face_sets", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS);
RNA_def_property_ui_text(prop, "Face Sets Boundary Automasking", "Do not affect vertices that belong to a Face Set boundary");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_face_sets_boundary_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_cavity", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_CAVITY_NORMAL);
RNA_def_property_ui_text(prop, "Cavity Mask", "Do not affect vertices on peaks, based on the surface curvature");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_cavity_editable");
RNA_def_property_boolean_funcs(prop, NULL, "rna_Sculpt_automasking_cavity_set");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_cavity_inverted", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_CAVITY_INVERTED);
RNA_def_property_ui_text(prop, "Inverted Cavity Mask", "Do not affect vertices within crevices, based on the surface curvature");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_cavity_editable");
RNA_def_property_boolean_funcs(prop, NULL, "rna_Sculpt_automasking_invert_cavity_set");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_custom_cavity_curve", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_CAVITY_USE_CURVE);
RNA_def_property_ui_text(prop, "Custom Cavity Curve", "Use custom curve");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_cavity_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_cavity_factor", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "automasking_cavity_factor");
RNA_def_property_ui_text(prop, "Cavity Factor", "The contrast of the cavity mask");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_cavity_editable");
RNA_def_property_float_default(prop, 1.0f);
RNA_def_property_range(prop, 0.0f, 5.0f);
RNA_def_property_ui_range(prop, 0.0f, 1.0f, 0.1, 3);
@ -889,6 +918,7 @@ static void rna_def_sculpt(BlenderRNA *brna)
prop = RNA_def_property(srna, "automasking_cavity_blur_steps", PROP_INT, PROP_NONE);
RNA_def_property_int_sdna(prop, NULL, "automasking_cavity_blur_steps");
RNA_def_property_ui_text(prop, "Blur Steps", "The number of times the cavity mask is blurred");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_cavity_editable");
RNA_def_property_int_default(prop, 0);
RNA_def_property_range(prop, 0, 25);
RNA_def_property_ui_range(prop, 0, 10, 1, 1);
@ -898,6 +928,7 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "automasking_cavity_curve");
RNA_def_property_struct_type(prop, "CurveMapping");
RNA_def_property_ui_text(prop, "Cavity Curve", "Curve used for the sensitivity");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_cavity_editable");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
@ -905,6 +936,7 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_pointer_sdna(prop, NULL, "automasking_cavity_curve_op");
RNA_def_property_struct_type(prop, "CurveMapping");
RNA_def_property_ui_text(prop, "Cavity Curve", "Curve used for the sensitivity");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_cavity_editable");
RNA_def_property_clear_flag(prop, PROP_ANIMATABLE);
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
@ -914,12 +946,14 @@ static void rna_def_sculpt(BlenderRNA *brna)
prop,
"Area Normal",
"Affect only vertices with a similar normal to where the stroke starts");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_area_normal_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_view_normal", PROP_BOOLEAN, PROP_NONE);
RNA_def_property_boolean_sdna(prop, NULL, "automasking_flags", BRUSH_AUTOMASKING_VIEW_NORMAL);
RNA_def_property_ui_text(
prop, "View Normal", "Affect only vertices with a normal that faces the viewer");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_view_normal_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "use_automasking_view_occlusion", PROP_BOOLEAN, PROP_NONE);
@ -928,12 +962,14 @@ static void rna_def_sculpt(BlenderRNA *brna)
prop,
"Occlusion",
"Only affect vertices that are not occluded by other faces. (Slower performance)");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_view_normal_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_start_normal_limit", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "automasking_start_normal_limit");
RNA_def_property_range(prop, 0.0001f, M_PI);
RNA_def_property_ui_text(prop, "Area Normal Limit", "The range of angles that will be affected");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_area_normal_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_start_normal_falloff", PROP_FLOAT, PROP_FACTOR);
@ -941,6 +977,7 @@ static void rna_def_sculpt(BlenderRNA *brna)
RNA_def_property_range(prop, 0.0001f, 1.0f);
RNA_def_property_ui_text(
prop, "Area Normal Falloff", "Extend the angular range with a falloff gradient");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_area_normal_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_view_normal_limit", PROP_FLOAT, PROP_ANGLE);
@ -956,6 +993,21 @@ static void rna_def_sculpt(BlenderRNA *brna)
prop, "View Normal Falloff", "Extend the angular range with a falloff gradient");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_scene_view_normal_limit", PROP_FLOAT, PROP_ANGLE);
RNA_def_property_float_sdna(prop, NULL, "automasking_scene_view_normal_limit");
RNA_def_property_range(prop, 0.0001f, M_PI);
RNA_def_property_ui_text(prop, "View Normal Limit", "The range of angles that will be affected");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_view_normal_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "automasking_scene_view_normal_falloff", PROP_FLOAT, PROP_FACTOR);
RNA_def_property_float_sdna(prop, NULL, "automasking_scene_view_normal_falloff");
RNA_def_property_range(prop, 0.0001f, 1.0f);
RNA_def_property_ui_text(
prop, "View Normal Falloff", "Extend the angular range with a falloff gradient");
RNA_def_property_editable_func(prop, "rna_Scene_automasking_view_normal_editable");
RNA_def_property_update(prop, NC_SCENE | ND_TOOLSETTINGS, NULL);
prop = RNA_def_property(srna, "symmetrize_direction", PROP_ENUM, PROP_NONE);
RNA_def_property_enum_items(prop, rna_enum_symmetrize_direction_items);
RNA_def_property_ui_text(prop, "Direction", "Source and destination for symmetrize operator");
@ -1701,3 +1753,80 @@ void RNA_def_sculpt_paint(BlenderRNA *brna)
}
#endif
static int rna_Scene_automasking_topology_editable(struct PointerRNA *ptr, const char **r_info) {
Sculpt *sd = (Sculpt*)ptr->data;
if (sd && (sd->paint.brush->automasking_flags & BRUSH_AUTOMASKING_TOPOLOGY)) {
*r_info = "The active brush already has the same auto-masking enabled.";
return PROP_INACTIVE | PROP_EDITABLE;
}
return sd ? PROP_EDITABLE: 0;
}
static int rna_Scene_automasking_face_sets_editable(struct PointerRNA *ptr, const char **r_info) {
Sculpt *sd = (Sculpt*)ptr->data;
if (sd && (sd->paint.brush->automasking_flags & BRUSH_AUTOMASKING_FACE_SETS)) {
*r_info = "The active brush already has the same auto-masking enabled.";
return PROP_INACTIVE | PROP_EDITABLE;
}
return sd ? PROP_EDITABLE: 0;
}
static int rna_Scene_automasking_mesh_boundary_editable(struct PointerRNA *ptr, const char **r_info) {
Sculpt *sd = (Sculpt*)ptr->data;
if (sd && (sd->paint.brush->automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_EDGES)) {
*r_info = "The active brush already has the same auto-masking enabled.";
return PROP_INACTIVE | PROP_EDITABLE;
}
return sd ? PROP_EDITABLE: 0;
}
static int rna_Scene_automasking_face_sets_boundary_editable(struct PointerRNA *ptr, const char **r_info) {
Sculpt *sd = (Sculpt*)ptr->data;
if (sd && (sd->paint.brush->automasking_flags & BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS)) {
*r_info = "The active brush already has the same auto-masking enabled.";
return PROP_INACTIVE | PROP_EDITABLE;
}
return sd ? PROP_EDITABLE: 0;
}
static int rna_Scene_automasking_cavity_editable(struct PointerRNA *ptr, const char **r_info) {
Sculpt *sd = (Sculpt*)ptr->data;
if (sd && (sd->paint.brush->automasking_flags & BRUSH_AUTOMASKING_CAVITY_ALL)) {
*r_info = "The active brush already has the same auto-masking enabled.";
return PROP_INACTIVE | PROP_EDITABLE;
}
return sd ? PROP_EDITABLE: 0;
}
static int rna_Scene_automasking_view_normal_editable(struct PointerRNA *ptr, const char **r_info) {
Sculpt *sd = (Sculpt*)ptr->data;
if (sd && (sd->paint.brush->automasking_flags & BRUSH_AUTOMASKING_VIEW_NORMAL)) {
*r_info = "The active brush already has the same auto-masking enabled.";
// TODO should probably utilize RNA_def_property_float_funcs to sync values between automasking_view and automasking_scene_view
sd->automasking_scene_view_normal_limit = sd->automasking_view_normal_limit;
Review

Do not change properties inside of an editable callback, these are meant to be mere queries and not modify data.

Do not change properties inside of an editable callback, these are meant to be mere queries and not modify data.
Review

Agreed. Will definitely revert them.

Agreed. Will definitely revert them.
sd->automasking_scene_view_normal_falloff = sd->automasking_view_normal_falloff;
return PROP_INACTIVE | PROP_EDITABLE;
} else {
sd->automasking_view_normal_limit = sd->automasking_scene_view_normal_limit;
sd->automasking_view_normal_falloff = sd->automasking_scene_view_normal_falloff;
}
return sd ? PROP_EDITABLE: 0;
}
static int rna_Scene_automasking_area_normal_editable(struct PointerRNA *ptr, const char **r_info) {
Sculpt *sd = (Sculpt*)ptr->data;
if (sd && (sd->paint.brush->automasking_flags & BRUSH_AUTOMASKING_BRUSH_NORMAL)) {
*r_info = "The active brush already has the same auto-masking enabled.";
return PROP_INACTIVE | PROP_EDITABLE;
}
return sd ? PROP_EDITABLE: 0;
}