From 87feba04ddcef58e119eba903fcc7e36cb8e09cf Mon Sep 17 00:00:00 2001 From: Joseph Eagar Date: Sun, 19 Sep 2021 01:02:00 -0700 Subject: [PATCH] Sculpt: more brush engine stuff; do not test. Pushing this commit early due to computer weirdness --- .../startup/bl_ui/properties_paint_common.py | 56 +- source/blender/blenkernel/BKE_brush.h | 9 +- source/blender/blenkernel/BKE_brush_engine.h | 77 +- source/blender/blenkernel/BKE_paint.h | 2 + source/blender/blenkernel/CMakeLists.txt | 1 + source/blender/blenkernel/intern/brush.c | 48 +- .../blender/blenkernel/intern/brush_engine.c | 993 +++++++++++++----- .../blenkernel/intern/brush_engine_presets.c | 423 ++++++++ source/blender/blenkernel/intern/colortools.c | 94 +- source/blender/blenkernel/intern/scene.c | 7 +- .../blenloader/intern/versioning_300.c | 8 + .../editors/sculpt_paint/paint_cursor.c | 40 +- .../editors/sculpt_paint/paint_image.c | 7 +- .../editors/sculpt_paint/paint_image_proj.c | 26 +- .../editors/sculpt_paint/paint_intern.h | 16 +- .../blender/editors/sculpt_paint/paint_ops.c | 8 +- .../editors/sculpt_paint/paint_stroke.c | 11 +- .../editors/sculpt_paint/paint_vertex.c | 6 +- source/blender/editors/sculpt_paint/sculpt.c | 550 +++++++++- .../editors/sculpt_paint/sculpt_intern.h | 8 +- .../editors/sculpt_paint/sculpt_smooth.c | 10 - .../blender/editors/sculpt_paint/sculpt_uv.c | 4 +- source/blender/makesdna/DNA_color_types.h | 3 + .../blender/makesdna/DNA_sculpt_brush_types.h | 5 + source/blender/makesrna/intern/rna_access.c | 2 +- .../makesrna/intern/rna_brush_engine.c | 126 ++- source/blender/python/intern/bpy_rna.c | 16 +- 27 files changed, 2203 insertions(+), 353 deletions(-) create mode 100644 source/blender/blenkernel/intern/brush_engine_presets.c diff --git a/release/scripts/startup/bl_ui/properties_paint_common.py b/release/scripts/startup/bl_ui/properties_paint_common.py index c8d7817e5a7..b011750bd14 100644 --- a/release/scripts/startup/bl_ui/properties_paint_common.py +++ b/release/scripts/startup/bl_ui/properties_paint_common.py @@ -19,6 +19,15 @@ # from bpy.types import Menu +channel_name_map = { + "size" : "RADIUS", + "autosmooth_fset_slide":"FSET_SLIDE", + "auto_smooth_factor": "AUTOSMOOTH", + "auto_smooth_projection": "SMOOTH_PROJECTION", + "auto_smooth_radius_factor": "AUTOSMOOTH_RADIUS_SCALE", + "boundary_smooth_factor": "BOUNDARY_SMOOTH", +}; + class UnifiedPaintPanel: # subclass must set # bl_space_type = 'IMAGE_EDITOR' @@ -105,11 +114,15 @@ class UnifiedPaintPanel: l1 = layout - if ch.ui_expanded: - layout = layout.box().column() #.column() is a bit more compact + #if ch.ui_expanded: + # layout = layout.box().column() #.column() is a bit more compact row = layout.row(align=True) - + + typeprop = "float_value" + if ch.type == "INT": + typeprop = "int_value" + if text is None: s = prop_name.lower().replace("_", " ").split(" "); text = '' @@ -124,7 +137,7 @@ class UnifiedPaintPanel: finalch = sd.channels.channels[prop_name] - row.prop(finalch, "value", icon=icon, text=text, slider=slider) + row.prop(finalch, typeprop, icon=icon, text=text, slider=slider) if pressure: row.prop(finalch.mappings["PRESSURE"], "enabled", text="", icon="STYLUS_PRESSURE") @@ -154,6 +167,15 @@ class UnifiedPaintPanel: if mp.ui_expanded: layout.template_curve_mapping(mp, "curve", brush=True) + + col = layout.column(align=True) + row = col.row(align=True) + row.operator("brush.curve_preset", icon='SMOOTHCURVE', text="").shape = 'SMOOTH' + row.operator("brush.curve_preset", icon='SPHERECURVE', text="").shape = 'ROUND' + row.operator("brush.curve_preset", icon='ROOTCURVE', text="").shape = 'ROOT' + row.operator("brush.curve_preset", icon='SHARPCURVE', text="").shape = 'SHARP' + row.operator("brush.curve_preset", icon='LINCURVE', text="").shape = 'LINE' + row.operator("brush.curve_preset", icon='NOCURVE', text="").shape = 'MAX' #row2.prop(mp, "curve") return row @@ -173,6 +195,16 @@ class UnifiedPaintPanel: ): """ Generalized way of adding brush options to the UI, along with their pen pressure setting and global toggle, if they exist. """ + + if prop_name in channel_name_map: + prop_name = channel_name_map[prop_name] + else: + prop_name = prop_name.upper() + + if prop_name in brush.channels.channels: + # def channel_unified(layout, context, brush, prop_name, icon='NONE', pressure=True, text=None, slider=False, header=False): + return UnifiedPaintPanel.channel_unified(layout, context, brush, prop_name, icon=icon, text=text, slider=slider, header=header) + row = layout.row(align=True) ups = context.tool_settings.unified_paint_settings prop_owner = brush @@ -1017,7 +1049,21 @@ def brush_shared_settings(layout, context, brush, popover=False): size_prop = "size" if size_mode and (size_owner.use_locked_size == 'SCENE'): size_prop = "unprojected_radius" + if size or size_mode: + if size: + UnifiedPaintPanel.channel_unified( + layout, + context, + brush, + "RADIUS" if size_prop == "size" else size_prop.upper(), + text="Radius", + slider=True, + ) + if size_mode: + layout.row().prop(size_owner, "use_locked_size", expand=True) + layout.separator() + elif size or size_mode: if size: UnifiedPaintPanel.prop_unified( layout, @@ -1033,7 +1079,7 @@ def brush_shared_settings(layout, context, brush, popover=False): layout.row().prop(size_owner, "use_locked_size", expand=True) layout.separator() - if 0 and strength: + if strength: UnifiedPaintPanel.channel_unified( layout, context, diff --git a/source/blender/blenkernel/BKE_brush.h b/source/blender/blenkernel/BKE_brush.h index 729956bdbc5..795f220871b 100644 --- a/source/blender/blenkernel/BKE_brush.h +++ b/source/blender/blenkernel/BKE_brush.h @@ -110,8 +110,13 @@ const float *BKE_brush_color_get(const struct Scene *scene, const struct Brush * const float *BKE_brush_secondary_color_get(const struct Scene *scene, const struct Brush *brush); void BKE_brush_color_set(struct Scene *scene, struct Brush *brush, const float color[3]); -int BKE_brush_size_get(const struct Scene *scene, const struct Brush *brush); -void BKE_brush_size_set(struct Scene *scene, struct Brush *brush, int size); +int BKE_brush_size_get(const struct Scene *scene, + const struct Brush *brush, + bool use_brush_channel); +void BKE_brush_size_set(struct Scene *scene, + struct Brush *brush, + int size, + bool use_brush_channel); float BKE_brush_unprojected_radius_get(const struct Scene *scene, const struct Brush *brush); void BKE_brush_unprojected_radius_set(struct Scene *scene, diff --git a/source/blender/blenkernel/BKE_brush_engine.h b/source/blender/blenkernel/BKE_brush_engine.h index 6b038f6d063..92e5e33ba02 100644 --- a/source/blender/blenkernel/BKE_brush_engine.h +++ b/source/blender/blenkernel/BKE_brush_engine.h @@ -44,17 +44,21 @@ is controller via BrushChannel->flag. This should completely replace UnifiedPaintSettings. */ -struct BrushChannel; -#include "BLO_read_write.h" #include "DNA_sculpt_brush_types.h" +struct BrushChannel; +struct BlendWriter; +struct BlendDataReader; +struct Brush; + typedef struct BrushMappingDef { int curve; bool enabled; bool inv; float min, max; int blendmode; + float factor; // if 0, will default to 1.0 } BrushMappingDef; typedef struct BrushMappingPreset { @@ -62,6 +66,10 @@ typedef struct BrushMappingPreset { struct BrushMappingDef pressure, xtilt, ytilt, angle, speed; } BrushMappingPreset; +typedef struct BrushMappingData { + float pressure, xtilt, ytilt, angle, speed; +} BrushMappingData; + #define MAX_BRUSH_ENUM_DEF 32 typedef struct BrushEnumDef { @@ -69,7 +77,7 @@ typedef struct BrushEnumDef { } BrushEnumDef; typedef struct BrushChannelType { - char name[32], idname[32]; + char name[32], idname[32], tooltip[512]; float min, max, soft_min, soft_max; BrushMappingPreset mappings; @@ -83,7 +91,6 @@ typedef struct BrushCommand { int tool; struct BrushChannelSet *params; struct BrushChannelSet *params_final; - int totparam; } BrushCommand; typedef struct BrushCommandList { @@ -117,22 +124,70 @@ void BKE_brush_channelset_merge(BrushChannelSet *dst, BrushChannelSet *parent); void BKE_brush_resolve_channels(struct Brush *brush, struct Sculpt *sd); -int BKE_brush_channel_get_int(BrushChannelSet *chset, char *idname); -float BKE_brush_channel_get_float(BrushChannelSet *chset, char *idname); -float BKE_brush_channel_set_float(BrushChannelSet *chset, char *idname, float val); +int BKE_brush_channelset_get_int(BrushChannelSet *chset, char *idname); + +// mapdata is optional, can be NULL + +float BKE_brush_channel_get_final_float(BrushChannelSet *brushset, + BrushChannelSet *toolset, + char *idname, + BrushMappingData *mapdata); +void BKE_brush_channel_set_final_float(BrushChannelSet *brushset, + BrushChannelSet *toolset, + char *idname, + float value); + +/* mapdata may be NULL */ +float BKE_brush_channel_get_float(BrushChannel *ch, BrushMappingData *mapdata); +void BKE_brush_channel_set_float(BrushChannel *ch, float val); + +/* mapdata may be NULL */ +float BKE_brush_channelset_get_float(BrushChannelSet *chset, + char *idname, + BrushMappingData *mapdata); +bool BKE_brush_channelset_set_float(BrushChannelSet *chset, char *idname, float val); + +float BKE_brush_channelset_get_final_float(BrushChannelSet *child, + BrushChannelSet *parent, + char *idname, + BrushMappingData *mapdata); + +void BKE_brush_channelset_set_final_float(BrushChannelSet *child, + BrushChannelSet *parent, + char *idname, + float value); + void BKE_brush_init_toolsettings(struct Sculpt *sd); void BKE_brush_builtin_create(struct Brush *brush, int tool); BrushCommandList *BKE_brush_commandlist_create(); void BKE_brush_commandlist_free(BrushCommandList *cl); -BrushCommand *BKE_brush_commandlist_add(BrushCommandList *cl); +BrushCommand *BKE_brush_commandlist_add(BrushCommandList *cl, + BrushChannelSet *chset_template, + bool auto_inherit); BrushCommand *BKE_brush_command_init(BrushCommand *command, int tool); -void BKE_builtin_commandlist_create(BrushChannelSet *chset, BrushCommandList *cl, int tool); -void BKE_brush_channelset_read(BlendDataReader *reader, BrushChannelSet *cset); -void BKE_brush_channelset_write(BlendWriter *writer, BrushChannelSet *cset); +void BKE_builtin_commandlist_create(struct Brush *brush, + BrushChannelSet *chset, + BrushCommandList *cl, + int tool, + BrushMappingData *map_data); // map_data may be NULL +void BKE_brush_channelset_read(struct BlendDataReader *reader, BrushChannelSet *cset); +void BKE_brush_channelset_write(struct BlendWriter *writer, BrushChannelSet *cset); void BKE_brush_mapping_copy_data(BrushMapping *dst, BrushMapping *src); const char *BKE_brush_mapping_type_to_str(BrushMappingType mapping); const char *BKE_brush_mapping_type_to_typename(BrushMappingType mapping); +void BKE_brush_channelset_flag_clear(BrushChannelSet *chset, const char *channel, int flag); +void BKE_brush_channelset_flag_set(BrushChannelSet *chset, const char *channel, int flag); + +/* adds missing channels to exising .channels in brush. + * if channels do not exist use BKE_brush_builtin_create. + */ +void BKE_brush_builtin_patch(struct Brush *brush, int tool); + +void BKE_brush_channelset_compat_load(BrushChannelSet *chset, + struct Brush *brush, + bool to_channels); + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_paint.h b/source/blender/blenkernel/BKE_paint.h index c207c816515..9949408d77b 100644 --- a/source/blender/blenkernel/BKE_paint.h +++ b/source/blender/blenkernel/BKE_paint.h @@ -23,7 +23,9 @@ * \ingroup bke */ +#include "BKE_brush_engine.h" #include "BKE_pbvh.h" + #include "BLI_bitmap.h" #include "BLI_utildefines.h" #include "DNA_brush_enums.h" diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index c16c8eee576..f08b52cf1e5 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -290,6 +290,7 @@ set(SRC intern/world.c intern/writeavi.c intern/brush_engine.c + intern/brush_engine_presets.c BKE_DerivedMesh.h BKE_action.h diff --git a/source/blender/blenkernel/intern/brush.c b/source/blender/blenkernel/intern/brush.c index 72e1a1fcce3..e2e33db0fd8 100644 --- a/source/blender/blenkernel/intern/brush.c +++ b/source/blender/blenkernel/intern/brush.c @@ -143,6 +143,10 @@ static void brush_free_data(ID *id) MEM_SAFE_FREE(brush->gradient); BKE_previewimg_free(&(brush->preview)); + + if (brush->channels) { + BKE_brush_channelset_free(brush->channels); + } } static void brush_make_local(Main *bmain, ID *id, const int flags) @@ -312,6 +316,7 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id) if (brush->channels) { BLO_read_data_address(reader, &brush->channels); BKE_brush_channelset_read(reader, brush->channels); + BKE_brush_builtin_patch(brush, brush->sculpt_tool); } else { BKE_brush_builtin_create(brush, brush->sculpt_tool); @@ -1792,6 +1797,16 @@ void BKE_brush_sculpt_reset(Brush *br) * settings used by a brush: */ // BKE_brush_debug_print_state(br); + BKE_brush_builtin_create(br, br->sculpt_tool); + + for (int i = 0; i < br->channels->totchannel; i++) { + BrushChannel *ch = br->channels->channels + i; + BrushChannelType *def = ch->def; + + BKE_brush_channel_free(ch); + BKE_brush_channel_init(ch, def); + } + brush_defaults(br); BKE_brush_curve_preset(br, CURVE_PRESET_SMOOTH); BKE_brush_default_input_curves_set(br); @@ -1805,7 +1820,7 @@ void BKE_brush_sculpt_reset(Brush *br) bool disable_dyntopo = false; - // basic face set setup for all organic brushes + // XXX basic face set setup for all organic brushes br->autosmooth_fset_slide = 1.0f; br->flag2 |= BRUSH_SMOOTH_PRESERVE_FACE_SETS | BRUSH_SMOOTH_USE_AREA_WEIGHT; @@ -2245,7 +2260,7 @@ float BKE_brush_sample_tex_3d(const Scene *scene, /* leave the coordinates relative to the screen */ /* use unadjusted size for tiled mode */ - invradius = 1.0f / BKE_brush_size_get(scene, br); + invradius = 1.0f / BKE_brush_size_get(scene, br, false); x = point_2d[0]; y = point_2d[1]; @@ -2358,7 +2373,7 @@ float BKE_brush_sample_masktex( /* leave the coordinates relative to the screen */ /* use unadjusted size for tiled mode */ - invradius = 1.0f / BKE_brush_size_get(scene, br); + invradius = 1.0f / BKE_brush_size_get(scene, br, false); x = point_2d[0]; y = point_2d[1]; @@ -2448,8 +2463,19 @@ void BKE_brush_color_set(struct Scene *scene, struct Brush *brush, const float c } } -void BKE_brush_size_set(Scene *scene, Brush *brush, int size) +void BKE_brush_size_set(Scene *scene, Brush *brush, int size, bool use_brush_channels) { + if (use_brush_channels) { + if (scene->toolsettings->sculpt && scene->toolsettings->sculpt->channels) { + BKE_brush_channelset_set_final_float( + brush->channels, scene->toolsettings->sculpt->channels, "RADIUS", (float)size); + return; + } + else { + BKE_brush_channelset_set_float(brush->channels, "RADIUS", (float)size); + } + } + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; /* make sure range is sane */ @@ -2463,8 +2489,18 @@ void BKE_brush_size_set(Scene *scene, Brush *brush, int size) } } -int BKE_brush_size_get(const Scene *scene, const Brush *brush) +int BKE_brush_size_get(const Scene *scene, const Brush *brush, bool use_brush_channel) { + if (use_brush_channel) { + if (scene->toolsettings->sculpt) { + return (int)BKE_brush_channelset_get_final_float( + brush->channels, scene->toolsettings->sculpt->channels, "RADIUS", NULL); + } + else { + return (int)BKE_brush_channelset_get_float(brush->channels, "RADIUS", NULL); + } + } + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; int size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size; @@ -2607,7 +2643,7 @@ void BKE_brush_jitter_pos(const Scene *scene, Brush *brush, const float pos[2], spread = 1.0; } else { - diameter = 2 * BKE_brush_size_get(scene, brush); + diameter = 2 * BKE_brush_size_get(scene, brush, false); spread = brush->jitter; } /* find random position within a circle of diameter 1 */ diff --git a/source/blender/blenkernel/intern/brush_engine.c b/source/blender/blenkernel/intern/brush_engine.c index f35bd0d56dc..da465140d52 100644 --- a/source/blender/blenkernel/intern/brush_engine.c +++ b/source/blender/blenkernel/intern/brush_engine.c @@ -5,10 +5,13 @@ #include "BLI_bitmap.h" #include "BLI_compiler_attrs.h" #include "BLI_compiler_compat.h" +#include "BLI_ghash.h" #include "BLI_listbase.h" #include "BLI_math.h" #include "BLI_memarena.h" +#include "BLI_mempool.h" #include "BLI_rect.h" +#include "BLI_smallhash.h" #include "DNA_brush_enums.h" #include "DNA_brush_types.h" @@ -30,7 +33,31 @@ #include "BLO_read_write.h" -#define ICON_NONE -1 +static struct { + char tag[4192]; +} namestack[256] = {0}; +int namestack_i = 1; + +ATTR_NO_OPT void namestack_push(const char *name) +{ + namestack_i++; + + strcpy(namestack[namestack_i].tag, namestack[namestack_i - 1].tag); + strcat(namestack[namestack_i].tag, "."); + strcat(namestack[namestack_i].tag, name); +} +void namestack_pop() +{ + namestack_i--; +} +#define namestack_head_name strdup(namestack[namestack_i].tag) + +void BKE_curvemapping_copy_data_tag_ex(CurveMapping *target, + const CurveMapping *cumap, + const char *tag); + +#define BKE_curvemapping_copy_data(dst, src) \ + BKE_curvemapping_copy_data_tag_ex(dst, src, namestack_head_name) static bool check_corrupted_curve(BrushMapping *dst) { @@ -42,6 +69,8 @@ static bool check_corrupted_curve(BrushMapping *dst) if (clip_size_x == 0.0f || clip_size_y == 0.0f) { for (int i = 0; i < 4; i++) { BKE_curvemapping_free_data(&dst->curve); + memset(&dst->curve, 0, sizeof(CurveMapping)); + BKE_curvemapping_set_defaults(&dst->curve, 1, 0.0, 0.0, 1.0, 1.0); BKE_curvemap_reset(&dst->curve.cm[i], @@ -68,177 +97,8 @@ networks. BrushCommandPreset will be generated from the node group inputs. */ -/* clang-format off */ -BrushChannelType brush_builtin_channels[] = { - { - .name = "Radius", - .idname = "RADIUS", - .min = 0.001f, - .type = BRUSH_CHANNEL_FLOAT, - .max = 2048.0f, - .fvalue = 50.0f, - .soft_min = 0.1f, - .soft_max = 1024.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false}, - } - }, - { - .name = "Strength", - .idname = "STRENGTH", - .min = -1.0f, - .type = BRUSH_CHANNEL_FLOAT, - .max = 4.0f, - .fvalue = 0.5f, - .soft_min = 0.0f, - .soft_max = 1.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = true}, - } - }, - { - .name = "Spacing", - .idname = "SPACING", - .min = 0.001f, - .type = BRUSH_CHANNEL_FLOAT, - .max = 4.0f, - .fvalue = 0.1f, - .soft_min = 0.005f, - .soft_max = 2.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = true}, - } - }, - { - .name = "Autosmooth", - .idname = "AUTOSMOOTH", - .type = BRUSH_CHANNEL_FLOAT, - .min = -1.0f, - .max = 4.0f, - .soft_min = 0.0f, - .soft_max = 1.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false, .inv = true}, - } - }, - { - .name = "Topology Rake", - .idname = "TOPOLOGY_RAKE", - .type = BRUSH_CHANNEL_FLOAT, - .min = -1.0f, - .max = 4.0f, - .soft_min = 0.0f, - .soft_max = 1.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false}, - } - }, - { - .name = "Autosmooth Radius Scale", - .idname = "AUTOSMOOTH_RADIUS_SCALE", - .type = BRUSH_CHANNEL_FLOAT, - .min = 0.0001f, - .max = 25.0f, - .fvalue = 1.0f, - .soft_min = 0.1f, - .soft_max = 4.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false}, - } - }, - { - .name = "Rake Radius Scale", - .idname = "TOPOLOGY_RAKE_RADIUS_SCALE", - .type = BRUSH_CHANNEL_FLOAT, - .min = 0.0001f, - .max = 25.0f, - .fvalue = 1.0f, - .soft_min = 0.1f, - .soft_max = 4.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false}, - } - }, - { - .name = "Face Set Slide", - .idname = "FSET_SLIDE", - .type = BRUSH_CHANNEL_FLOAT, - .min = 0.0001f, - .max = 1.0f, - .fvalue = 1.0f, - .soft_min = 0.1f, - .soft_max = 1.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false}, - } - }, - { - .name = "Boundary Smooth", - .idname = "BOUNDARY_SMOOTH", - .type = BRUSH_CHANNEL_FLOAT, - .min = 0.0001f, - .max = 1.0f, - .soft_min = 0.1f, - .soft_max = 1.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false}, - } - }, - { - .name = "Projection", - .idname = "PROJECTION", - .type = BRUSH_CHANNEL_FLOAT, - .min = 0.0001f, - .max = 1.0f, - .soft_min = 0.1f, - .soft_max = 1.0f, - .mappings = { - .pressure = {.curve = CURVE_PRESET_SMOOTH, .min = 0.0f, .max = 1.0f, .enabled = false}, - } - }, - { - .name = "Topology Rake Mode", - .idname = "TOPOLOGY_RAKE_MODE", - .type = BRUSH_CHANNEL_ENUM, - .enumdef = {.items = { - {0, "BRUSH_DIRECTION", ICON_NONE, "Stroke", "Stroke Direction"}, - {1, "CURVATURE", ICON_NONE, "Curvature", "Follow mesh curvature"}, - {-1, 0} - }} - }, - { - .name = "Automasking", - .idname = "AUTOMASKING", - .flag = BRUSH_CHANNEL_INHERIT_IF_UNSET | BRUSH_CHANNEL_INHERIT, - .type = BRUSH_CHANNEL_BITMASK, - .enumdef = {.items = { - {BRUSH_AUTOMASKING_BOUNDARY_EDGES, "BOUNDARY_EDGE", ICON_NONE, "Boundary Edges", ""}, - {BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS, "BOUNDARY_FACE_SETS", ICON_NONE, "Boundary Face Sets", ""}, - {BRUSH_AUTOMASKING_CONCAVITY, "CONCAVITY", ICON_NONE, "Concave", ""}, - {BRUSH_AUTOMASKING_INVERT_CONCAVITY, "INVERT_CONCAVITY", ICON_NONE, "Invert Concave", "Invert Concave Map"}, - {BRUSH_AUTOMASKING_FACE_SETS, "FACE_SETS", ICON_NONE, "Face Sets", ""}, - {BRUSH_AUTOMASKING_TOPOLOGY, "TOPOLOGY", ICON_NONE, "Topology", ""} - }} - }, - { - .name = "Disable Dyntopo", - .idname = "DYNTOPO_DISABLED", - .type = BRUSH_CHANNEL_INT, - .flag = BRUSH_CHANNEL_NO_MAPPINGS, - .ivalue = 0 - }, - { - .name = "Detail Range", - .idname = "DYNTOPO_DETAIL_RANGE", - .type = BRUSH_CHANNEL_FLOAT, - .min = 0.001, - .max = 0.99, - .ivalue = 0 - }, -}; - -/* clang-format on */ -const int builtin_channel_len = ARRAY_SIZE(brush_builtin_channels); +extern BrushChannelType brush_builtin_channels[]; +extern const int brush_builtin_channel_len; void BKE_brush_channel_free(BrushChannel *ch) { @@ -249,12 +109,28 @@ void BKE_brush_channel_free(BrushChannel *ch) ATTR_NO_OPT void BKE_brush_channel_copy_data(BrushChannel *dst, BrushChannel *src) { + // we have to free old curvemappings here, + // before the *dst = *src, otherwise the new + // curves will be freed in BKE_brush_mapping_copy_data + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BKE_curvemapping_free_data(&dst->mappings[i].curve); + } + *dst = *src; + // clear curves in dst, see comment above + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + memset(&dst->mappings[i].curve, 0, sizeof(CurveMapping)); + } + + namestack_push(__func__); + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { BKE_brush_mapping_copy_data(dst->mappings + i, src->mappings + i); dst->mappings[i].type = i; } + + namestack_pop(); } ATTR_NO_OPT void BKE_brush_channel_init(BrushChannel *ch, BrushChannelType *def) @@ -268,6 +144,7 @@ ATTR_NO_OPT void BKE_brush_channel_init(BrushChannel *ch, BrushChannelType *def) ch->fvalue = def->fvalue; ch->ivalue = def->ivalue; + ch->type = def->type; ch->def = def; for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { @@ -276,6 +153,7 @@ ATTR_NO_OPT void BKE_brush_channel_init(BrushChannel *ch, BrushChannelType *def) map->type = i; + BKE_curvemapping_free_data(curve); memset(curve, 0, sizeof(*curve)); float min, max; @@ -309,7 +187,7 @@ ATTR_NO_OPT void BKE_brush_channel_init(BrushChannel *ch, BrushChannelType *def) BKE_curvemapping_init(curve); map->blendmode = mdef->blendmode; - map->factor = 1.0f; + map->factor = mdef->factor == 0.0f ? 1.0f : mdef->factor; if (mdef->enabled) { map->flag |= BRUSH_MAPPING_ENABLED; @@ -342,10 +220,13 @@ void BKE_brush_channelset_add(BrushChannelSet *chset, BrushChannel *ch) chset->channels = MEM_callocN(sizeof(BrushChannel) * chset->totchannel, "chset->channels"); } else { - chset->channels = MEM_recallocN(chset->channels, sizeof(BrushChannel) * chset->totchannel); + chset->channels = MEM_recallocN_id( + chset->channels, sizeof(BrushChannel) * chset->totchannel, "chset->channels"); } + namestack_push(__func__); BKE_brush_channel_copy_data(chset->channels + chset->totchannel - 1, ch); + namestack_pop(); } ATTR_NO_OPT BrushChannel *BKE_brush_channelset_lookup(BrushChannelSet *chset, const char *idname) @@ -392,7 +273,7 @@ void BKE_brush_channel_def_copy(BrushChannelType *dst, BrushChannelType *src) ATTR_NO_OPT BrushChannelType *BKE_brush_builtin_channel_def_find(const char *name) { - for (int i = 0; i < builtin_channel_len; i++) { + for (int i = 0; i < brush_builtin_channel_len; i++) { BrushChannelType *def = brush_builtin_channels + i; if (STREQ(def->idname, name)) { @@ -412,19 +293,28 @@ ATTR_NO_OPT void BKE_brush_channelset_add_builtin(BrushChannelSet *chset, const return; } - BrushChannel ch; + BrushChannel ch = {0}; + + namestack_push(__func__); BKE_brush_channel_init(&ch, def); BKE_brush_channelset_add(chset, &ch); + BKE_brush_channel_free(&ch); + + namestack_pop(); } bool BKE_brush_channelset_ensure_builtin(BrushChannelSet *chset, const char *idname) { + namestack_push(__func__); + if (!BKE_brush_channelset_has(chset, idname)) { BKE_brush_channelset_add_builtin(chset, idname); + namestack_pop(); return true; } + namestack_pop(); return false; } @@ -434,42 +324,55 @@ void BKE_brush_channelset_ensure_existing(BrushChannelSet *chset, BrushChannel * return; } + namestack_push(__func__); BKE_brush_channelset_add(chset, existing); + namestack_pop(); } #define ADDCH(name) BKE_brush_channelset_ensure_builtin(chset, name) #define GETCH(name) BKE_brush_channelset_lookup(chset, name) -void BKE_brush_channelset_merge(BrushChannelSet *dst, - BrushChannelSet *child, - BrushChannelSet *parent) +ATTR_NO_OPT void BKE_brush_channelset_merge(BrushChannelSet *dst, + BrushChannelSet *child, + BrushChannelSet *parent) { // first add missing channels + namestack_push(__func__); for (int step = 0; step < 2; step++) { BrushChannelSet *chset = step ? parent : child; for (int i = 0; i < chset->totchannel; i++) { BrushChannel *ch = chset->channels + i; + BrushChannel *ch2 = BKE_brush_channelset_lookup(dst, ch->idname); - if (BKE_brush_channelset_has(dst, ch->idname)) { + if (ch2 && step > 0) { continue; } - BrushChannel ch2; - BKE_brush_channel_copy_data(&ch2, ch); - BKE_brush_channelset_add(chset, &ch2); + if (!ch2) { + BrushChannel ch3 = {0}; + + BKE_brush_channel_copy_data(&ch3, ch); + BKE_brush_channelset_add(dst, &ch3); + + BKE_brush_channel_free(&ch3); + } + else { + BKE_brush_channel_copy_data(ch2, ch); + } } } - for (int i = 0; i < child->totchannel; i++) { - BrushChannel *ch = child->channels + i; - BrushChannel *mch = BKE_brush_channelset_lookup(dst, ch->idname); - BrushChannel *pch = BKE_brush_channelset_lookup(parent, ch->name); + for (int i = 0; i < parent->totchannel; i++) { + BrushChannel *pch = parent->channels + i; + BrushChannel *mch = BKE_brush_channelset_lookup(dst, pch->idname); + BrushChannel *ch = BKE_brush_channelset_lookup(child, pch->idname); - bool ok = ch->flag & BRUSH_CHANNEL_INHERIT; + if (!ch) { + continue; + } if (ch->flag & BRUSH_CHANNEL_INHERIT) { - BKE_brush_channel_free(mch); BKE_brush_channel_copy_data(mch, pch); continue; } @@ -478,6 +381,8 @@ void BKE_brush_channelset_merge(BrushChannelSet *dst, mch->ivalue = ch->ivalue | pch->ivalue; } } + + namestack_pop(); } BrushChannelSet *BKE_brush_channelset_copy(BrushChannelSet *src) @@ -490,6 +395,8 @@ BrushChannelSet *BKE_brush_channelset_copy(BrushChannelSet *src) return chset; } + namestack_push(__func__); + chset->channels = MEM_calloc_arrayN( src->totchannel, sizeof(BrushChannel), "chset->channels copied"); @@ -499,9 +406,28 @@ BrushChannelSet *BKE_brush_channelset_copy(BrushChannelSet *src) BKE_brush_channel_copy_data(ch, src->channels + i); } + namestack_pop(); + return chset; } +void BKE_brush_commandlist_start(BrushCommandList *list, + Brush *brush, + BrushChannelSet *chset_final) +{ + for (int i = 0; i < list->totcommand; i++) { + BrushCommand *cmd = list->commands + i; + + // Build final list of command parameters + if (cmd->params_final) { + BKE_brush_channelset_free(cmd->params_final); + } + cmd->params_final = BKE_brush_channelset_create(); + + BKE_brush_channelset_merge(cmd->params_final, cmd->params, chset_final); + } +} + void BKE_brush_resolve_channels(Brush *brush, Sculpt *sd) { if (brush->channels_final) { @@ -511,26 +437,9 @@ void BKE_brush_resolve_channels(Brush *brush, Sculpt *sd) brush->channels_final = BKE_brush_channelset_create(); BKE_brush_channelset_merge(brush->channels_final, brush->channels, sd->channels); - - if (!brush->commandlist) { - return; - } - - BrushCommandList *cl = brush->commandlist; - - for (int i = 0; i < cl->totcommand; i++) { - BrushCommand *command = cl->commands + i; - - if (command->params_final) { - BKE_brush_channelset_free(command->params_final); - } - - command->params_final = BKE_brush_channelset_create(); - BKE_brush_channelset_merge(command->params_final, command->params, brush->channels_final); - } } -int BKE_brush_channel_get_int(BrushChannelSet *chset, char *idname) +int BKE_brush_channelset_get_int(BrushChannelSet *chset, char *idname) { BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname); @@ -542,19 +451,122 @@ int BKE_brush_channel_get_int(BrushChannelSet *chset, char *idname) return ch->ivalue; } -float BKE_brush_channel_get_float(BrushChannelSet *chset, char *idname) +float BKE_brush_channelset_get_final_float(BrushChannelSet *brushset, + BrushChannelSet *toolset, + char *idname, + BrushMappingData *mapdata) { - BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname); + BrushChannel *ch = BKE_brush_channelset_lookup(brushset, idname); - if (!ch) { - printf("%s, unknown channel %s", __func__, idname); - return 0; + if (!ch || (ch->flag & BRUSH_CHANNEL_INHERIT)) { + BrushChannel *pch = BKE_brush_channelset_lookup(toolset, idname); + + if (pch) { + return BKE_brush_channel_get_float(pch, mapdata); + } } - return ch->fvalue; + if (ch) { + return BKE_brush_channel_get_float(ch, mapdata); + } + + printf("%s: failed to find brush channel %s\n", __func__, idname); + + return 0.0f; } -float BKE_brush_channel_set_float(BrushChannelSet *chset, char *idname, float val) +void BKE_brush_channelset_set_final_float(BrushChannelSet *brushset, + BrushChannelSet *toolset, + char *idname, + float value) +{ + BrushChannel *ch = BKE_brush_channelset_lookup(brushset, idname); + + if (!ch || (ch->flag & BRUSH_CHANNEL_INHERIT)) { + BrushChannel *pch = BKE_brush_channelset_lookup(toolset, idname); + + if (pch) { + BKE_brush_channel_set_float(pch, value); + return; + } + } + + if (!ch) { + printf("%s: failed to find brush channel %s\n", __func__, idname); + return; + } + + BKE_brush_channel_set_float(ch, value); +} + +float BKE_brush_channelset_get_float(BrushChannelSet *chset, + char *idname, + BrushMappingData *mapdata) +{ + BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname); + + if (!ch) { + printf("%s, unknown channel %s", __func__, idname); + return 0.0f; + } + + return BKE_brush_channel_get_float(ch, mapdata); +} + +float BKE_brush_channel_get_float(BrushChannel *ch, BrushMappingData *mapdata) +{ + + float f = ch->fvalue; + + if (mapdata) { + float map = f; + + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BrushMapping *mp = ch->mappings + i; + + if (!(mp->flag & BRUSH_MAPPING_ENABLED)) { + continue; + } + + float f2 = BKE_curvemapping_evaluateF(&mp->curve, 0, f); + switch (mp->blendmode) { + case MA_RAMP_BLEND: + break; + case MA_RAMP_MULT: + f2 *= f; + break; + case MA_RAMP_DIV: + f2 = f / (0.00001f + f); + break; + case MA_RAMP_ADD: + f2 += f; + break; + case MA_RAMP_SUB: + f2 = f - f2; + break; + case MA_RAMP_DIFF: + f2 = fabsf(f - f2); + break; + default: + printf("Unsupported brush mapping blend mode for %s (%s); will mix instead\n", + ch->name, + ch->idname); + break; + } + + f += (f2 - f) * mp->factor; + } + } + + return f; +} + +void BKE_brush_channel_set_float(BrushChannel *ch, float val) +{ + ch->fvalue = val; +} + +bool BKE_brush_channelset_set_float(BrushChannelSet *chset, char *idname, float val) { BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname); @@ -563,56 +575,416 @@ float BKE_brush_channel_set_float(BrushChannelSet *chset, char *idname, float va return 0; } - float old = ch->fvalue; + BKE_brush_channel_set_float(ch, val); - ch->fvalue = val; - - return old; + return true; } void BKE_brush_init_toolsettings(Sculpt *sd) { + namestack_push(__func__); + + if (sd->channels) { + BKE_brush_channelset_free(sd->channels); + } + BrushChannelSet *chset = sd->channels = BKE_brush_channelset_create(); ADDCH("RADIUS"); ADDCH("STRENGTH"); ADDCH("AUTOMASKING"); + ADDCH("TOPOLOGY_RAKE_MODE"); ADDCH("DYNTOPO_DISABLED"); ADDCH("DYNTOPO_DETAIL_RANGE"); + + namestack_pop(); +} + +void BKE_brush_channelset_flag_clear(BrushChannelSet *chset, const char *channel, int flag) +{ + BrushChannel *ch = BKE_brush_channelset_lookup(chset, channel); + + if (!ch) { + printf("%s: unknown channel '%s'\n", __func__, channel); + return; + } + + ch->flag &= ~flag; +} + +void BKE_brush_channelset_flag_set(BrushChannelSet *chset, const char *channel, int flag) +{ + BrushChannel *ch = BKE_brush_channelset_lookup(chset, channel); + + if (!ch) { + printf("%s: unknown channel '%s'\n", __func__, channel); + return; + } + + ch->flag |= flag; +} + +// adds any missing channels to brushes +void BKE_brush_builtin_patch(Brush *brush, int tool) +{ + namestack_push(__func__); + + if (!brush->channels) { + brush->channels = BKE_brush_channelset_create(); + } + + BrushChannelSet *chset = brush->channels; + + ADDCH("RADIUS"); + ADDCH("SPACING"); + ADDCH("STRENGTH"); + + ADDCH("AUTOSMOOTH"); + ADDCH("AUTOSMOOTH_RADIUS_SCALE"); + ADDCH("AUTOSMOOTH_SPACING"); + ADDCH("AUTOSMOOTH_USE_SPACING"); + ADDCH("AUTOSMOOTH_PROJECTION"); + + ADDCH("TOPOLOGY_RAKE"); + ADDCH("TOPOLOGY_RAKE_MODE"); + ADDCH("TOPOLOGY_RAKE_RADIUS_SCALE"); + ADDCH("TOPOLOGY_RAKE_USE_SPACING"); + ADDCH("TOPOLOGY_RAKE_SPACING"); + ADDCH("TOPOLOGY_RAKE_PROJECTION"); + + ADDCH("HARDNESS"); + ADDCH("TIP_ROUNDNESS"); + ADDCH("NORMAL_RADIUS_FACTOR"); + + ADDCH("AUTOMASKING"); + + ADDCH("DYNTOPO_DISABLED"); + ADDCH("DYNTOPO_DETAIL_RANGE"); + ADDCH("DYNTOPO_OPS"); + + ADDCH("ACCUMULATE"); + ADDCH("ORIGINAL_NORMAL"); + ADDCH("ORIGINAL_PLANE"); + ADDCH("JITTER"); + ADDCH("JITTER_ABSOLUTE"); + + switch (tool) { + case SCULPT_TOOL_DRAW: { + break; + } + case SCULPT_TOOL_SLIDE_RELAX: + ADDCH("SLIDE_DEFORM_TYPE"); + break; + } + + namestack_pop(); } void BKE_brush_builtin_create(Brush *brush, int tool) { - if (brush->channels) { - BKE_brush_channelset_free(brush->channels); + namestack_push(__func__); + + if (!brush->channels) { + brush->channels = BKE_brush_channelset_create(); } - BrushChannelSet *chset = brush->channels = BKE_brush_channelset_create(); + BrushChannelSet *chset = brush->channels; - ADDCH("RADIUS"); - ADDCH("STRENGTH"); - ADDCH("AUTOSMOOTH"); - ADDCH("TOPOLOGY_RAKE"); - ADDCH("AUTOSMOOTH_RADIUS_SCALE"); - ADDCH("TOPOLOGY_RAKE_RADIUS_SCALE"); + BKE_brush_builtin_patch(brush, tool); - ADDCH("AUTOMASKING"); + GETCH("STRENGTH")->flag |= BRUSH_CHANNEL_INHERIT; + GETCH("RADIUS")->flag |= BRUSH_CHANNEL_INHERIT; switch (tool) { case SCULPT_TOOL_DRAW: { - BrushChannel *ch = GETCH("STRENGTH"); + break; + } + case SCULPT_TOOL_DRAW_SHARP: + GETCH("SPACING")->ivalue = 5; + GETCH("RADIUS")->mappings[BRUSH_MAPPING_PRESSURE].blendmode = true; + break; + case SCULPT_TOOL_DISPLACEMENT_ERASER: + case SCULPT_TOOL_FAIRING: + case SCULPT_TOOL_SCENE_PROJECT: + GETCH("SPACING")->ivalue = 10; + GETCH("STRENGTH")->fvalue = 1.0f; + GETCH("DYNTOPO_DISABLED")->ivalue = 1; + break; + case SCULPT_TOOL_SLIDE_RELAX: + GETCH("SPACING")->ivalue = 10; + GETCH("STRENGTH")->fvalue = 1.0f; + GETCH("DYNTOPO_DISABLED")->ivalue = 1; + GETCH("SLIDE_DEFORM_TYPE")->ivalue = BRUSH_SLIDE_DEFORM_DRAG; + break; + case SCULPT_TOOL_CLAY: + GETCH("RADIUS")->mappings[BRUSH_MAPPING_PRESSURE].flag |= BRUSH_MAPPING_ENABLED; + GETCH("SPACING")->ivalue = 3; + GETCH("AUTOSMOOTH")->fvalue = 0.25f; + GETCH("NORMAL_RADIUS_FACTOR")->fvalue = 0.75f; + GETCH("HARDNESS")->fvalue = 0.65; + break; + case SCULPT_TOOL_TWIST: + GETCH("STRENGTH")->fvalue = 0.5f; + GETCH("NORMAL_RADIUS_FACTOR")->fvalue = 1.0f; + GETCH("SPACING")->ivalue = 6; + GETCH("HARDNESS")->fvalue = 0.5; + break; + case SCULPT_TOOL_CLAY_STRIPS: { + GETCH("RADIUS")->mappings[BRUSH_MAPPING_PRESSURE].flag |= BRUSH_MAPPING_ENABLED; + GETCH("TIP_ROUNDNESS")->fvalue = 0.18f; + GETCH("NORMAL_RADIUS_FACTOR")->fvalue = 1.35f; + GETCH("STRENGTH")->fvalue = 0.8f; + GETCH("ACCUMULATE")->ivalue = 1; + + CurveMapping *curve = &GETCH("RADIUS")->mappings[BRUSH_MAPPING_PRESSURE].curve; + CurveMap *cuma = curve->cm; + + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.55f; + BKE_curvemap_insert(cuma, 0.5f, 0.7f); + cuma->curve[2].x = 1.0f; + cuma->curve[2].y = 1.0f; + BKE_curvemapping_changed(curve, true); + + cuma = curve->cm; + BKE_curvemap_insert(cuma, 0.6f, 0.25f); + BKE_curvemapping_changed(curve, true); - ch->mappings[BRUSH_MAPPING_PRESSURE].flag &= ~BRUSH_MAPPING_ENABLED; - ch->flag = BRUSH_CHANNEL_INHERIT; break; } default: { // implement me! - BKE_brush_channelset_free(chset); - brush->channels = NULL; + // BKE_brush_channelset_free(chset); + // brush->channels = NULL; break; } } + + namestack_pop(); +} + +#ifdef FLOAT +# undef FLOAT +#endif +#ifdef INT +# undef INT +#endif +#ifdef BOOL +# undef BOOL +#endif + +#define FLOAT BRUSH_CHANNEL_FLOAT +#define INT BRUSH_CHANNEL_INT +#define BOOL BRUSH_CHANNEL_BOOL +#define FLOAT3 BRUSH_CHANNEL_VEC3 +#define FLOAT4 BRUSH_CHANNEL_VEC4 + +/* clang-format off */ +#define DEF(brush_member, channel_name, btype, ctype) \ + {offsetof(Brush, brush_member), #channel_name, btype, ctype, sizeof(((Brush){0}).brush_member)}, +/* clang-format on */ + +typedef struct BrushSettingsMap { + int brush_offset; + const char *channel_name; + int brush_type; + int channel_type; + int member_size; +} BrushSettingsMap; + +/* clang-format off */ +static BrushSettingsMap brush_settings_map[] = { + DEF(size, RADIUS, INT, FLOAT) + DEF(alpha, STRENGTH, FLOAT, FLOAT) + DEF(autosmooth_factor, AUTOSMOOTH, FLOAT, FLOAT) + DEF(autosmooth_projection, SMOOTH_PROJECTION, FLOAT, FLOAT) + DEF(topology_rake_projection, TOPOLOGY_RAKE_PROJECTION, FLOAT, FLOAT) + DEF(topology_rake_radius_factor, TOPOLOGY_RAKE_RADIUS_SCALE, FLOAT, FLOAT) + DEF(topology_rake_spacing, TOPOLOGY_RAKE_SPACING, INT, FLOAT) + DEF(normal_weight, NORMAL_WEIGHT, FLOAT, FLOAT) + DEF(rake_factor, RAKE_FACTOR, FLOAT, FLOAT) + DEF(weight, WEIGHT, FLOAT, FLOAT) + DEF(jitter, JITTER, FLOAT, FLOAT) + DEF(jitter_absolute, JITTER_ABSOLITE, INT, INT) + DEF(smooth_stroke_radius, SMOOTH_STROKE_RADIUS, INT, FLOAT) + DEF(smooth_stroke_factor, SMOOTH_STROKE_FACTOR, FLOAT, FLOAT) + DEF(rate, RATE, FLOAT, FLOAT) + DEF(flow, FLOW, FLOAT, FLOAT) + DEF(wet_mix, WET_MIX, FLOAT, FLOAT) + DEF(wet_persistence, WET_PERSISTENCE, FLOAT, FLOAT) + DEF(density, DENSITY, FLOAT, FLOAT) + DEF(tip_scale_x, TIP_SCALE_X, FLOAT, FLOAT) + DEF(autosmooth_fset_slide, FSET_SLIDE, FLOAT, FLOAT) + DEF(boundary_smooth_factor, BOUNDARY_SMOOTH, FLOAT, FLOAT) + DEF(autosmooth_radius_factor, AUTOSMOOTH_RADIUS_SCALE, FLOAT, FLOAT) +}; +static const int brush_settings_map_len = ARRAY_SIZE(brush_settings_map); + +/* clang-format on */ +#undef DEF + +typedef struct BrushFlagMap { + int member_offset; + char *channel_name; + int flag; + int member_size; +} BrushFlagMap; + +/* clang-format off */ +#define DEF(member, channel, flag)\ + {offsetof(Brush, member), #channel, flag, sizeof(((Brush){0}).member)}, + +BrushFlagMap brush_flags_map[] = { + DEF(flag, ORIGINAL_NORMAL, BRUSH_ORIGINAL_NORMAL) + DEF(flag, ORIGINAL_PLANE, BRUSH_ORIGINAL_PLANE) + DEF(flag, ACCUMULATE, BRUSH_ACCUMULATE) +}; +int brush_flags_map_len = ARRAY_SIZE(brush_flags_map); + +/* clang-format on */ + +static ATTR_NO_OPT void do_coerce( + int type1, void *ptr1, int size1, int type2, void *ptr2, int size2) +{ + double val = 0; + + switch (type1) { + case BRUSH_CHANNEL_FLOAT: + val = *(float *)ptr1; + break; + case BRUSH_CHANNEL_INT: + case BRUSH_CHANNEL_ENUM: + case BRUSH_CHANNEL_BITMASK: + case BRUSH_CHANNEL_BOOL: + switch (size1) { + case 1: + val = (double)*(char *)ptr1; + break; + case 2: + val = (double)*(unsigned short *)ptr1; + break; + case 4: + val = (double)*(int *)ptr1; + break; + case 8: + val = (double)*(int64_t *)ptr1; + break; + } + break; + } + + switch (type2) { + case BRUSH_CHANNEL_FLOAT: + *(float *)ptr2 = (float)val; + break; + case BRUSH_CHANNEL_INT: + case BRUSH_CHANNEL_ENUM: + case BRUSH_CHANNEL_BITMASK: + case BRUSH_CHANNEL_BOOL: { + switch (size2) { + case 1: + *(char *)ptr2 = (char)val; + break; + case 2: + *(unsigned short *)ptr2 = (unsigned short)val; + break; + case 4: + *(int *)ptr2 = (int)val; + break; + case 8: + *(int64_t *)ptr2 = (int64_t)val; + break; + } + break; + } + } +} + +void *get_channel_value_pointer(BrushChannel *ch, int *r_data_size) +{ + *r_data_size = 4; + + switch (ch->type) { + case BRUSH_CHANNEL_FLOAT: + return &ch->fvalue; + case BRUSH_CHANNEL_INT: + case BRUSH_CHANNEL_ENUM: + case BRUSH_CHANNEL_BITMASK: + case BRUSH_CHANNEL_BOOL: + return &ch->ivalue; + case BRUSH_CHANNEL_VEC3: + *r_data_size = sizeof(float) * 3; + printf("implement me!\n"); + return NULL; + case BRUSH_CHANNEL_VEC4: + *r_data_size = sizeof(float) * 4; + printf("implement me!\n"); + return NULL; + } + + return NULL; +} + +ATTR_NO_OPT void BKE_brush_channelset_compat_load(BrushChannelSet *chset, + Brush *brush, + bool brush_to_channels) +{ + for (int i = 0; i < brush_flags_map_len; i++) { + BrushFlagMap *mf = brush_flags_map + i; + BrushChannel *ch = BKE_brush_channelset_lookup(chset, mf->channel_name); + + if (!ch) { + continue; + } + + char *ptr = (char *)brush; + ptr += mf->member_offset; + + switch (mf->member_size) { + case 1: { + char *f = (char *)ptr; + ch->ivalue = (*f & mf->flag) ? 1 : 0; + break; + } + case 2: { + ushort *f = (ushort *)ptr; + ch->ivalue = (*f & mf->flag) ? 1 : 0; + break; + } + case 4: { + uint *f = (uint *)ptr; + ch->ivalue = (*f & mf->flag) ? 1 : 0; + break; + } + case 8: { + uint64_t *f = (uint64_t *)ptr; + ch->ivalue = (*f & mf->flag) ? 1 : 0; + break; + } + } + } + + for (int i = 0; i < brush_settings_map_len; i++) { + BrushSettingsMap *mp = brush_settings_map + i; + BrushChannel *ch = BKE_brush_channelset_lookup(chset, mp->channel_name); + + if (!ch) { + continue; + } + + char *bptr = (char *)brush; + bptr += mp->brush_offset; + + int csize; + void *cptr = get_channel_value_pointer(ch, &csize); + + if (brush_to_channels) { + do_coerce(mp->brush_type, bptr, mp->member_size, ch->type, cptr, csize); + } + else { + do_coerce(ch->type, cptr, csize, mp->brush_type, bptr, mp->member_size); + } + } } BrushCommandList *BKE_brush_commandlist_create() @@ -637,7 +1009,9 @@ void BKE_brush_commandlist_free(BrushCommandList *cl) MEM_freeN(cl); } -BrushCommand *BKE_brush_commandlist_add(BrushCommandList *cl) +BrushCommand *BKE_brush_commandlist_add(BrushCommandList *cl, + BrushChannelSet *chset_template, + bool auto_inherit) { cl->totcommand++; @@ -645,11 +1019,25 @@ BrushCommand *BKE_brush_commandlist_add(BrushCommandList *cl) cl->commands = MEM_callocN(sizeof(BrushCommand) * cl->totcommand, "BrushCommand"); } else { - cl->commands = MEM_recallocN(cl->commands, sizeof(BrushCommand) * cl->totcommand); + cl->commands = MEM_recallocN_id( + cl->commands, sizeof(BrushCommand) * cl->totcommand, "cl->commands"); } BrushCommand *cmd = cl->commands + cl->totcommand - 1; - cmd->params = BKE_brush_channelset_create(); + + if (chset_template) { + cmd->params = BKE_brush_channelset_copy(chset_template); + + for (int i = 0; auto_inherit && i < cmd->params->totchannel; i++) { + BrushChannel *ch = cmd->params->channels + i; + + ch->flag |= BRUSH_CHANNEL_INHERIT; + } + } + else { + cmd->params = BKE_brush_channelset_create(); + } + cmd->params_final = NULL; return cmd; @@ -659,6 +1047,10 @@ BrushCommand *BKE_brush_command_init(BrushCommand *command, int tool) { BrushChannelSet *chset = command->params; + namestack_push(__func__); + + command->tool = tool; + ADDCH("SPACING"); switch (tool) { @@ -671,46 +1063,113 @@ BrushCommand *BKE_brush_command_init(BrushCommand *command, int tool) ADDCH("STRENGTH"); ADDCH("FSET_SLIDE"); ADDCH("BOUNDARY_SMOOTH"); - ADDCH("PROJECTION"); + ADDCH("AUTOSMOOTH_PROJECTION"); break; case SCULPT_TOOL_TOPOLOGY_RAKE: ADDCH("RADIUS"); ADDCH("STRENGTH"); // ADDCH("FSET_SLIDE"); // ADDCH("BOUNDARY_SMOOTH"); - ADDCH("PROJECTION"); + ADDCH("AUTOSMOOTH_PROJECTION"); ADDCH("TOPOLOGY_RAKE_MODE"); break; case SCULPT_TOOL_DYNTOPO: break; } + namestack_pop(); + return command; } -void BKE_builtin_commandlist_create(BrushChannelSet *chset, BrushCommandList *cl, int tool) +static void float_set_uninherit(BrushChannelSet *chset, const char *channel, float val) +{ + BrushChannel *ch = BKE_brush_channelset_lookup(chset, channel); + + if (!ch) { + printf("%s: unknown channel %s\n", __func__, channel); + return; + } + + ch->fvalue = val; + ch->flag &= ~BRUSH_CHANNEL_INHERIT; +} + +ATTR_NO_OPT void BKE_builtin_commandlist_create(Brush *brush, + BrushChannelSet *chset, + BrushCommandList *cl, + int tool, + BrushMappingData *mapdata) { BrushCommand *cmd; - cmd = BKE_brush_commandlist_add(cl); + /* add main tool */ + + cmd = BKE_brush_commandlist_add(cl, chset, true); BKE_brush_command_init(cmd, tool); - for (int i = 0; i < cmd->totparam; i++) { - // inherit from brush channels for main tool - cmd->params->channels[i].flag |= BRUSH_CHANNEL_INHERIT; + float radius = BKE_brush_channelset_get_float(chset, "RADIUS", NULL); + + bool no_autosmooth = ELEM( + brush->sculpt_tool, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK); + + /* build autosmooth command */ + float autosmooth_scale = BKE_brush_channelset_get_float(chset, "AUTOSMOOTH_RADIUS_SCALE", NULL); + float autosmooth_projection = BKE_brush_channelset_get_float( + chset, "TOPOLOGY_RAKE_PROJECTION", NULL); + + float autosmooth_spacing; + + if (BKE_brush_channelset_get_int(chset, "AUTOSMOOTH_USE_SPACING")) { + autosmooth_spacing = BKE_brush_channelset_get_float(chset, "AUTOSMOOTH_SPACING", NULL); + } + else { + autosmooth_spacing = BKE_brush_channelset_get_float(chset, "SPACING", NULL); } - float radius = BKE_brush_channel_get_float(chset, "RADIUS"); - float autosmooth_scale = BKE_brush_channel_get_float(chset, "AUTOSMOOTH_RADIUS_SCALE"); - - float autosmooth = BKE_brush_channel_get_float(chset, "AUTOSMOOTH"); - if (autosmooth > 0.0f) { - cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl), SCULPT_TOOL_SMOOTH); - BKE_brush_channel_set_float(cmd->params, "STRENGTH", autosmooth); - BKE_brush_channel_set_float(cmd->params, "RADIUS", radius * autosmooth_scale); - BKE_brush_channel_set_float( - cmd->params, "PROJECTION", BKE_brush_channel_get_float(chset, "AUTOSMOOTH_PROJECTION")); + float autosmooth = BKE_brush_channelset_get_float(chset, "AUTOSMOOTH", NULL); + if (!no_autosmooth && autosmooth > 0.0f) { + cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, brush->channels, true), + SCULPT_TOOL_SMOOTH); + float_set_uninherit(cmd->params, "STRENGTH", autosmooth); + float_set_uninherit(cmd->params, "RADIUS", radius * autosmooth_scale); + float_set_uninherit(cmd->params, "PROJECTION", autosmooth_projection); + float_set_uninherit(cmd->params, "SPACING", autosmooth_spacing); } + + float topology_rake_scale = BKE_brush_channelset_get_float( + chset, "TOPOLOGY_RAKE_RADIUS_SCALE", NULL); + float topology_rake_projection = BKE_brush_channelset_get_float( + chset, "TOPOLOGY_RAKE_PROJECTION", NULL); + + /* build topology rake command*/ + float topology_rake = BKE_brush_channelset_get_float(chset, "TOPOLOGY_RAKE", NULL); + float topology_rake_spacing; + + if (BKE_brush_channelset_get_int(chset, "TOPOLOGY_RAKE_USE_SPACING")) { + topology_rake_spacing = BKE_brush_channelset_get_float(chset, "TOPOLOGY_RAKE_SPACING", NULL); + } + else { + topology_rake_spacing = BKE_brush_channelset_get_float(chset, "SPACING", NULL); + } + + if (topology_rake > 0.0f) { + cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, brush->channels, true), + SCULPT_TOOL_SMOOTH); + + float_set_uninherit(cmd->params, "STRENGTH", topology_rake); + float_set_uninherit(cmd->params, "RADIUS", radius * topology_rake_scale); + float_set_uninherit(cmd->params, "PROJECTION", topology_rake_projection); + float_set_uninherit(cmd->params, "SPACING", topology_rake_spacing); + } + + /* build dyntopo command */ + + if (!BKE_brush_channelset_get_int(chset, "DYNTOPO_DISABLED")) { + cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, brush->channels, true), + SCULPT_TOOL_DYNTOPO); + } + // if (!BKE_brush_channelset_get_int) } void BKE_brush_channelset_read(BlendDataReader *reader, BrushChannelSet *cset) @@ -736,10 +1195,14 @@ void BKE_brush_channelset_read(BlendDataReader *reader, BrushChannelSet *cset) printf("failed to find brush definition"); ch->def = BKE_brush_default_channel_def(); } + else { + // ensure ->type is correct + ch->type = ch->def->type; + } } } -void BKE_brush_channelset_write(BlendWriter *writer, BrushChannelSet *cset) +ATTR_NO_OPT void BKE_brush_channelset_write(BlendWriter *writer, BrushChannelSet *cset) { BLO_write_struct(writer, BrushChannelSet, cset); BLO_write_struct_array_by_name(writer, "BrushChannel", cset->totchannel, cset->channels); @@ -795,19 +1258,39 @@ const char *BKE_brush_mapping_type_to_typename(BrushMappingType mapping) void BKE_brush_mapping_copy_data(BrushMapping *dst, BrushMapping *src) { + BKE_curvemapping_free_data(&dst->curve); + // do not copy .type int type = dst->type; *dst = *src; dst->type = type; + namestack_push(__func__); + BKE_curvemapping_copy_data(&dst->curve, &src->curve); BKE_curvemapping_init(&dst->curve); check_corrupted_curve(dst); + + namestack_pop(); } -/* clang-format on */ +void BKE_brush_channelset_to_unified_settings(BrushChannelSet *chset, UnifiedPaintSettings *ups) +{ + BrushChannel *ch; + if (ch = BKE_brush_channelset_lookup(chset, "RADIUS")) { + ups->size = ch->fvalue; + } + + if (ch = BKE_brush_channelset_lookup(chset, "STRENGTH")) { + ups->alpha = ch->fvalue; + } + + if (ch = BKE_brush_channelset_lookup(chset, "WEIGHT")) { + ups->weight = ch->fvalue; + } +} /* idea for building built-in preset node graphs: from brush_builder import Builder; diff --git a/source/blender/blenkernel/intern/brush_engine_presets.c b/source/blender/blenkernel/intern/brush_engine_presets.c new file mode 100644 index 00000000000..8b4cbdd8c19 --- /dev/null +++ b/source/blender/blenkernel/intern/brush_engine_presets.c @@ -0,0 +1,423 @@ +#include "MEM_guardedalloc.h" + +#include "BLI_alloca.h" +#include "BLI_array.h" +#include "BLI_bitmap.h" +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_listbase.h" +#include "BLI_math.h" +#include "BLI_memarena.h" +#include "BLI_rect.h" + +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_color_types.h" +#include "DNA_curveprofile_types.h" +#include "DNA_material_types.h" +#include "DNA_node_types.h" +#include "DNA_sculpt_brush_types.h" + +#include "BKE_brush.h" +#include "BKE_colorband.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_node.h" +#include "BKE_paint.h" + +#include "BKE_brush_engine.h" +#include "BKE_curveprofile.h" + +#include "BLO_read_write.h" + +/* +Instructions to add a built-in channel: + +1. Add to brush_builtin_channels +2. Add to BKE_brush_builtin_patch to insert it in old brushes (without converting data) + +To enable converting to/from old data: +3. If not a boolean mapping to a bitflag: Add to brush_settings_map +4. If a boolean mapping to a bitflag: Add to brush_flags_map_len. +*/ +#define ICON_NONE -1 + +/* clang-format off */ +#define MAKE_FLOAT_EX_OPEN(idname1, name1, tooltip1, value1, min1, max1, smin1, smax1) \ + {.name = name1, \ + .idname = idname1, \ + .fvalue = value1,\ + .tooltip = tooltip1, \ + .min = min1,\ + .max = max1,\ + .soft_min = smin1,\ + .soft_max = smax1,\ + .type = BRUSH_CHANNEL_FLOAT + +#define MAKE_FLOAT_EX(idname, name, tooltip, value, min, max, smin, smax) \ + MAKE_FLOAT_EX_OPEN(idname, name, tooltip, value, min, max, smin, smax) } + +#define MAKE_FLOAT(idname, name, tooltip, value, min, max) MAKE_FLOAT_EX(idname, name, tooltip, value, min, max, min, max) + +#define MAKE_INT_EX_OPEN(idname1, name1, tooltip1, value1, min1, max1, smin1, smax1) \ + {.name = name1, \ + .idname = idname1, \ + .tooltip = tooltip1, \ + .min = min1,\ + .max = max1,\ + .ivalue = value1,\ + .soft_min = smin1,\ + .soft_max = smax1,\ + .type = BRUSH_CHANNEL_INT + +#define MAKE_INT_EX(idname, name, tooltip, value, min, max, smin, smax) \ + MAKE_INT_EX_OPEN(idname, name, tooltip, value, min, max, smin, smax) } + +#define MAKE_INT(idname, name, tooltip, value, min, max) MAKE_INT_EX(idname, name, tooltip, value, min, max, min, max) + +#define MAKE_BOOL_EX_OPEN(idname1, name1, tooltip1, value1)\ + {.name = name1, \ + .idname = idname1, \ + .tooltip = tooltip1, \ + .ivalue = value1,\ + .type = BRUSH_CHANNEL_BOOL + +#define MAKE_BOOL(idname, name, tooltip, value)\ + MAKE_BOOL_EX_OPEN(idname, name, tooltip, value) } + +/* clang-format on */ + +/* clang-format off */ +BrushChannelType brush_builtin_channels[] = { + { + .name = "Radius", + .idname = "RADIUS", + .min = 0.001f, + .type = BRUSH_CHANNEL_FLOAT, + .max = 2048.0f, + .fvalue = 50.0f, + .soft_min = 0.1f, + .soft_max = 1024.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0f, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Strength", + .idname = "STRENGTH", + .min = -1.0f, + .type = BRUSH_CHANNEL_FLOAT, + .max = 4.0f, + .fvalue = 0.5f, + .soft_min = 0.0f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0f, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Alpha", + .idname = "ALPHA", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0f, + .max = 1.0f, + .fvalue = 1.0f, + .soft_min = 0.0f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0f, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Spacing", + .idname = "SPACING", + .min = 1.0f, + .type = BRUSH_CHANNEL_INT, + .max = 500.0f, + .fvalue = 10.0f, + .soft_min = 1.0f, + .soft_max = 500.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Autosmooth", + .idname = "AUTOSMOOTH", + .type = BRUSH_CHANNEL_FLOAT, + .min = -1.0f, + .max = 4.0f, + .soft_min = 0.0f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false, .inv = true}, + } + }, + { + .name = "Topology Rake", + .idname = "TOPOLOGY_RAKE", + .type = BRUSH_CHANNEL_FLOAT, + .min = -1.0f, + .max = 4.0f, + .soft_min = 0.0f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Autosmooth Radius Scale", + .idname = "AUTOSMOOTH_RADIUS_SCALE", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 25.0f, + .fvalue = 1.0f, + .soft_min = 0.1f, + .soft_max = 4.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Rake Radius Scale", + .idname = "TOPOLOGY_RAKE_RADIUS_SCALE", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 25.0f, + .fvalue = 1.0f, + .soft_min = 0.1f, + .soft_max = 4.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Face Set Slide", + .idname = "FSET_SLIDE", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 1.0f, + .fvalue = 1.0f, + .soft_min = 0.1f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Boundary Smooth", + .idname = "BOUNDARY_SMOOTH", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 1.0f, + .soft_min = 0.1f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Projection", + .idname = "PROJECTION", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 1.0f, + .soft_min = 0.1f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Use Spacing", + .idname = "TOPOLOGY_RAKE_USE_SPACING", + .type = BRUSH_CHANNEL_BOOL, + .ivalue = 0 + }, + { + .name = "Use Spacing", + .idname = "AUTOSMOOTH_USE_SPACING", + .type = BRUSH_CHANNEL_BOOL, + .ivalue = 0 + }, + { + .name = "Projection", + .idname = "AUTOSMOOTH_PROJECTION", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 1.0f, + .soft_min = 0.1f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Projection", + .idname = "TOPOLOGY_RAKE_PROJECTION", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 1.0f, + .fvalue = 0.975f, + .soft_min = 0.1f, + .soft_max = 1.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Spacing", + .idname = "TOPOLOGY_RAKE_SPACING", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 1.0f, + .fvalue = 13.0f, + .soft_min = 0.1f, + .soft_max = 100.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + { + .name = "Spacing", + .idname = "AUTOSMOOTH_SPACING", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0001f, + .max = 1.0f, + .fvalue = 13.0f, + .soft_min = 0.1f, + .soft_max = 100.0f, + .mappings = { + .pressure = {.curve = CURVE_PRESET_LINE, .factor = 1.0, .min = 0.0f, .max = 1.0f, .enabled = false}, + } + }, + + { + .name = "Topology Rake Mode", + .idname = "TOPOLOGY_RAKE_MODE", + .type = BRUSH_CHANNEL_ENUM, + .enumdef = {.items = { + {0, "BRUSH_DIRECTION", ICON_NONE, "Stroke", "Stroke Direction"}, + {1, "CURVATURE", ICON_NONE, "Curvature", "Follow mesh curvature"}, + {-1, 0} + }} + }, + { + .name = "Automasking", + .idname = "AUTOMASKING", + .flag = BRUSH_CHANNEL_INHERIT_IF_UNSET | BRUSH_CHANNEL_INHERIT, + .type = BRUSH_CHANNEL_BITMASK, + .enumdef = {.items = { + {BRUSH_AUTOMASKING_BOUNDARY_EDGES, "BOUNDARY_EDGE", ICON_NONE, "Boundary Edges", ""}, + {BRUSH_AUTOMASKING_BOUNDARY_FACE_SETS, "BOUNDARY_FACE_SETS", ICON_NONE, "Boundary Face Sets", ""}, + {BRUSH_AUTOMASKING_CONCAVITY, "CONCAVITY", ICON_NONE, "Concave", ""}, + {BRUSH_AUTOMASKING_INVERT_CONCAVITY, "INVERT_CONCAVITY", ICON_NONE, "Invert Concave", "Invert Concave Map"}, + {BRUSH_AUTOMASKING_FACE_SETS, "FACE_SETS", ICON_NONE, "Face Sets", ""}, + {BRUSH_AUTOMASKING_TOPOLOGY, "TOPOLOGY", ICON_NONE, "Topology", ""} + }} + }, + { + .name = "Disable Dyntopo", + .idname = "DYNTOPO_DISABLED", + .type = BRUSH_CHANNEL_BOOL, + .flag = BRUSH_CHANNEL_NO_MAPPINGS, + .ivalue = 0 + }, + { + .name = "Detail Range", + .idname = "DYNTOPO_DETAIL_RANGE", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.001, + .max = 0.99, + .flag = BRUSH_CHANNEL_INHERIT, + .ivalue = 0 + }, + { + .name = "Operations", + .idname = "DYNTOPO_OPS", + .type = BRUSH_CHANNEL_BITMASK, + .flag = BRUSH_CHANNEL_INHERIT, + .ivalue = DYNTOPO_COLLAPSE | DYNTOPO_CLEANUP | DYNTOPO_SUBDIVIDE, + .enumdef = { + {DYNTOPO_COLLAPSE, "COLLAPSE", ICON_NONE, "Collapse", ""}, + {DYNTOPO_SUBDIVIDE, "SUBDIVIDE", ICON_NONE, "Subdivide", ""}, + {DYNTOPO_CLEANUP, "CLEANUP", ICON_NONE, "Cleanup", ""}, + {DYNTOPO_LOCAL_COLLAPSE, "LOCAL_COLLAPSE", ICON_NONE, "Local Collapse", ""}, + {DYNTOPO_LOCAL_SUBDIVIDE, "LOCAL_SUBDIVIDE", ICON_NONE, "Local Subdivide", ""}, + {-1, NULL, -1, NULL, NULL} + } + }, + + { + .name = "Slide Deform Type", + .idname = "SLIDE_DEFORM_TYPE", + .ivalue = BRUSH_SLIDE_DEFORM_DRAG, + .type = BRUSH_CHANNEL_ENUM, + .enumdef = { + {BRUSH_SLIDE_DEFORM_DRAG, "DRAG", ICON_NONE, "Drag", ""}, + {BRUSH_SLIDE_DEFORM_PINCH, "PINCH", ICON_NONE, "Pinch", ""}, + {BRUSH_SLIDE_DEFORM_EXPAND, "EXPAND", ICON_NONE, "Expand", ""}, + {-1, NULL, -1, NULL} + } + }, + { + .name = "Normal Radius", + .idname = "NORMAL_RADIUS_FACTOR", + .tooltip = "Ratio between the brush radius and the radius that is going to be " + "used to sample the normal", + .type = BRUSH_CHANNEL_FLOAT, + .min = 0.0f, + .max = 2.0f, + .soft_min = 0.0f, + .soft_max = 2.0f, + }, + { + .name = "Hardness", + .idname = "HARDNESS", + .tooltip = "Brush hardness", + .type = BRUSH_CHANNEL_FLOAT, + .fvalue = 0.0f, + .min = 0.0f, + .max = 1.0f, + .soft_min = 0.0f, + .soft_max = 1.0f + }, + { + .name = "Tip Roundness", + .idname = "TIP_ROUNDNESS", + .tooltip = "", + .type = BRUSH_CHANNEL_FLOAT, + .fvalue = 0.0f, + .min = 0.0f, + .max = 1.0f, + .soft_min = 0.0f, + .soft_max = 1.0f + }, + { + .name = "Accumulate", + .idname = "ACCUMULATE", + .type = BRUSH_CHANNEL_BOOL, + .ivalue = 0 + }, + + MAKE_FLOAT("NORMAL_WEIGHT", "Normal Weight", "", 0.0f, 0.0f, 1.0f), + MAKE_FLOAT("RAKE_FACTOR", "Rake Factor", "How much grab will follow cursor rotation", 0.0f, 0.0f, 10.0f), + MAKE_FLOAT("WEIGHT", "Weight", "", 0.5f, 0.0f, 1.0f), + MAKE_FLOAT("JITTER", "Jitter", "Jitter the position of the brush while painting", 0.0f, 0.0f, 1.0f), + MAKE_INT("JITTER_ABSOLUTE", "Absolute Jitter", "", 0, 0.0f, 1000.0f), + MAKE_FLOAT("SMOOTH_STROKE_RADIUS", "Smooth Stroke Radius", "Minimum distance from last point before stroke continues", 10.0f, 10.0f, 200.0f), + MAKE_FLOAT("SMOOTH_STROKE_FACTOR", "Smooth Stroke Factor", "", 0.5f, 0.5f, 0.99f), + MAKE_FLOAT_EX("RATE", "Rate", "", 0.5, 0.0001f, 10000.0f, 0.01f, 1.0f), + MAKE_FLOAT("FLOW", "Flow", "Amount of paint that is applied per stroke sample", 0.0f, 0.0f, 1.0f), + MAKE_FLOAT("WET_MIX", "Wet Mix", "Amount of paint that is picked from the surface into the brush color", 0.0f, 0.0f, 1.0f), + MAKE_FLOAT("WET_PERSISTENCE", "Wet Persistence", "Amount of wet paint that stays in the brush after applying paint to the surface", 0.0f, 0.0f, 1.0f), + MAKE_FLOAT("DENSITY", "Density", "Amount of random elements that are going to be affected by the brush", 0.0f, 0.0f, 1.0f), + MAKE_FLOAT("TIP_SCALE_X", "Tip Scale X", "Scale of the brush tip in the X axis", 0.0f, 0.0f, 1.0f), + MAKE_FLOAT("DASH_RATIO", "Dash Ratio", "Ratio of samples in a cycle that the brush is enabled", 0.0f, 0.0f, 1.0f), + MAKE_FLOAT_EX("PLANE_OFFSET", "Plane Offset", "Adjust plane on which the brush acts towards or away from the object surface", 0.0f, -2.0f, 2.0f, -0.5f, 0.5f), + MAKE_BOOL("ORIGINAL_NORMAL", "Original Normal", "When locked keep using normal of surface where stroke was initiated", false), + MAKE_BOOL("ORIGINAL_PLANE", "Original Plane", "When locked keep using the plane origin of surface where stroke was initiated", false) +}; + +/* clang-format on */ +const int brush_builtin_channel_len = ARRAY_SIZE(brush_builtin_channels); diff --git a/source/blender/blenkernel/intern/colortools.c b/source/blender/blenkernel/intern/colortools.c index c6d84965185..e739b776570 100644 --- a/source/blender/blenkernel/intern/colortools.c +++ b/source/blender/blenkernel/intern/colortools.c @@ -124,6 +124,37 @@ void BKE_curvemapping_free(CurveMapping *cumap) } } +static void *my_dupalloc_id(void *mem, const char *tag) +{ + size_t size = MEM_allocN_len(mem); + + void *ret = MEM_mallocN(size, tag); + memcpy(ret, mem, size); + + return ret; +}; + +void BKE_curvemapping_copy_data_tag_ex(CurveMapping *target, + const CurveMapping *cumap, + const char *tag) +{ + int a; + + *target = *cumap; + + for (a = 0; a < CM_TOT; a++) { + if (cumap->cm[a].curve) { + target->cm[a].curve = my_dupalloc_id(cumap->cm[a].curve, tag); + } + if (cumap->cm[a].table) { + target->cm[a].table = my_dupalloc_id(cumap->cm[a].table, tag); + } + if (cumap->cm[a].premultable) { + target->cm[a].premultable = my_dupalloc_id(cumap->cm[a].premultable, tag); + } + } +} + void BKE_curvemapping_copy_data(CurveMapping *target, const CurveMapping *cumap) { int a; @@ -305,6 +336,15 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope case CURVE_PRESET_BELL: cuma->totpoint = 3; break; + case CURVE_PRESET_POW2: + cuma->totpoint = 5; + break; + case CURVE_PRESET_POW3: + cuma->totpoint = 6; + break; + case CURVE_PRESET_POW15: + cuma->totpoint = 6; + break; } cuma->curve = MEM_callocN(cuma->totpoint * sizeof(CurveMapPoint), "curve points"); @@ -401,8 +441,60 @@ void BKE_curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope cuma->curve[2].x = 1.0f; cuma->curve[2].y = 0.025f; break; - } + case CURVE_PRESET_POW2: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + cuma->curve[1].x = 0.25f; + cuma->curve[1].y = 0.0625f; + + cuma->curve[2].x = 0.5; + cuma->curve[2].y = 0.25; + + cuma->curve[3].x = 0.75f; + cuma->curve[3].y = 0.5625f; + + cuma->curve[4].x = 1.0f; + cuma->curve[4].y = 1.0f; + + case CURVE_PRESET_POW3: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + + cuma->curve[1].x = 0.135f; + cuma->curve[1].y = 0.002f; + + cuma->curve[2].x = 0.318; + cuma->curve[2].y = 0.032; + + cuma->curve[3].x = 0.528; + cuma->curve[3].y = 0.147f; + + cuma->curve[4].x = 0.757f; + cuma->curve[4].y = 0.433f; + + cuma->curve[5].x = 1.0f; + cuma->curve[5].y = 1.0f; + case CURVE_PRESET_POW15: + cuma->curve[0].x = 0.0f; + cuma->curve[0].y = 0.0f; + + cuma->curve[1].x = 0.135f; + cuma->curve[1].y = 0.002f; + + cuma->curve[2].x = 0.318; + cuma->curve[2].y = 0.032; + + cuma->curve[3].x = 0.528; + cuma->curve[3].y = 0.147f; + + cuma->curve[4].x = 0.757f; + cuma->curve[4].y = 0.433f; + + cuma->curve[5].x = 1.0f; + cuma->curve[5].y = 1.0f; + } + //[[0,0], [0.134,0.002], [0.318,0.032], [0.528,0.147], [0.757,0.433], [1,1] /* mirror curve in x direction to have positive slope * rather than default negative slope */ if (slope == CURVEMAP_SLOPE_POSITIVE) { diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index 6824afadb3e..796e7ab2c41 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1060,10 +1060,15 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &sce->toolsettings->sculpt->channels); BKE_brush_channelset_read(reader, sce->toolsettings->sculpt->channels); } - else { + else if (sce->toolsettings->sculpt) { sce->toolsettings->sculpt->channels = BKE_brush_channelset_create(); } + if (sce->toolsettings->sculpt) { + // make sure radius exists in the toolsettings brush channel set + BKE_brush_channelset_ensure_builtin(sce->toolsettings->sculpt->channels, "RADIUS"); + } + /* relink grease pencil interpolation curves */ BLO_read_data_address(reader, &sce->toolsettings->gp_interpolate.custom_ipo); if (sce->toolsettings->gp_interpolate.custom_ipo) { diff --git a/source/blender/blenloader/intern/versioning_300.c b/source/blender/blenloader/intern/versioning_300.c index 33927ad800f..f786b10f02c 100644 --- a/source/blender/blenloader/intern/versioning_300.c +++ b/source/blender/blenloader/intern/versioning_300.c @@ -49,6 +49,7 @@ #include "BKE_animsys.h" #include "BKE_asset.h" #include "BKE_brush.h" +#include "BKE_brush_engine.h" #include "BKE_collection.h" #include "BKE_deform.h" #include "BKE_fcurve.h" @@ -1239,6 +1240,9 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) if (br->autosmooth_fset_slide == 0.0f) { Brush defbrush = *br; + // don't free data inside of pointers copied from br + defbrush.channels = NULL; + defbrush.curve = NULL; defbrush.pressure_size_curve = defbrush.pressure_strength_curve = NULL; BKE_brush_sculpt_reset(&defbrush); @@ -1258,6 +1262,10 @@ void blo_do_versions_300(FileData *fd, Library *UNUSED(lib), Main *bmain) if (br->autosmooth_projection == 0.0f) { br->autosmooth_projection = defbrush.autosmooth_projection; } + + if (defbrush.channels) { + BKE_brush_channelset_free(defbrush.channels); + } } if (br->sculpt_tool == SCULPT_TOOL_VCOL_BOUNDARY) { diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index e0296f7a6ac..d63581dfdc1 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -253,6 +253,8 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col, bool prima bool init; TexSnapshot *target; + bool use_brush_channels = paint_use_channels(vc->C); + MTex *mtex = (primary) ? &br->mtex : &br->mask_mtex; ePaintOverlayControlFlags overlay_flags = BKE_paint_get_overlay_flags(); uchar *buffer = NULL; @@ -273,12 +275,12 @@ static int load_tex(Brush *br, ViewContext *vc, float zoom, bool col, bool prima struct ImagePool *pool = NULL; /* Stencil is rotated later. */ const float rotation = (mtex->brush_map_mode != MTEX_MAP_MODE_STENCIL) ? -mtex->rot : 0.0f; - const float radius = BKE_brush_size_get(vc->scene, br) * zoom; + const float radius = BKE_brush_size_get(vc->scene, br, use_brush_channels) * zoom; make_tex_snap(target, vc, zoom); if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) { - int s = BKE_brush_size_get(vc->scene, br); + int s = BKE_brush_size_get(vc->scene, br, use_brush_channels); int r = 1; for (s >>= 1; s > 0; s >>= 1) { @@ -420,13 +422,14 @@ static int load_tex_cursor(Brush *br, ViewContext *vc, float zoom) cursor_snap.curve_preset != br->curve_preset; init = (cursor_snap.overlay_texture != 0); + bool use_brush_channels = paint_use_channels(vc->C); if (refresh) { int s, r; cursor_snap.zoom = zoom; - s = BKE_brush_size_get(vc->scene, br); + s = BKE_brush_size_get(vc->scene, br, use_brush_channels); r = 1; for (s >>= 1; s > 0; s >>= 1) { @@ -565,6 +568,8 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, } } + bool use_brush_channels = paint_use_channels(vc->C); + if (!(mtex->tex) || !((mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) || (valid && ELEM(mtex->brush_map_mode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_TILED)))) { @@ -598,7 +603,7 @@ static bool paint_draw_tex_overlay(UnifiedPaintSettings *ups, quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size; } else { - const int radius = BKE_brush_size_get(vc->scene, brush) * zoom; + const int radius = BKE_brush_size_get(vc->scene, brush, use_brush_channels) * zoom; quad.xmin = x - radius; quad.ymin = y - radius; quad.xmax = x + radius; @@ -691,6 +696,8 @@ static bool paint_draw_cursor_overlay( rctf quad; /* Check for overlay mode. */ + bool use_brush_channels = paint_use_channels(vc->C); + if (!(brush->overlay_flags & BRUSH_OVERLAY_CURSOR)) { return false; } @@ -710,7 +717,7 @@ static bool paint_draw_cursor_overlay( quad.ymax = ups->anchored_initial_mouse[1] + ups->anchored_size; } else { - const int radius = BKE_brush_size_get(vc->scene, brush) * zoom; + const int radius = BKE_brush_size_get(vc->scene, brush, use_brush_channels) * zoom; center[0] = x; center[1] = y; @@ -1003,6 +1010,8 @@ static void paint_cursor_update_unprojected_radius(UnifiedPaintSettings *ups, { float unprojected_radius, projected_radius; + bool use_brush_channels = paint_use_channels(vc->C); + /* Update the brush's cached 3D radius. */ if (!BKE_brush_use_locked_size(vc->scene, brush)) { /* Get 2D brush radius. */ @@ -1014,7 +1023,7 @@ static void paint_cursor_update_unprojected_radius(UnifiedPaintSettings *ups, projected_radius = 8; } else { - projected_radius = BKE_brush_size_get(vc->scene, brush); + projected_radius = BKE_brush_size_get(vc->scene, brush, use_brush_channels); } } @@ -1301,10 +1310,13 @@ static bool paint_cursor_context_init(bContext *C, pcontext->translation[0] = (float)x; pcontext->translation[1] = (float)y; + bool use_brush_channels = paint_use_channels(pcontext->C); + float zoomx, zoomy; get_imapaint_zoom(C, &zoomx, &zoomy); pcontext->zoomx = max_ff(zoomx, zoomy); - pcontext->final_radius = (BKE_brush_size_get(pcontext->scene, pcontext->brush) * zoomx); + pcontext->final_radius = + (BKE_brush_size_get(pcontext->scene, pcontext->brush, use_brush_channels) * zoomx); /* There is currently no way to check if the direction is inverted before starting the stroke, * so this does not reflect the state of the brush in the UI. */ @@ -1339,6 +1351,8 @@ static bool paint_cursor_context_init(bContext *C, static void paint_cursor_update_pixel_radius(PaintCursorContext *pcontext) { + bool use_brush_channels = paint_use_channels(pcontext->C); + if (pcontext->is_cursor_over_mesh) { Brush *brush = BKE_paint_brush(pcontext->paint); pcontext->pixel_radius = project_brush_radius( @@ -1347,7 +1361,7 @@ static void paint_cursor_update_pixel_radius(PaintCursorContext *pcontext) pcontext->location); if (pcontext->pixel_radius == 0) { - pcontext->pixel_radius = BKE_brush_size_get(pcontext->scene, brush); + pcontext->pixel_radius = BKE_brush_size_get(pcontext->scene, brush, use_brush_channels); } copy_v3_v3(pcontext->scene_space_location, pcontext->location); @@ -1357,7 +1371,7 @@ static void paint_cursor_update_pixel_radius(PaintCursorContext *pcontext) Sculpt *sd = CTX_data_tool_settings(pcontext->C)->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); - pcontext->pixel_radius = BKE_brush_size_get(pcontext->scene, brush); + pcontext->pixel_radius = BKE_brush_size_get(pcontext->scene, brush, use_brush_channels); } } @@ -1396,7 +1410,7 @@ static void paint_cursor_sculpt_session_update_and_init(PaintCursorContext *pcon paint_cursor_update_pixel_radius(pcontext); if (BKE_brush_use_locked_size(scene, brush)) { - BKE_brush_size_set(scene, brush, pcontext->pixel_radius); + BKE_brush_size_set(scene, brush, pcontext->pixel_radius, !!ss); } if (pcontext->is_cursor_over_mesh) { @@ -1562,9 +1576,13 @@ static void sculpt_cursor_draw_3D_face_set_preview(PaintCursorContext *pcontext) static void paint_cursor_update_object_space_radius(PaintCursorContext *pcontext) { + bool use_brush_channels = paint_use_channels(pcontext->C); + if (!BKE_brush_use_locked_size(pcontext->scene, pcontext->brush)) { pcontext->radius = paint_calc_object_space_radius( - &pcontext->vc, pcontext->location, BKE_brush_size_get(pcontext->scene, pcontext->brush)); + &pcontext->vc, + pcontext->location, + BKE_brush_size_get(pcontext->scene, pcontext->brush, use_brush_channels)); } else { pcontext->radius = BKE_brush_unprojected_radius_get(pcontext->scene, pcontext->brush); diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index b5113681955..9f584ea50ff 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -598,6 +598,9 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) ToolSettings *toolsettings = scene->toolsettings; PaintOperation *pop = paint_stroke_mode_data(stroke); Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + Object *ob = CTX_data_active_object(C); + + bool is_sculpt = ob && ob->mode == OB_MODE_SCULPT; toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; @@ -614,7 +617,7 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) paint_stroke_flipped(stroke), 1.0, 0.0, - BKE_brush_size_get(scene, brush)); + BKE_brush_size_get(scene, brush, is_sculpt)); /* two redraws, one for GPU update, one for notification */ paint_proj_redraw(C, pop->custom_paint, false); paint_proj_redraw(C, pop->custom_paint, true); @@ -639,7 +642,7 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) paint_stroke_flipped(stroke), 1.0, 0.0, - BKE_brush_size_get(scene, brush)); + BKE_brush_size_get(scene, brush, is_sculpt)); /* two redraws, one for GPU update, one for notification */ paint_proj_redraw(C, pop->custom_paint, false); paint_proj_redraw(C, pop->custom_paint, true); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index 75834e019f5..f3798c9af1e 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -4461,7 +4461,10 @@ static void project_paint_begin(const bContext *C, /* At the moment this is just ps->arena_mt[0], but use this to show were not multi-threading. */ MemArena *arena; - const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush); + Object *ob = CTX_data_active_object(C); + bool use_brush_channels = paint_use_channels(C); + + const int diameter = 2 * BKE_brush_size_get(ps->scene, ps->brush, use_brush_channels); bool reset_threads = false; @@ -5908,8 +5911,11 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m ProjStrokeHandle *ps_handle; Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; + ePaintMode paintmode = BKE_paintmode_get_active_from_context(C); char symmetry_flag_views[ARRAY_SIZE(ps_handle->ps_views)] = {0}; + bool use_brush_channels = paint_use_channels(C); + ps_handle = MEM_callocN(sizeof(ProjStrokeHandle), "ProjStrokeHandle"); ps_handle->scene = scene; ps_handle->brush = BKE_paint_brush(&settings->imapaint.paint); @@ -5921,7 +5927,7 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m return ps_handle; } - ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush); + ps_handle->orig_brush_size = BKE_brush_size_get(scene, ps_handle->brush, use_brush_channels); Mesh *mesh = BKE_mesh_from_object(ob); ps_handle->symmetry_flags = mesh->symmetry; @@ -5963,8 +5969,8 @@ void *paint_proj_new_stroke(bContext *C, Object *ob, const float mouse[2], int m } /* Don't allow brush size below 2 */ - if (BKE_brush_size_get(scene, ps_handle->brush) < 2) { - BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize); + if (BKE_brush_size_get(scene, ps_handle->brush, use_brush_channels) < 2) { + BKE_brush_size_set(scene, ps_handle->brush, 2 * U.pixelsize, paintmode == PAINT_MODE_SCULPT); } /* allocate and initialize spatial data structures */ @@ -6036,7 +6042,7 @@ void paint_proj_stroke_done(void *ps_handle_p) PROJ_PAINT_STATE_SHARED_CLEAR(ps_handle->ps_views[i]); } - BKE_brush_size_set(scene, ps_handle->brush, ps_handle->orig_brush_size); + BKE_brush_size_set(scene, ps_handle->brush, ps_handle->orig_brush_size, false); paint_brush_exit_tex(ps_handle->brush); @@ -6068,6 +6074,8 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + bool use_brush_channels = paint_use_channels(C); + if (!ED_paint_proj_mesh_data_check(scene, ob, &uvs, &mat, &tex, NULL)) { ED_paint_data_warning(op->reports, uvs, mat, tex, true); WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL); @@ -6119,9 +6127,9 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) ps.is_texbrush = false; ps.is_maskbrush = false; ps.do_masking = false; - orig_brush_size = BKE_brush_size_get(scene, ps.brush); + orig_brush_size = BKE_brush_size_get(scene, ps.brush, use_brush_channels); /* cover the whole image */ - BKE_brush_size_set(scene, ps.brush, 32 * U.pixelsize); + BKE_brush_size_set(scene, ps.brush, 32 * U.pixelsize, use_brush_channels); /* so pixels are initialized with minimal info */ ps.tool = PAINT_TOOL_DRAW; @@ -6132,7 +6140,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) project_paint_begin(C, &ps, false, 0); if (ps.me_eval == NULL) { - BKE_brush_size_set(scene, ps.brush, orig_brush_size); + BKE_brush_size_set(scene, ps.brush, orig_brush_size, use_brush_channels); BKE_report(op->reports, RPT_ERROR, "Could not get valid evaluated mesh"); return OPERATOR_CANCELLED; } @@ -6157,7 +6165,7 @@ static int texture_paint_camera_project_exec(bContext *C, wmOperator *op) ED_image_undo_push_end(); scene->toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; - BKE_brush_size_set(scene, ps.brush, orig_brush_size); + BKE_brush_size_set(scene, ps.brush, orig_brush_size, false); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/sculpt_paint/paint_intern.h b/source/blender/editors/sculpt_paint/paint_intern.h index 4f5187a71d0..1fea57a011e 100644 --- a/source/blender/editors/sculpt_paint/paint_intern.h +++ b/source/blender/editors/sculpt_paint/paint_intern.h @@ -170,13 +170,13 @@ void paint_cursor_start(struct Paint *p, bool (*poll)(struct bContext *C)); void paint_cursor_delete_textures(void); /** -* used by various actions that have their own spacing that -* is coarser then the brush spacing. e.g. sculpt dyntopo. -* -* \param state: pointer to a float used for internal state, should be initialized to zero at start of stroke -* \return false if the action should be skipped. -* -*/ + * used by various actions that have their own spacing that + * is coarser then the brush spacing. e.g. sculpt dyntopo. + * + * \param state: pointer to a float used for internal state, should be initialized to zero at start + * of stroke \return false if the action should be skipped. + * + */ bool paint_stroke_apply_subspacing(struct PaintStroke *stroke, const float spacing, const enum ePaintMode mode, @@ -455,5 +455,7 @@ enum eBlurKernelType; BlurKernel *paint_new_blur_kernel(struct Brush *br, bool proj); void paint_delete_blur_kernel(BlurKernel *); +#define paint_use_channels(C) (BKE_paintmode_get_active_from_context(C) == PAINT_MODE_SCULPT) + /* paint curve defines */ #define PAINT_CURVE_NUM_SEGMENTS 40 diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index f08771292a8..59260ddbbb4 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -143,10 +143,14 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op) // Object *ob = CTX_data_active_object(C); float scalar = RNA_float_get(op->ptr, "scalar"); + bool use_brush_channels = paint_use_channels(C); + Object *ob = CTX_data_active_object(C); + if (brush) { /* pixel radius */ { - const int old_size = (!is_gpencil) ? BKE_brush_size_get(scene, brush) : brush->size; + const int old_size = (!is_gpencil) ? BKE_brush_size_get(scene, brush, use_brush_channels) : + brush->size; int size = (int)(scalar * old_size); if (abs(old_size - size) < U.pixelsize) { @@ -164,7 +168,7 @@ static int brush_scale_size_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - BKE_brush_size_set(scene, brush, size); + BKE_brush_size_set(scene, brush, size, use_brush_channels); } /* unprojected radius */ diff --git a/source/blender/editors/sculpt_paint/paint_stroke.c b/source/blender/editors/sculpt_paint/paint_stroke.c index ffa8714d162..a6913477e22 100644 --- a/source/blender/editors/sculpt_paint/paint_stroke.c +++ b/source/blender/editors/sculpt_paint/paint_stroke.c @@ -301,8 +301,8 @@ static bool paint_brush_update(bContext *C, ups->stroke_active = true; ups->size_pressure_value = stroke->cached_size_pressure; - ups->pixel_radius = BKE_brush_size_get(scene, brush); - ups->initial_pixel_radius = BKE_brush_size_get(scene, brush); + ups->pixel_radius = BKE_brush_size_get(scene, brush, mode == PAINT_MODE_SCULPT); + ups->initial_pixel_radius = BKE_brush_size_get(scene, brush, mode == PAINT_MODE_SCULPT); if (BKE_brush_use_size_pressure(brush) && paint_supports_dynamic_size(brush, mode)) { ups->pixel_radius *= stroke->cached_size_pressure; @@ -637,7 +637,8 @@ static float paint_space_stroke_spacing(bContext *C, BKE_curvemapping_init(brush->pressure_size_curve); final_size_pressure = BKE_curvemapping_evaluateF(brush->pressure_size_curve, 0, size_pressure); } - float size = BKE_brush_size_get(scene, stroke->brush) * final_size_pressure; + float size = BKE_brush_size_get(scene, stroke->brush, mode == PAINT_MODE_SCULPT) * + final_size_pressure; if (paint_stroke_use_scene_spacing(brush, mode)) { if (!BKE_brush_use_locked_size(scene, brush)) { float last_object_space_position[3]; @@ -729,7 +730,7 @@ static float paint_space_get_final_size_intern( bContext *C, const Scene *scene, PaintStroke *stroke, float pressure, float dpressure) { ePaintMode mode = BKE_paintmode_get_active_from_context(C); - float size = BKE_brush_size_get(scene, stroke->brush) * pressure; + float size = BKE_brush_size_get(scene, stroke->brush, mode == PAINT_MODE_SCULPT) * pressure; if (paint_stroke_use_scene_spacing(stroke->brush, mode)) { if (!BKE_brush_use_locked_size(scene, stroke->brush)) { @@ -1059,7 +1060,7 @@ static bool sculpt_is_grab_tool(Brush *br) SCULPT_TOOL_POSE, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_THUMB, - SCULPT_TOOL_ARRAY, + SCULPT_TOOL_ARRAY, SCULPT_TOOL_ROTATE, SCULPT_TOOL_SNAKE_HOOK); } diff --git a/source/blender/editors/sculpt_paint/paint_vertex.c b/source/blender/editors/sculpt_paint/paint_vertex.c index 51b96fb9c99..c5d6311793f 100644 --- a/source/blender/editors/sculpt_paint/paint_vertex.c +++ b/source/blender/editors/sculpt_paint/paint_vertex.c @@ -1486,7 +1486,7 @@ static void vwpaint_update_cache_invariants( StrokeCache *cache; Scene *scene = CTX_data_scene(C); UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; - const Brush *brush = vp->paint.brush; + Brush *brush = vp->paint.brush; ViewContext *vc = paint_stroke_view_context(op->customdata); Object *ob = CTX_data_active_object(C); float mat[3][3]; @@ -1571,7 +1571,7 @@ static void vwpaint_update_cache_variants(bContext *C, VPaint *vp, Object *ob, P /* Truly temporary data that isn't stored in properties */ if (cache->first_time) { cache->initial_radius = paint_calc_object_space_radius( - cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); + cache->vc, cache->true_location, BKE_brush_size_get(scene, brush, true)); BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); } @@ -1758,7 +1758,7 @@ static void get_brush_alpha_data(const Scene *scene, float *r_brush_alpha_value, float *r_brush_alpha_pressure) { - *r_brush_size_pressure = BKE_brush_size_get(scene, brush) * + *r_brush_size_pressure = BKE_brush_size_get(scene, brush, true) * (BKE_brush_use_size_pressure(brush) ? ss->cache->pressure : 1.0f); *r_brush_alpha_value = BKE_brush_alpha_get(scene, brush); *r_brush_alpha_pressure = (BKE_brush_use_alpha_pressure(brush) ? ss->cache->pressure : 1.0f); diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index dd1ab2ef557..8203ace7bec 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -56,6 +56,7 @@ #include "BKE_attribute.h" #include "BKE_brush.h" +#include "BKE_brush_engine.h" #include "BKE_ccg.h" #include "BKE_colortools.h" #include "BKE_context.h" @@ -123,6 +124,15 @@ static bool sculpt_check_boundary_vertex_in_base_mesh(const SculptSession *ss, const SculptVertRef index); +typedef void (*BrushActionFunc)(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups); + +void sculpt_combine_proxies(Sculpt *sd, Object *ob); +static void SCULPT_run_command_list( + Sculpt *sd, Object *ob, Brush *brush, BrushCommandList *list, UnifiedPaintSettings *ups); +static void do_symmetrical_brush_actions(Sculpt *sd, + Object *ob, + BrushActionFunc action, + UnifiedPaintSettings *ups); /* Sculpt PBVH abstraction API * @@ -3567,6 +3577,7 @@ static float brush_strength(const Sculpt *sd, } float pressure = BKE_brush_use_alpha_pressure(brush) ? cache->pressure : 1.0f; + if (brush->pressure_strength_curve) { } BKE_curvemapping_init(brush->pressure_strength_curve); @@ -8375,6 +8386,26 @@ static void do_brush_action_task_cb(void *__restrict userdata, void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups) { SculptSession *ss = ob->sculpt; + + if (ELEM(brush->sculpt_tool, + SCULPT_TOOL_DRAW, + SCULPT_TOOL_DRAW_SHARP, + SCULPT_TOOL_CLAY_STRIPS, + SCULPT_TOOL_CLAY, + SCULPT_TOOL_CREASE, + SCULPT_TOOL_CLOTH)) { + // SCULPT_run_command_list + BrushCommandList *list = BKE_brush_commandlist_create(); + BKE_builtin_commandlist_create( + brush, ss->cache->channels_final, list, brush->sculpt_tool, &ss->cache->input_mapping); + + SCULPT_run_command_list(sd, ob, brush, list, ups); + + BKE_brush_commandlist_free(list); + + return; + } + int totnode; PBVHNode **nodes; @@ -8779,6 +8810,460 @@ void do_brush_action(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings ups->last_stroke_valid = true; } +void BKE_brush_commandlist_start(BrushCommandList *list, + Brush *brush, + BrushChannelSet *chset_final); + +ATTR_NO_OPT static void SCULPT_run_command_list( + Sculpt *sd, Object *ob, Brush *brush, BrushCommandList *list, UnifiedPaintSettings *ups) +{ + SculptSession *ss = ob->sculpt; + int totnode; + PBVHNode **nodes; + + float start_radius = ss->cache->radius; + + float radius_scale = 1.0f; + float radius_max = 0.0f; + + BKE_brush_commandlist_start(list, brush, ss->cache->channels_final); + + // this does a more high-level check then SCULPT_TOOL_HAS_DYNTOPO; + bool has_dyntopo = SCULPT_stroke_is_dynamic_topology(ss, brush); + bool all_nodes_undo = false; + bool cloth_nodes_undo = false; + + // get maximum radius + for (int i = 0; i < list->totcommand; i++) { + BrushCommand *cmd = list->commands + i; + + Brush brush2 = *brush; + brush2.sculpt_tool = cmd->tool; + + // Load parameters into brush2 for compatibility with old code + BKE_brush_channelset_compat_load(cmd->params_final, &brush2, false); + + /* With these options enabled not all required nodes are inside the original brush radius, so + * the brush can produce artifacts in some situations. */ + if (cmd->tool == SCULPT_TOOL_DRAW && + BKE_brush_channelset_get_int(cmd->params_final, "ORIGINAL_NORMAL")) { + radius_scale = MAX2(radius_scale, 2.0f); + } + + if (SCULPT_tool_needs_all_pbvh_nodes(&brush2)) { + all_nodes_undo = true; + } + else if (cmd->tool == SCULPT_TOOL_CLOTH) { + cloth_nodes_undo = true; + } + + if (!SCULPT_TOOL_HAS_DYNTOPO(cmd->tool)) { + has_dyntopo = false; + } + + float radius = BKE_brush_channelset_get_float( + ss->cache->channels_final, "RADIUS", &ss->cache->input_mapping); + + radius_max = max_ff(radius_max, radius); + } + + float ratio = radius_max / BKE_brush_channelset_get_float( + ss->cache->channels_final, "RADIUS", &ss->cache->input_mapping); + + ss->cache->radius = start_radius * ratio; + ss->cache->radius_squared = start_radius * start_radius * ratio * ratio; + + /* Check for unsupported features. */ + PBVHType type = BKE_pbvh_type(ss->pbvh); + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR) && + !ELEM(type, PBVH_BMESH, PBVH_FACES)) { + return; + } + + if (brush->sculpt_tool == SCULPT_TOOL_ARRAY && type != PBVH_FACES) { + return; + } + + /* Build a list of all nodes that are potentially within the brush's area of influence */ + const bool use_original = sculpt_tool_needs_original(brush->sculpt_tool) ? true : + ss->cache->original; + + if (all_nodes_undo) { + /* These brushes need to update all nodes as they are not constrained by the brush radius */ + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + } + else if (cloth_nodes_undo) { + // + float initial_radius = ss->cache->initial_radius; + float old_radius = ss->cache->radius; + + float radius = ss->cache->initial_radius; + + radius = max_ff(radius, ss->cache->radius); + radius *= radius_scale; + + ss->cache->initial_radius = radius; + ss->cache->radius = radius; + ss->cache->radius_squared = radius * radius; + + nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode); + + ss->cache->initial_radius = initial_radius; + ss->cache->radius = old_radius; + ss->cache->radius_squared = old_radius * old_radius; + } + else { + nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); + } + + /* Draw Face Sets in draw mode makes a single undo push, in alt-smooth mode deforms the + * vertices and uses regular coords undo. */ + /* It also assigns the paint_face_set here as it needs to be done regardless of the stroke type + * and the number of nodes under the brush influence. */ + if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS && + SCULPT_stroke_is_first_brush_step(ss->cache) && !ss->cache->alt_smooth) { + + // faceset undo node is created below for pbvh_bmesh + if (BKE_pbvh_type(ss->pbvh) != PBVH_BMESH) { + SCULPT_undo_push_node(ob, NULL, SCULPT_UNDO_FACE_SETS); + } + + if (ss->cache->invert) { + /* When inverting the brush, pick the paint face mask ID from the mesh. */ + ss->cache->paint_face_set = SCULPT_active_face_set_get(ss); + } + else { + /* By default create a new Face Sets. */ + ss->cache->paint_face_set = SCULPT_face_set_next_available_get(ss); + } + } + + /* Initialize automasking cache. For anchored brushes with spherical falloff, we start off with + * zero radius, thus we have no pbvh nodes on the first brush step. */ + if (totnode || + ((brush->falloff_shape == PAINT_FALLOFF_SHAPE_SPHERE) && (brush->flag & BRUSH_ANCHORED))) { + if (SCULPT_stroke_is_first_brush_step(ss->cache)) { + if (SCULPT_is_automasking_enabled(sd, ss, brush)) { + ss->cache->automasking = SCULPT_automasking_cache_init(sd, brush, ob); + } + } + } + + /* Only act if some verts are inside the brush area. */ + if (totnode == 0) { + ss->cache->radius = start_radius; + ss->cache->radius_squared = start_radius * start_radius; + + return; + } + float location[3]; + + // dyntopo can't push undo nodes inside a thread + if (ss->bm) { + if (ELEM(brush->sculpt_tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) { + for (int i = 0; i < totnode; i++) { + int other = brush->vcol_boundary_factor > 0.0f ? SCULPT_UNDO_COORDS : -1; + + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COLOR, other); + BKE_pbvh_node_mark_update_color(nodes[i]); + } + } + else if (brush->sculpt_tool == SCULPT_TOOL_DRAW_FACE_SETS) { + for (int i = 0; i < totnode; i++) { + if (ss->cache->alt_smooth) { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, SCULPT_UNDO_COORDS); + } + else { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_FACE_SETS, -1); + } + + BKE_pbvh_node_mark_update(nodes[i]); + } + } + else { + for (int i = 0; i < totnode; i++) { + SCULPT_ensure_dyntopo_node_undo(ob, nodes[i], SCULPT_UNDO_COORDS, -1); + + BKE_pbvh_node_mark_update(nodes[i]); + } + } + } + else { + SculptThreadedTaskData task_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, &task_data, do_brush_action_task_cb, &settings); + } + + Brush *oldbrush = ss->cache->brush; + Brush _dummy = *brush, *brush2 = &_dummy; + + for (int step = 0; step < list->totcommand; step++) { + BrushCommand *cmd = list->commands + step; + + *brush2 = *brush; + + ss->cache->brush = brush2; + + // Load parameters into brush2 for compatibility with old code + BKE_brush_channelset_compat_load(cmd->params_final, brush2, false); + + brush2->sculpt_tool = cmd->tool; + + /*Search PBVH*/ + if (step > 0) { + MEM_SAFE_FREE(nodes); + + if (SCULPT_tool_needs_all_pbvh_nodes(brush)) { + /* These brushes need to update all nodes as they are not constrained by the brush radius + */ + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + } + else if (brush->sculpt_tool == SCULPT_TOOL_CLOTH) { + nodes = SCULPT_cloth_brush_affected_nodes_gather(ss, brush, &totnode); + } + else { + /* With these options enabled not all required nodes are inside the original brush radius, + * so the brush can produce artifacts in some situations. */ + if (brush->sculpt_tool == SCULPT_TOOL_DRAW && brush->flag & BRUSH_ORIGINAL_NORMAL) { + radius_scale = MAX2(radius_scale, 2.0f); + } + nodes = sculpt_pbvh_gather_generic(ob, sd, brush, use_original, radius_scale, &totnode); + } + } + + if (sculpt_brush_needs_normal(ss, brush2)) { + update_sculpt_normal(sd, ob, nodes, totnode); + } + + if (brush2->mtex.brush_map_mode == MTEX_MAP_MODE_AREA) { + update_brush_local_mat(sd, ob); + } + + if (brush2->sculpt_tool == SCULPT_TOOL_POSE && SCULPT_stroke_is_first_brush_step(ss->cache)) { + SCULPT_pose_brush_init(sd, ob, ss, brush2); + } + + if (brush2->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) { + if (!ss->cache->cloth_sim) { + ss->cache->cloth_sim = SCULPT_cloth_brush_simulation_create( + ss, 1.0f, 1.0f, 0.0f, false, true); + SCULPT_cloth_brush_simulation_init(ss, ss->cache->cloth_sim); + } + SCULPT_cloth_brush_store_simulation_state(ss, ss->cache->cloth_sim); + SCULPT_cloth_brush_ensure_nodes_constraints( + sd, ob, nodes, totnode, ss->cache->cloth_sim, ss->cache->location, FLT_MAX); + } + + bool invert = ss->cache->pen_flip || ss->cache->invert || brush2->flag & BRUSH_DIR_IN; + + SCULPT_replay_log_append(sd, ss, ob); + + /* Apply one type of brush action. */ + switch (brush2->sculpt_tool) { + case SCULPT_TOOL_DRAW: + do_draw_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SMOOTH: + if (brush2->smooth_deform_type == BRUSH_SMOOTH_DEFORM_LAPLACIAN) { + SCULPT_do_smooth_brush(sd, ob, nodes, totnode, brush2->autosmooth_projection); + } + else if (brush2->smooth_deform_type == BRUSH_SMOOTH_DEFORM_SURFACE) { + SCULPT_do_surface_smooth_brush(sd, ob, nodes, totnode); + } + else if (brush2->smooth_deform_type == BRUSH_SMOOTH_DEFORM_DIRECTIONAL) { + SCULPT_do_directional_smooth_brush(sd, ob, nodes, totnode); + } + else if (brush2->smooth_deform_type == BRUSH_SMOOTH_DEFORM_UNIFORM_WEIGHTS) { + SCULPT_do_uniform_weights_smooth_brush(sd, ob, nodes, totnode); + } + break; + case SCULPT_TOOL_CREASE: + do_crease_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_BLOB: + do_crease_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_PINCH: + do_pinch_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_INFLATE: + do_inflate_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_GRAB: + do_grab_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_ROTATE: + do_rotate_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SNAKE_HOOK: + do_snake_hook_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_NUDGE: + do_nudge_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_THUMB: + do_thumb_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_LAYER: + do_layer_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_FLATTEN: + do_flatten_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLAY: + do_clay_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLAY_STRIPS: + do_clay_strips_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_TWIST: + do_twist_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); + break; + case SCULPT_TOOL_FILL: + if (invert && brush2->flag & BRUSH_INVERT_TO_SCRAPE_FILL) { + do_scrape_brush(sd, ob, nodes, totnode); + } + else { + do_fill_brush(sd, ob, nodes, totnode); + } + break; + case SCULPT_TOOL_SCRAPE: + if (invert && brush2->flag & BRUSH_INVERT_TO_SCRAPE_FILL) { + do_fill_brush(sd, ob, nodes, totnode); + } + else { + do_scrape_brush(sd, ob, nodes, totnode); + } + break; + case SCULPT_TOOL_MASK: + 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); + break; + case SCULPT_TOOL_ELASTIC_DEFORM: + do_elastic_deform_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SLIDE_RELAX: + do_slide_relax_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_BOUNDARY: + SCULPT_do_boundary_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_CLOTH: + SCULPT_do_cloth_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_DRAW_FACE_SETS: + SCULPT_do_draw_face_sets_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_DISPLACEMENT_ERASER: + do_displacement_eraser_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_DISPLACEMENT_SMEAR: + do_displacement_smear_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_PAINT: + SCULPT_do_paint_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SMEAR: + SCULPT_do_smear_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_FAIRING: + do_fairing_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SCENE_PROJECT: + do_scene_project_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_SYMMETRIZE: + SCULPT_do_symmetrize_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_ARRAY: + SCULPT_do_array_brush(sd, ob, nodes, totnode); + case SCULPT_TOOL_VCOL_BOUNDARY: + SCULPT_smooth_vcol_boundary(sd, ob, nodes, totnode, ss->cache->bstrength); + break; + case SCULPT_TOOL_UV_SMOOTH: + SCULPT_uv_brush(sd, ob, nodes, totnode); + break; + case SCULPT_TOOL_TOPOLOGY_RAKE: + bmesh_topology_rake(sd, ob, nodes, totnode, brush2->alpha); + break; + case SCULPT_TOOL_DYNTOPO: + if (has_dyntopo) { + do_symmetrical_brush_actions(sd, ob, sculpt_topology_update, ups); + } + break; + } + + if (ss->needs_pbvh_rebuild) { + bContext *C = ss->cache->vc->C; + + /* The mesh was modified, rebuild the PBVH. */ + BKE_particlesystem_reset_all(ob); + BKE_ptcache_object_reset(CTX_data_scene(C), ob, PTCACHE_RESET_OUTDATED); + + DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); + BKE_scene_graph_update_tagged(CTX_data_ensure_evaluated_depsgraph(C), CTX_data_main(C)); + SCULPT_pbvh_clear(ob); + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, false, false); + + if (brush->sculpt_tool == SCULPT_TOOL_ARRAY) { + SCULPT_tag_update_overlays(C); + } + ss->needs_pbvh_rebuild = false; + } + + sculpt_combine_proxies(sd, ob); + } + + /* + paint_stroke_apply_subspacing( + ss->cache->stroke, + spacing, + PAINT_MODE_SCULPT, + &ss->cache->last_smooth_t[SCULPT_get_symmetry_pass(ss)]); + + */ + + /* The cloth brush adds the gravity as a regular force and it is processed in the solver. */ + if (ss->cache->supports_gravity && !ELEM(brush->sculpt_tool, + SCULPT_TOOL_CLOTH, + SCULPT_TOOL_DRAW_FACE_SETS, + SCULPT_TOOL_BOUNDARY)) { + do_gravity(sd, ob, nodes, totnode, sd->gravity_factor); + } + + if (brush->deform_target == BRUSH_DEFORM_TARGET_CLOTH_SIM) { + if (SCULPT_stroke_is_main_symmetry_pass(ss->cache)) { + SCULPT_cloth_sim_activate_nodes(ss->cache->cloth_sim, nodes, totnode); + SCULPT_cloth_brush_do_simulation_step(sd, ob, ss->cache->cloth_sim, nodes, totnode); + } + } + + ss->cache->brush = oldbrush; + ss->cache->radius = start_radius; + ss->cache->radius_squared = start_radius * start_radius; + + MEM_SAFE_FREE(nodes); +} + /* Flush displacement from deformed PBVH vertex to original mesh. */ static void sculpt_flush_pbvhvert_deform(Object *ob, PBVHVertexIter *vd) { @@ -9082,8 +9567,6 @@ void SCULPT_cache_calc_brushdata_symm(StrokeCache *cache, } } -typedef void (*BrushActionFunc)(Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups); - static void do_tiled( Sculpt *sd, Object *ob, Brush *brush, UnifiedPaintSettings *ups, BrushActionFunc action) { @@ -9214,7 +9697,7 @@ static void do_symmetrical_brush_actions(Sculpt *sd, static void sculpt_update_tex(const Scene *scene, Sculpt *sd, SculptSession *ss) { Brush *brush = BKE_paint_brush(&sd->paint); - const int radius = BKE_brush_size_get(scene, brush); + const int radius = BKE_brush_size_get(scene, brush, true); MEM_SAFE_FREE(ss->texcache); @@ -9390,6 +9873,10 @@ void SCULPT_cache_free(SculptSession *ss, StrokeCache *cache) ss, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3, ss->cache->prefairing_co); } + if (ss->cache->channels_final) { + BKE_brush_channelset_free(ss->cache->channels_final); + } + MEM_SAFE_FREE(ss->cache->fairing_mask); MEM_SAFE_FREE(ss->cache->fairing_fade); MEM_SAFE_FREE(ss->cache->prefairing_co); @@ -9542,7 +10029,7 @@ static void sculpt_update_cache_invariants( else { Paint *p = &sd->paint; Brush *br; - int size = BKE_brush_size_get(scene, brush); + int size = BKE_brush_size_get(scene, brush, true); BLI_strncpy(cache->saved_active_brush_name, brush->id.name + 2, @@ -9552,8 +10039,8 @@ static void sculpt_update_cache_invariants( if (br) { BKE_paint_brush_set(p, br); brush = br; - cache->saved_smooth_size = BKE_brush_size_get(scene, brush); - BKE_brush_size_set(scene, brush, size); + cache->saved_smooth_size = BKE_brush_size_get(scene, brush, true); + BKE_brush_size_set(scene, brush, size, paint_use_channels(C)); BKE_curvemapping_init(brush->curve); } } @@ -9921,16 +10408,41 @@ static void sculpt_update_cache_variants(bContext *C, Sculpt *sd, Object *ob, Po * events. We should avoid this after events system re-design. */ if (paint_supports_dynamic_size(brush, PAINT_MODE_SCULPT) || cache->first_time) { cache->pressure = RNA_float_get(ptr, "pressure"); + cache->input_mapping.pressure = cache->pressure; } cache->x_tilt = RNA_float_get(ptr, "x_tilt"); cache->y_tilt = RNA_float_get(ptr, "y_tilt"); + cache->input_mapping.xtilt = cache->x_tilt; + cache->input_mapping.ytilt = cache->y_tilt; + + { + float direction[4]; + 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); + + /* If the active area is being applied for symmetry, flip it + * across the symmetry axis and rotate it back to the original + * position in order to project it. This insures that the + * brush texture will be oriented correctly. */ + direction[3] = 0.0f; + mul_v4_m4v4(direction, cache->projection_mat, direction); + + cache->input_mapping.angle = atan2(direction[1], direction[0]); + + // cache->vc + } /* Truly temporary data that isn't stored in properties. */ if (SCULPT_stroke_is_first_brush_step_of_symmetry_pass(ss->cache)) { if (!BKE_brush_use_locked_size(scene, brush)) { cache->initial_radius = paint_calc_object_space_radius( - cache->vc, cache->true_location, BKE_brush_size_get(scene, brush)); + cache->vc, cache->true_location, BKE_brush_size_get(scene, brush, true)); BKE_brush_unprojected_radius_set(scene, brush, cache->initial_radius); } else { @@ -10280,7 +10792,8 @@ bool SCULPT_cursor_geometry_info_update(bContext *C, ss->v3d = vc.v3d; if (!BKE_brush_use_locked_size(scene, brush)) { - radius = paint_calc_object_space_radius(&vc, out->location, BKE_brush_size_get(scene, brush)); + radius = paint_calc_object_space_radius( + &vc, out->location, BKE_brush_size_get(scene, brush, true)); } else { radius = BKE_brush_unprojected_radius_get(scene, brush); @@ -10699,12 +11212,31 @@ static bool sculpt_stroke_test_start(bContext *C, struct wmOperator *op, const f void sculpt_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; Sculpt *sd = CTX_data_tool_settings(C)->sculpt; Object *ob = CTX_data_active_object(C); SculptSession *ss = ob->sculpt; Brush *brush = BKE_paint_brush(&sd->paint); + extern void BKE_brush_channelset_to_unified_settings(BrushChannelSet * chset, + UnifiedPaintSettings * ups); + if (ss->cache->channels_final) { + BKE_brush_channelset_free(ss->cache->channels_final); + } + + if (brush->channels && sd->channels) { + ss->cache->channels_final = BKE_brush_channelset_create(); + BKE_brush_channelset_merge(ss->cache->channels_final, brush->channels, sd->channels); + } + else if (brush->channels) { + ss->cache->channels_final = BKE_brush_channelset_copy(brush->channels); + } + + // load settings into brush and unified paint settings + BKE_brush_channelset_compat_load(ss->cache->channels_final, brush, false); + BKE_brush_channelset_to_unified_settings(ss->cache->channels_final, ups); + ss->cache->stroke_distance = stroke->stroke_distance; ss->cache->stroke_distance_t = stroke->stroke_distance_t; ss->cache->stroke = stroke; @@ -10873,7 +11405,7 @@ static void sculpt_stroke_done(const bContext *C, struct PaintStroke *UNUSED(str /* Do nothing. */ } else { - BKE_brush_size_set(scene, brush, ss->cache->saved_smooth_size); + BKE_brush_size_set(scene, brush, ss->cache->saved_smooth_size, true); brush = (Brush *)BKE_libblock_find_name(bmain, ID_BR, ss->cache->saved_active_brush_name); if (brush) { BKE_paint_brush_set(&sd->paint, brush); diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index 7eb1707c6d8..4b7164e9829 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -37,6 +37,7 @@ #include "ED_view3d.h" #include "BKE_attribute.h" +#include "BKE_brush_engine.h" #include "BKE_paint.h" #include "BKE_pbvh.h" @@ -47,6 +48,7 @@ struct KeyBlock; struct Object; struct SculptUndoNode; struct bContext; +struct BrushChannelSet; enum ePaintSymmetryFlags; @@ -1082,6 +1084,8 @@ typedef struct AutomaskingCache { } AutomaskingCache; typedef struct StrokeCache { + BrushMappingData input_mapping; + /* Invariants */ float initial_radius; float scale[3]; @@ -1089,6 +1093,8 @@ typedef struct StrokeCache { float clip_tolerance[3]; float initial_mouse[2]; + struct BrushChannelSet *channels_final; + /* Variants */ float radius; float radius_squared; @@ -1135,7 +1141,7 @@ typedef struct StrokeCache { /* Clean this up! */ struct ViewContext *vc; - const struct Brush *brush; + struct Brush *brush; float special_rotation; float grab_delta[3], grab_delta_symmetry[3]; diff --git a/source/blender/editors/sculpt_paint/sculpt_smooth.c b/source/blender/editors/sculpt_paint/sculpt_smooth.c index 68c551b5bfe..4afd94303aa 100644 --- a/source/blender/editors/sculpt_paint/sculpt_smooth.c +++ b/source/blender/editors/sculpt_paint/sculpt_smooth.c @@ -485,16 +485,6 @@ void vec_transform(float r_dir2[3], float no[3], int bits) } } -volatile int blehrand = 0; -static int blehrand_get() -{ - int i = blehrand; - i = (i * 124325 + 231423322) & 524287; - - blehrand = i; - return i; -} - /* For bmesh: Average surrounding verts based on an orthogonality measure. * Naturally converges to a quad-like structure. */ void SCULPT_bmesh_four_neighbor_average(SculptSession *ss, diff --git a/source/blender/editors/sculpt_paint/sculpt_uv.c b/source/blender/editors/sculpt_paint/sculpt_uv.c index e5ca5e4defd..0a990f161f0 100644 --- a/source/blender/editors/sculpt_paint/sculpt_uv.c +++ b/source/blender/editors/sculpt_paint/sculpt_uv.c @@ -329,7 +329,7 @@ static void uv_sculpt_stroke_apply(bContext *C, ED_space_image_get_size(sima, &width, &height); ED_space_image_get_zoom(sima, region, &zoomx, &zoomy); - radius = BKE_brush_size_get(scene, brush) / (width * zoomx); + radius = BKE_brush_size_get(scene, brush, true) / (width * zoomx); aspectRatio = width / (float)height; /* We will compare squares to save some computation */ @@ -696,7 +696,7 @@ static UvSculptData *uv_sculpt_stroke_init(bContext *C, wmOperator *op, const wm alpha = BKE_brush_alpha_get(scene, brush); - radius = BKE_brush_size_get(scene, brush); + radius = BKE_brush_size_get(scene, brush, true); sima = CTX_wm_space_image(C); ED_space_image_get_size(sima, &width, &height); ED_space_image_get_zoom(sima, region, &zoomx, &zoomy); diff --git a/source/blender/makesdna/DNA_color_types.h b/source/blender/makesdna/DNA_color_types.h index debf2c3475e..3c8bc3a5f4a 100644 --- a/source/blender/makesdna/DNA_color_types.h +++ b/source/blender/makesdna/DNA_color_types.h @@ -117,6 +117,9 @@ typedef enum eCurveMappingPreset { CURVE_PRESET_ROOT = 6, CURVE_PRESET_GAUSS = 7, CURVE_PRESET_BELL = 8, + CURVE_PRESET_POW2 = 9, + CURVE_PRESET_POW3 = 10, + CURVE_PRESET_POW15 = 11 } eCurveMappingPreset; /** #CurveMapping.tone */ diff --git a/source/blender/makesdna/DNA_sculpt_brush_types.h b/source/blender/makesdna/DNA_sculpt_brush_types.h index b8d70567520..4f7af3e9bbd 100644 --- a/source/blender/makesdna/DNA_sculpt_brush_types.h +++ b/source/blender/makesdna/DNA_sculpt_brush_types.h @@ -24,6 +24,8 @@ */ #pragma once +#include "DNA_color_types.h" + typedef struct BrushMapping { char name[64]; CurveMapping curve; @@ -86,4 +88,7 @@ enum { BRUSH_CHANNEL_INT = 1 << 1, BRUSH_CHANNEL_ENUM = 1 << 2, BRUSH_CHANNEL_BITMASK = 1 << 3, + BRUSH_CHANNEL_BOOL = 1 << 4, + BRUSH_CHANNEL_VEC3 = 1 << 5, + BRUSH_CHANNEL_VEC4 = 1 << 6 }; diff --git a/source/blender/makesrna/intern/rna_access.c b/source/blender/makesrna/intern/rna_access.c index fceb6d045c3..35eb855e6b1 100644 --- a/source/blender/makesrna/intern/rna_access.c +++ b/source/blender/makesrna/intern/rna_access.c @@ -3457,7 +3457,7 @@ int RNA_property_enum_get(PointerRNA *ptr, PropertyRNA *prop) return eprop->defaultvalue; } -void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value) +ATTR_NO_OPT void RNA_property_enum_set(PointerRNA *ptr, PropertyRNA *prop, int value) { EnumPropertyRNA *eprop = (EnumPropertyRNA *)prop; IDProperty *idprop; diff --git a/source/blender/makesrna/intern/rna_brush_engine.c b/source/blender/makesrna/intern/rna_brush_engine.c index 6cabd177780..30d5bd4b392 100644 --- a/source/blender/makesrna/intern/rna_brush_engine.c +++ b/source/blender/makesrna/intern/rna_brush_engine.c @@ -43,6 +43,8 @@ #include "DNA_sculpt_brush_types.h" #include "WM_types.h" +static EnumPropertyItem null_enum[1] = {{0, "null", 0, 0}, {-1, NULL, -1, -1}}; + #ifdef RNA_RUNTIME int rna_BrushChannelSet_channels_begin(CollectionPropertyIterator *iter, struct PointerRNA *ptr) @@ -100,6 +102,38 @@ void rna_BrushChannel_value_range( } } +int rna_BrushChannel_get_ivalue(PointerRNA *rna) +{ + BrushChannel *ch = rna->data; + + return ch->ivalue; +} +void rna_BrushChannel_set_ivalue(PointerRNA *rna, int value) +{ + BrushChannel *ch = rna->data; + + ch->ivalue = value; +} + +void rna_BrushChannel_ivalue_range( + PointerRNA *rna, int *min, int *max, int *soft_min, int *soft_max) +{ + BrushChannel *ch = rna->data; + + if (ch->def) { + *min = (int)ch->def->min; + *max = (int)ch->def->max; + *soft_min = (int)ch->def->soft_min; + *soft_max = (int)ch->def->soft_max; + } + else { + *min = 0; + *max = 65535; + *soft_min = 0; + *soft_max = 1024; + } +} + PointerRNA rna_BrushMapping_curve_get(PointerRNA *ptr) { BrushMapping *mapping = (BrushMapping *)ptr->data; @@ -152,6 +186,36 @@ int rna_BrushChannel_mappings_length(PointerRNA *ptr) return BRUSH_MAPPING_MAX; } +int rna_BrushChannel_enum_value_get(PointerRNA *ptr) +{ + BrushChannel *ch = (BrushChannel *)ptr->data; + + return ch->ivalue; +} + +int rna_BrushChannel_enum_value_set(PointerRNA *ptr, int val) +{ + BrushChannel *ch = (BrushChannel *)ptr->data; + + ch->ivalue = val; + + return 1; +} + +ATTR_NO_OPT const EnumPropertyItem *rna_BrushChannel_enum_value_get_items(struct bContext *C, + PointerRNA *ptr, + PropertyRNA *prop, + bool *r_free) +{ + BrushChannel *ch = (BrushChannel *)ptr->data; + + if (!ch->def || !ELEM(ch->type, BRUSH_CHANNEL_ENUM, BRUSH_CHANNEL_BITMASK)) { + return null_enum; + } + + return ch->def->enumdef.items; +} + #endif static EnumPropertyItem mapping_type_items[] = { @@ -202,6 +266,9 @@ EnumPropertyItem channel_types[] = {{BRUSH_CHANNEL_FLOAT, "FLOAT", ICON_NONE, "F {BRUSH_CHANNEL_INT, "INT", ICON_NONE, "Int"}, {BRUSH_CHANNEL_ENUM, "ENUM", ICON_NONE, "Enum"}, {BRUSH_CHANNEL_BITMASK, "BITMASK", ICON_NONE, "Bitmask"}, + {BRUSH_CHANNEL_BOOL, "BOOL", ICON_NONE, "Boolean"}, + {BRUSH_CHANNEL_VEC3, "VEC3", ICON_NONE, "Color3"}, + {BRUSH_CHANNEL_VEC4, "VEC4", ICON_NONE, "Color4"}, {-1, NULL, -1, NULL}}; void RNA_def_brush_channel(BlenderRNA *brna) @@ -230,7 +297,41 @@ void RNA_def_brush_channel(BlenderRNA *brna) RNA_def_property_clear_flag(prop, PROP_EDITABLE | PROP_ANIMATABLE); RNA_def_property_ui_text(prop, "Type", "Value Type"); - prop = RNA_def_property(srna, "value", PROP_FLOAT, PROP_NONE); + prop = RNA_def_property(srna, "bool_value", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_sdna(prop, "BrushChannel", "ivalue", 1); + RNA_def_property_ui_text(prop, "Value", "Current value"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + + prop = RNA_def_property(srna, "int_value", PROP_INT, PROP_NONE); + RNA_def_property_int_sdna(prop, "BrushChannel", "ivalue"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text(prop, "Value", "Current value"); + RNA_def_property_int_funcs(prop, + "rna_BrushChannel_get_ivalue", + "rna_BrushChannel_set_ivalue", + "rna_BrushChannel_ivalue_range"); + + prop = RNA_def_property(srna, "float_value", PROP_FLOAT, PROP_NONE); + RNA_def_property_float_sdna(prop, "BrushChannel", "fvalue"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text(prop, "Value", "Current value"); + RNA_def_property_float_funcs(prop, + "rna_BrushChannel_get_value", + "rna_BrushChannel_set_value", + "rna_BrushChannel_value_range"); + + // XXX hack warning: these next two are duplicates of above + // to get different subtypes + prop = RNA_def_property(srna, "factor_value", PROP_FLOAT, PROP_FACTOR); + RNA_def_property_float_sdna(prop, "BrushChannel", "fvalue"); + RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); + RNA_def_property_ui_text(prop, "Value", "Current value"); + RNA_def_property_float_funcs(prop, + "rna_BrushChannel_get_value", + "rna_BrushChannel_set_value", + "rna_BrushChannel_value_range"); + + prop = RNA_def_property(srna, "percent_value", PROP_FLOAT, PROP_PERCENTAGE); RNA_def_property_float_sdna(prop, "BrushChannel", "fvalue"); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); RNA_def_property_ui_text(prop, "Value", "Current value"); @@ -252,7 +353,7 @@ void RNA_def_brush_channel(BlenderRNA *brna) prop = RNA_def_property(srna, "inherit_if_unset", PROP_BOOLEAN, PROP_NONE); RNA_def_property_boolean_sdna(prop, "BrushChannel", "flag", BRUSH_CHANNEL_INHERIT_IF_UNSET); RNA_def_property_override_flag(prop, PROPOVERRIDE_OVERRIDABLE_LIBRARY); - RNA_def_property_ui_text(prop, "Inherit If Unset", "Combine with default settings"); + RNA_def_property_ui_text(prop, "Combine", "Combine with default settings"); prop = RNA_def_property(srna, "mappings", PROP_COLLECTION, PROP_NONE); // RNA_def_property_collection_sdna(prop, "BrushChannel", "mappings", NULL); @@ -266,6 +367,27 @@ void RNA_def_brush_channel(BlenderRNA *brna) "rna_BrushChannel_mappings_lookupstring", "rna_BrushChannel_mappings_assignint"); RNA_def_property_struct_type(prop, "BrushMapping"); + + prop = RNA_def_property(srna, "enum_value", PROP_ENUM, PROP_UNIT_NONE); + RNA_def_property_ui_text(prop, "Enum Value", "Enum values (for enums"); + RNA_def_property_enum_items(prop, null_enum); + RNA_def_property_enum_funcs(prop, + "rna_BrushChannel_enum_value_get", + "rna_BrushChannel_enum_value_set", + "rna_BrushChannel_enum_value_get_items"); + + prop = RNA_def_property(srna, "flags_value", PROP_ENUM, PROP_UNIT_NONE); + RNA_def_property_ui_text(prop, "Flags Value", "Flags values"); + + RNA_def_property_enum_bitflag_sdna(prop, "BrushChannel", "ivalue"); + RNA_def_property_enum_items(prop, null_enum); + RNA_def_property_enum_funcs(prop, + "rna_BrushChannel_enum_value_get", + "rna_BrushChannel_enum_value_set", + "rna_BrushChannel_enum_value_get_items"); + RNA_def_property_flag(prop, PROP_ENUM_FLAG); + + // PROP_ENUM_FLAG } void RNA_def_brush_channelset(BlenderRNA *brna) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index 35acb56e66a..d978b3d6756 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -353,7 +353,7 @@ void pyrna_write_set(bool val) { rna_disallow_writes = !val; } -#else /* USE_PEDANTIC_WRITE */ +#else /* USE_PEDANTIC_WRITE */ bool pyrna_write_check(void) { return true; @@ -775,7 +775,7 @@ PyObject *pyrna_math_object_from_array(PointerRNA *ptr, PropertyRNA *prop) ret = pyrna_prop_CreatePyObject(ptr, prop); /* Owned by the mathutils PyObject. */ } } -#else /* USE_MATHUTILS */ +#else /* USE_MATHUTILS */ (void)ptr; (void)prop; #endif /* USE_MATHUTILS */ @@ -1297,7 +1297,7 @@ static int pyrna_prop_to_enum_bitfield( return ret; } -static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val) +ATTR_NO_OPT static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val) { PyObject *item, *ret = NULL; @@ -1372,7 +1372,7 @@ static PyObject *pyrna_enum_to_py(PointerRNA *ptr, PropertyRNA *prop, int val) return ret; } -PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop) +ATTR_NO_OPT PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop) { PyObject *ret; const int type = RNA_property_type(prop); @@ -1410,7 +1410,7 @@ PyObject *pyrna_prop_to_py(PointerRNA *ptr, PropertyRNA *prop) else { ret = PyUnicode_FromStringAndSize(buf, buf_len); } -#else /* USE_STRING_COERCE */ +#else /* USE_STRING_COERCE */ if (subtype == PROP_BYTESTRING) { ret = PyBytes_FromStringAndSize(buf, buf_len); } @@ -1537,7 +1537,7 @@ static PyObject *pyrna_func_to_py(const PointerRNA *ptr, FunctionRNA *func) return (PyObject *)pyfunc; } -static int pyrna_py_to_prop( +ATTR_NO_OPT static int pyrna_py_to_prop( PointerRNA *ptr, PropertyRNA *prop, void *data, PyObject *value, const char *error_prefix) { /* XXX hard limits should be checked here. */ @@ -1723,7 +1723,7 @@ static int pyrna_py_to_prop( else { param = PyUnicode_AsUTF8(value); } -#else /* USE_STRING_COERCE */ +#else /* USE_STRING_COERCE */ param = PyUnicode_AsUTF8(value); #endif /* USE_STRING_COERCE */ @@ -6507,7 +6507,7 @@ PyTypeObject pyrna_struct_Type = { /* delete references to contained objects */ (inquiry)pyrna_struct_clear, /* inquiry tp_clear; */ #else - NULL, /* traverseproc tp_traverse; */ + NULL, /* traverseproc tp_traverse; */ /* delete references to contained objects */ NULL, /* inquiry tp_clear; */