diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index 9364aafb4ed..c6bad232953 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -2105,11 +2105,51 @@ def brush_settings_advanced(layout, context, brush, popover=False): context, brush, "automasking_boundary_edges_propagation_steps") - UnifiedPaintPanel.channel_unified(layout.column(), + + flags = UnifiedPaintPanel.get_channel_value(context, brush, "automasking") + + if "CONCAVITY" in flags: + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "concave_mask_factor", + slider=True) + + enable_orig_normal = False + + if "BRUSH_NORMAL" in flags: + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "normal_mask_limit", + slider=True) + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "normal_mask_falloff", + slider=True) + enable_orig_normal = True + + if "VIEW_NORMAL" in flags: + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "view_normal_mask_limit", + slider=True) + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "view_normal_mask_falloff", + slider=True) + enable_orig_normal = True + + sub = layout.row() + sub.enabled = enable_orig_normdal + + UnifiedPaintPanel.channel_unified(sub, context, brush, - "concave_mask_factor", - slider=True) + "automasking_use_original_normal") """ col = layout.column(heading="Auto-Masking", align=True) diff --git a/release/scripts/startup/bl_ui/space_toolsystem_common.py b/release/scripts/startup/bl_ui/space_toolsystem_common.py index c4dabb5b5bc..f63beee3f03 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_common.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_common.py @@ -120,6 +120,8 @@ ToolDef = namedtuple( "draw_cursor", # Various options, see: `bpy.types.WorkSpaceTool.setup` options argument. "options", + #get_enabled(ctx, idname) whether tool should be grayed out + "get_enabled" ) ) del namedtuple @@ -143,6 +145,7 @@ def from_dict(kw_args): "operator": None, "draw_settings": None, "draw_cursor": None, + "get_enabled": None } kw.update(kw_args) @@ -725,9 +728,14 @@ class ToolSelectPanelHelper: icon_value = ToolSelectPanelHelper._icon_value_from_icon_handle(item.icon) sub = ui_gen.send(False) + sub2 = sub + + if item.get_enabled is not None: + sub2 = sub.row(align=False) + sub2.enabled = item.get_enabled(context, item.idname) if use_menu: - sub.operator_menu_hold( + sub2.operator_menu_hold( "wm.tool_set_by_id", text=item.label if show_text else "", depress=is_active, @@ -735,7 +743,7 @@ class ToolSelectPanelHelper: icon_value=icon_value, ).name = item.idname else: - sub.operator( + sub2.operator( "wm.tool_set_by_id", text=item.label if show_text else "", depress=is_active, diff --git a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py index da185b53544..6edd0cb5a6d 100644 --- a/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py +++ b/release/scripts/startup/bl_ui/space_toolsystem_toolbar.py @@ -1211,6 +1211,83 @@ class _defs_particle: attr="tool",) +def generate_from_enum_ex(_context, *, + idname_prefix, + icon_prefix, + type, + attr, + cursor='DEFAULT', + tooldef_keywords={}, + exclude_filter={}, + combine_map={}): + """ + combine_map combines items, takes the form of a dict: + + combine_map = { + "PARENT KEY" : ( + "CHILD KEY" + ) + } + + """ + + combinekeys = {} + parentmap = {} + + for k, v in combine_map.items(): + combinekeys[k] = [] + + for child in v: + parentmap[child] = k + + tool_defs = [] + + #build combine key owners first + for enum in type.bl_rna.properties[attr].enum_items_static: + name = enum.name + idname = enum.identifier + if idname in exclude_filter: + continue + + if idname in combinekeys: + combinekeys[idname].append(ToolDef.from_dict(dict(idname=idname_prefix + name, + label=name, + icon=icon_prefix + idname.lower(), + cursor=cursor, + data_block=idname, + **tooldef_keywords,))) + + for enum in type.bl_rna.properties[attr].enum_items_static: + name = enum.name + idname = enum.identifier + if idname in exclude_filter: + continue + + if idname in combinekeys: + tool_defs.append(combinekeys[idname]) + elif idname in parentmap: + parentkey = parentmap[idname] + + combinekeys[parentkey].append(ToolDef.from_dict(dict(idname=idname_prefix + name, + label=name, + icon=icon_prefix + idname.lower(), + cursor=cursor, + data_block=idname, + **tooldef_keywords,))) + else: + tool_defs.append(ToolDef.from_dict(dict(idname=idname_prefix + name, + label=name, + icon=icon_prefix + idname.lower(), + cursor=cursor, + data_block=idname, + **tooldef_keywords,))) + + # finalize combined keys + for k, v in combinekeys.items(): + tool_defs[tool_defs.index(v)] = tuple(v) + + return tuple(tool_defs) + class _defs_sculpt: @staticmethod @@ -1229,12 +1306,32 @@ class _defs_sculpt: "SMOOTH" : ("ENHANCE_DETAILS",) } + def get_enabled(context, idname): + if "multires" in idname.lower() or idname.lower() == "builtin_brush.displacement heal": + print("IDNAME", idname) + have_multires = False; + ob = context.object + + for mod in ob.modifiers: + ok = mod.type == "MULTIRES" + ok = ok and mod.sculpt_levels > 0 + ok = ok and mod.show_viewport + + if ok: + have_multires = True + break + + return have_multires + + return True + return generate_from_enum_ex(context, idname_prefix="builtin_brush.", icon_prefix="brush.sculpt.", type=bpy.types.Brush, attr="sculpt_tool", exclude_filter=exclude_filter, + tooldef_keywords={"get_enabled" : get_enabled}, combine_map=combine_map) @ToolDef.from_fn diff --git a/release/scripts/startup/bl_ui/space_view3d_toolbar.py b/release/scripts/startup/bl_ui/space_view3d_toolbar.py index 2fbad9ba285..c5f0c055b9a 100644 --- a/release/scripts/startup/bl_ui/space_view3d_toolbar.py +++ b/release/scripts/startup/bl_ui/space_view3d_toolbar.py @@ -1361,11 +1361,50 @@ class VIEW3D_PT_sculpt_automasking(Panel, View3DPaintPanel): brush, "automasking_boundary_edges_propagation_steps", ui_editing=False) - UnifiedPaintPanel.channel_unified(layout.column(), + + if "CONCAVITY" in sculpt.channels["automasking"].value: + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "concave_mask_factor", + ui_editing=False, slider=True, show_mappings=True) + + enable_orig_normal = False + + if "BRUSH_NORMAL" in sculpt.channels["automasking"].value: + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "normal_mask_limit", + ui_editing=False, slider=True, show_mappings=True) + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "normal_mask_falloff", + ui_editing=False, slider=True, show_mappings=True) + enable_orig_normal = True + + if "VIEW_NORMAL" in sculpt.channels["automasking"].value: + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "view_normal_mask_limit", + ui_editing=False, slider=True, show_mappings=True) + UnifiedPaintPanel.channel_unified(layout.column(), + context, + brush, + "view_normal_mask_falloff", + ui_editing=False, slider=True, show_mappings=True) + enable_orig_normal = True + + sub = layout.row() + sub.enabled = enable_orig_normal + + UnifiedPaintPanel.channel_unified(sub, context, brush, - "concave_mask_factor", - ui_editing=False, slider=True, show_mappings=True) + "automasking_use_original_normal", + ui_editing=False, show_mappings=False) class VIEW3D_PT_sculpt_options_gravity(Panel, View3DPaintPanel): diff --git a/source/blender/blenkernel/intern/brush_channel_define.h b/source/blender/blenkernel/intern/brush_channel_define.h index 9a56991e1c8..6c9021d1ef3 100644 --- a/source/blender/blenkernel/intern/brush_channel_define.h +++ b/source/blender/blenkernel/intern/brush_channel_define.h @@ -270,9 +270,19 @@ MAKE_ENUM(blend,"Blending Mode","Brush blending mode",IMB_BLEND_MIX,{\ {BRUSH_AUTOMASKING_INVERT_CONCAVITY, "INVERT_CONCAVITY", "NONE", "Invert Cavity", "Invert Cavity Map"}, {BRUSH_AUTOMASKING_FACE_SETS, "FACE_SETS", "NONE", "Face Sets", ""}, {BRUSH_AUTOMASKING_TOPOLOGY, "TOPOLOGY", "NONE", "Topology", ""}, + {BRUSH_AUTOMASKING_BRUSH_NORMAL, "BRUSH_NORMAL", "NONE", "Brush Normal", "Mask using normal at center of brush"}, + {BRUSH_AUTOMASKING_VIEW_NORMAL, "VIEW_NORMAL", "NONE", "View Normal", "Mask using view normal"}, {-1}, }) + MAKE_FLOAT(normal_mask_limit, "Brush Normal Limit", "", M_PI*0.5f, 0.0001f, M_PI) + MAKE_FLOAT(normal_mask_falloff, "Brush Normal Falloff", "", 0.1, 0.0, 1.0) + + MAKE_FLOAT(view_normal_mask_limit, "View Limit", "", M_PI*0.5f, 0.0001f, M_PI) + MAKE_FLOAT(view_normal_mask_falloff, "View Falloff", "", 0.1, 0.0, 1.0) + + MAKE_BOOL_EX(automasking_use_original_normal, "Original Normal", "Use original normal for automasking", true, BRUSH_CHANNEL_NO_MAPPINGS) + MAKE_BOOL_EX(dyntopo_disabled,"Disable Dyntopo","",false,BRUSH_CHANNEL_NO_MAPPINGS) MAKE_FLAGS_EX(dyntopo_mode,"Dyntopo Operators","",DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP | DYNTOPO_SUBDIVIDE,BRUSH_CHANNEL_INHERIT,{\ {DYNTOPO_COLLAPSE, "COLLAPSE", "NONE", "Collapse", ""}, diff --git a/source/blender/blenkernel/intern/brush_engine_presets.c b/source/blender/blenkernel/intern/brush_engine_presets.c index 5dd202194f0..0c463470edc 100644 --- a/source/blender/blenkernel/intern/brush_engine_presets.c +++ b/source/blender/blenkernel/intern/brush_engine_presets.c @@ -264,7 +264,9 @@ static bool check_builtin_init() //} SUBTYPE_SET(smooth_stroke_radius, BRUSH_CHANNEL_PIXEL); - + SUBTYPE_SET(normal_mask_limit, BRUSH_CHANNEL_ANGLE); + SUBTYPE_SET(view_normal_mask_limit, BRUSH_CHANNEL_ANGLE); + SUBTYPE_SET(jitter_absolute, BRUSH_CHANNEL_PIXEL); SUBTYPE_SET(radius, BRUSH_CHANNEL_PIXEL); @@ -306,6 +308,11 @@ static bool check_builtin_init() SETCAT(concave_mask_factor, "Automasking"); SETCAT(automasking, "Automasking"); SETCAT(automasking_boundary_edges_propagation_steps, "Automasking"); + SETCAT(normal_mask_limit, "Automasking"); + SETCAT(view_normal_mask_limit, "Automasking"); + SETCAT(normal_mask_falloff, "Automasking"); + SETCAT(view_normal_mask_falloff, "Automasking"); + SETCAT(automasking_use_original_normal, "Automasking"); def = GETDEF(concave_mask_factor); def->mappings.pressure.inv = true; @@ -1144,6 +1151,11 @@ void BKE_brush_builtin_patch(Brush *brush, int tool) ADDCH(automasking); ADDCH(automasking_boundary_edges_propagation_steps); ADDCH(concave_mask_factor); + ADDCH(normal_mask_limit); + ADDCH(automasking_use_original_normal); + ADDCH(view_normal_mask_limit); + ADDCH(normal_mask_falloff); + ADDCH(view_normal_mask_falloff); ADDCH(dyntopo_disabled); ADDCH(dyntopo_disable_smooth); @@ -2045,6 +2057,12 @@ void BKE_brush_check_toolsettings(Sculpt *sd) ADDCH(automasking_boundary_edges_propagation_steps); ADDCH(concave_mask_factor); ADDCH(automasking); + ADDCH(normal_mask_limit); + ADDCH(automasking_use_original_normal); + ADDCH(view_normal_mask_limit); + ADDCH(normal_mask_falloff); + ADDCH(view_normal_mask_falloff); + ADDCH(topology_rake_mode); ADDCH(plane_offset); diff --git a/source/blender/draw/intern/draw_cache_impl_mesh.c b/source/blender/draw/intern/draw_cache_impl_mesh.c index 6981c771173..8bde4e081d6 100644 --- a/source/blender/draw/intern/draw_cache_impl_mesh.c +++ b/source/blender/draw/intern/draw_cache_impl_mesh.c @@ -574,12 +574,34 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, if (layer == -1) { layer = CustomData_get_named_layer(cd_vdata, CD_PROP_COLOR, name); - type = CD_PROP_COLOR; + if (layer != -1) { + type = CD_PROP_COLOR; + domain = ATTR_DOMAIN_POINT; + } + } + + if (layer == -1) { + layer = CustomData_get_named_layer(cd_ldata, CD_PROP_COLOR, name); + if (layer != -1) { + type = CD_PROP_COLOR; + domain = ATTR_DOMAIN_CORNER; + } + } + + if (layer == -1) { + layer = CustomData_get_named_layer(cd_vdata, CD_MLOOPCOL, name); + if (layer != -1) { + type = CD_MLOOPCOL; + domain = ATTR_DOMAIN_POINT; + } } if (layer == -1) { layer = CustomData_get_named_layer(cd_ldata, CD_MLOOPCOL, name); - type = CD_MCOL; + if (layer != -1) { + type = CD_MLOOPCOL; + domain = ATTR_DOMAIN_CORNER; + } } #if 0 /* Tangents are always from UV's - this will never happen. */ @@ -656,7 +678,10 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, break; } - case CD_MCOL: + /* note that attr->type will always be CD_PROP_COLOR event for + CD_MLOOPCOL layers, see node_shader_gpu_vertex_color in + node_shader_vertex_color.cc + */ case CD_MLOOPCOL: case CD_PROP_COLOR: { const AttributeRef *render = &me->attr_color_render; @@ -667,15 +692,28 @@ static DRW_MeshCDMask mesh_cd_calc_used_gpu_layers(const Mesh *me, if (layer == -1 && name[0] != '\0') { layer = CustomData_get_named_layer_index(cd_ldata, type, name); + domain = layer != -1 ? ATTR_DOMAIN_CORNER : domain; - if (layer != -1) { - domain = ATTR_DOMAIN_CORNER; - } - else { + if (layer == -1) { layer = CustomData_get_named_layer_index(cd_vdata, type, name); + domain = layer != -1 ? ATTR_DOMAIN_POINT : domain; + } + + if (layer == -1) { + layer = CustomData_get_named_layer_index(cd_ldata, CD_MLOOPCOL, name); + + if (layer != -1) { + domain = ATTR_DOMAIN_CORNER; + type = CD_MLOOPCOL; + } + } + + if (layer == -1) { + layer = CustomData_get_named_layer_index(cd_vdata, CD_MLOOPCOL, name); if (layer != -1) { domain = ATTR_DOMAIN_POINT; + type = CD_MLOOPCOL; } } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index a972c4ff3c6..bba49f3f95a 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -56,6 +56,7 @@ #include "DNA_node_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" +#include "DNA_view3d_types.h" #include "BKE_attribute.h" #include "BKE_brush.h" @@ -2944,6 +2945,8 @@ static int sculpt_brush_needs_normal(const SculptSession *ss, const Brush *brush return ((SCULPT_TOOL_HAS_NORMAL_WEIGHT(SCULPT_get_tool(ss, brush)) && (ss->cache->normal_weight > 0.0f)) || + SCULPT_automasking_needs_normal(ss, brush) || + ELEM(SCULPT_get_tool(ss, brush), SCULPT_TOOL_BLOB, SCULPT_TOOL_CREASE, @@ -8436,6 +8439,13 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f Brush *brush = BKE_paint_brush(&sd->paint); Object *ob = CTX_data_active_object(C); + if (SCULPT_TOOL_NEEDS_COLOR(brush->sculpt_tool)) { + View3D *v3d = CTX_wm_view3d(C); + if (v3d) { + v3d->shading.color_type = V3D_SHADING_VERTEX_COLOR; + } + } + if (brush && brush->channels) { int tool = RNA_enum_get(op->ptr, "tool_override"); BrushChannelSet *channels = brush->channels; diff --git a/source/blender/editors/sculpt_paint/sculpt_automasking.c b/source/blender/editors/sculpt_paint/sculpt_automasking.c index 693a7c81bb1..39dd0b5cb3a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_automasking.c +++ b/source/blender/editors/sculpt_paint/sculpt_automasking.c @@ -142,6 +142,50 @@ static bool SCULPT_automasking_needs_factors_cache(SculptSession *ss, return false; } +bool SCULPT_automasking_needs_normal(const SculptSession *ss, const Brush *brush) +{ + int flags = SCULPT_get_int(ss, automasking, NULL, brush); + + return flags & (BRUSH_AUTOMASKING_BRUSH_NORMAL | BRUSH_AUTOMASKING_VIEW_NORMAL); +} + +static float sculpt_automasking_normal_calc(AutomaskingCache *automasking, + SculptSession *ss, + SculptVertRef vert, + float normal[3], + float limit, + float falloff) +{ + float normal_v[3]; + + if (automasking->settings.original_normal) { + SCULPT_vertex_check_origdata(ss, vert); + copy_v3_v3(normal_v, SCULPT_vertex_origno_get(ss, vert)); + } + else { + SCULPT_vertex_normal_get(ss, vert, normal_v); + } + + float angle = saacos(dot_v3v3(normal, normal_v)) / M_PI; + falloff *= 0.5; + + /* note that limit is pre-divided by M_PI */ + + if (angle > limit - falloff && angle < limit + falloff) { + float t = 1.0f - (angle - (limit - falloff)) / (2.0 * falloff); + + /* smoothstep */ + t = t * t * (3.0 - 2.0 * t); + + return t; + } + else if (angle > limit) { + return 0.0f; + } + + return 1.0f; +} + float SCULPT_automasking_factor_get(AutomaskingCache *automasking, SculptSession *ss, SculptVertRef vert) @@ -195,6 +239,32 @@ float SCULPT_automasking_factor_get(AutomaskingCache *automasking, } } + if (ss->cache && (automasking->settings.flags & BRUSH_AUTOMASKING_BRUSH_NORMAL)) { + float normal[3]; + + copy_v3_v3(normal, ss->cache->initial_normal); + + mask *= sculpt_automasking_normal_calc(automasking, + ss, + vert, + normal, + automasking->settings.normal_limit, + automasking->settings.normal_falloff); + } + + if (ss->cache && (automasking->settings.flags & BRUSH_AUTOMASKING_VIEW_NORMAL)) { + float normal[3]; + + copy_v3_v3(normal, ss->cache->view_normal); + + mask *= sculpt_automasking_normal_calc(automasking, + ss, + vert, + normal, + automasking->settings.view_normal_limit, + automasking->settings.view_normal_falloff); + } + return mask; } @@ -394,6 +464,19 @@ static void SCULPT_automasking_cache_settings_update(AutomaskingCache *automaski automasking->settings.initial_face_set = SCULPT_active_face_set_get(ss); automasking->settings.concave_factor = SCULPT_get_float(ss, concave_mask_factor, sd, brush); + + /* pre-divide by M_PI */ + automasking->settings.normal_limit = SCULPT_get_float(ss, normal_mask_limit, sd, brush) / M_PI; + automasking->settings.normal_falloff = SCULPT_get_float(ss, normal_mask_falloff, sd, brush); + + automasking->settings.view_normal_limit = SCULPT_get_float( + ss, view_normal_mask_limit, sd, brush) / + M_PI; + automasking->settings.view_normal_falloff = SCULPT_get_float( + ss, view_normal_mask_falloff, sd, brush); + + automasking->settings.original_normal = SCULPT_get_bool( + ss, automasking_use_original_normal, sd, brush); } void SCULPT_automasking_step_update(AutomaskingCache *automasking, diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 0d41e5fea7c..8633ac43777 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -577,8 +577,9 @@ void SCULPT_pbvh_clear(Object *ob); float SCULPT_automasking_factor_get(struct AutomaskingCache *automasking, SculptSession *ss, SculptVertRef vert); +bool SCULPT_automasking_needs_normal(const SculptSession *ss, const Brush *brush); -/* Returns the automasking cache depending on the active tool. Used for code that can run both for + /* Returns the automasking cache depending on the active tool. Used for code that can run both for * brushes and filter. */ struct AutomaskingCache *SCULPT_automasking_active_cache_get(SculptSession *ss); @@ -1297,6 +1298,9 @@ typedef struct AutomaskingSettings { int initial_face_set; int current_face_set; // used by faceset draw tool float concave_factor; + float normal_limit, normal_falloff; + float view_normal_limit, view_normal_falloff; + bool original_normal; } AutomaskingSettings; typedef struct AutomaskingCache { @@ -1995,6 +1999,8 @@ void SCULPT_ensure_persistent_layers(SculptSession *ss, struct Object *ob); // these tools don't support dynamic pbvh splitting during the stroke #define DYNTOPO_HAS_DYNAMIC_SPLIT(tool) true +#define SCULPT_TOOL_NEEDS_COLOR(tool) ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) + /*get current symmetry pass index inclusive of both mirror and radial symmetry*/ int SCULPT_get_symmetry_pass(const SculptSession *ss); diff --git a/source/blender/makesdna/DNA_brush_enums.h b/source/blender/makesdna/DNA_brush_enums.h index 37722f72a3f..fec5758cb05 100644 --- a/source/blender/makesdna/DNA_brush_enums.h +++ b/source/blender/makesdna/DNA_brush_enums.h @@ -344,7 +344,9 @@ typedef enum eAutomasking_flag { BRUSH_AUTOMASKING_BOUNDARY_EDGES = (1 << 2), BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS = (1 << 3), BRUSH_AUTOMASKING_CONCAVITY = (1 << 4), - BRUSH_AUTOMASKING_INVERT_CONCAVITY = (1 << 5) + BRUSH_AUTOMASKING_INVERT_CONCAVITY = (1 << 5), + BRUSH_AUTOMASKING_BRUSH_NORMAL = (1<<6), + BRUSH_AUTOMASKING_VIEW_NORMAL = (1 << 7), } eAutomasking_flag; typedef enum ePaintBrush_flag { diff --git a/source/blender/makesdna/DNA_sculpt_brush_types.h b/source/blender/makesdna/DNA_sculpt_brush_types.h index 30d0d25c9ea..98f5a951484 100644 --- a/source/blender/makesdna/DNA_sculpt_brush_types.h +++ b/source/blender/makesdna/DNA_sculpt_brush_types.h @@ -156,6 +156,7 @@ enum { BRUSH_CHANNEL_COLOR, BRUSH_CHANNEL_FACTOR, BRUSH_CHANNEL_PERCENT, - BRUSH_CHANNEL_PIXEL + BRUSH_CHANNEL_PIXEL, + BRUSH_CHANNEL_ANGLE }; /* clang-format on */ diff --git a/source/blender/makesrna/intern/rna_brush_engine.c b/source/blender/makesrna/intern/rna_brush_engine.c index e637d77cd16..b6004d3e376 100644 --- a/source/blender/makesrna/intern/rna_brush_engine.c +++ b/source/blender/makesrna/intern/rna_brush_engine.c @@ -125,6 +125,9 @@ struct StructRNA *rna_BrushChannel_refine(PointerRNA *ptr) case BRUSH_CHANNEL_PIXEL: subtype = PROP_PIXEL; break; + case BRUSH_CHANNEL_ANGLE: + subtype = PROP_ANGLE; + break; default: subtype = PROP_NONE; break;