diff options
Diffstat (limited to 'source/blender/blenkernel/intern')
-rw-r--r-- | source/blender/blenkernel/intern/brush.cc | 116 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush_channel.cc | 1492 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/brush_channel_define.h | 235 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/scene.cc | 16 |
4 files changed, 1856 insertions, 3 deletions
diff --git a/source/blender/blenkernel/intern/brush.cc b/source/blender/blenkernel/intern/brush.cc index 6f1435e90b8..30c9137629b 100644 --- a/source/blender/blenkernel/intern/brush.cc +++ b/source/blender/blenkernel/intern/brush.cc @@ -21,6 +21,7 @@ #include "BKE_bpath.h" #include "BKE_brush.h" +#include "BKE_brush_channel.h" #include "BKE_colortools.h" #include "BKE_context.h" #include "BKE_gpencil.h" @@ -34,6 +35,11 @@ #include "BKE_paint.h" #include "BKE_texture.h" +#include "RNA_access.h" +#include "RNA_path.h" +#include "RNA_prototypes.h" +#include "RNA_types.h" + #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -54,6 +60,9 @@ static void brush_init_data(ID *id) /* the default alpha falloff curve */ BKE_brush_curve_preset(brush, CURVE_PRESET_SMOOTH); + + brush->channels = BKE_brush_channelset_create(); + BKE_brush_channelset_ensure_channels(brush->channels, PAINT_MODE_INVALID, 0); } static void brush_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, const int flag) @@ -100,6 +109,10 @@ static void brush_copy_data(Main * /*bmain*/, ID *id_dst, const ID *id_src, cons brush_dst->curves_sculpt_settings = MEM_cnew(__func__, *(brush_src->curves_sculpt_settings)); } + if (brush_src->channels) { + brush_dst->channels = BKE_brush_channelset_copy(brush_src->channels); + } + /* enable fake user by default */ id_fake_user_set(&brush_dst->id); } @@ -134,6 +147,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) @@ -256,6 +273,10 @@ static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_addres if (brush->gradient) { BLO_write_struct(writer, ColorBand, brush->gradient); } + + if (brush->channels) { + BKE_brush_channelset_blend_write(brush->channels, writer); + } } static void brush_blend_read_data(BlendDataReader *reader, ID *id) @@ -337,6 +358,14 @@ static void brush_blend_read_data(BlendDataReader *reader, ID *id) brush->preview = nullptr; brush->icon_imbuf = nullptr; + + BLO_read_data_address(reader, &brush->channels); + + if (brush->channels) { + BKE_brush_channelset_blend_read(brush->channels, reader); + } + + BKE_brush_channelset_ensure_all_modes(brush); } static void brush_blend_read_lib(BlendLibReader *reader, ID *id) @@ -1707,6 +1736,14 @@ void BKE_brush_sculpt_reset(Brush *br) brush_defaults(br); BKE_brush_curve_preset(br, CURVE_PRESET_SMOOTH); + /* Reset brush channels */ + if (br->channels) { + BKE_brush_channelset_free(br->channels); + } + + br->channels = BKE_brush_channelset_create(); + BKE_brush_channelset_ensure_channels(br->channels, PAINT_MODE_SCULPT, br->sculpt_tool); + /* Use the curve presets by default */ br->curve_preset = BRUSH_CURVE_SMOOTH; @@ -2269,6 +2306,9 @@ 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) { + BKE_brush_int_set_unified(scene, brush, size, size); + +#if 0 UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; /* make sure range is sane */ @@ -2280,14 +2320,20 @@ void BKE_brush_size_set(Scene *scene, Brush *brush, int size) else { brush->size = size; } +#endif } int BKE_brush_size_get(const Scene *scene, const Brush *brush) { + return BKE_brush_int_get_unified(scene, brush, size); + /* theoretically faster: return BKE_brush_eval_int(brush, scene, size, NULL); */ + +#if 0 UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; int size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size; return size; +#endif } bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush) @@ -2298,9 +2344,16 @@ bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush) (brush->flag & BRUSH_LOCK_SIZE); } -bool BKE_brush_use_size_pressure(const Brush *brush) +bool BKE_brush_use_size_pressure(const Scene *scene, const Brush *brush) { - return brush->flag & BRUSH_SIZE_PRESSURE; + if (!BKE_brush_use_locked_size(scene, brush)) { + return BKE_brush_mapping_enabled(scene, brush, size, BRUSH_MAPPING_PRESSURE); + } + else { + return BKE_brush_mapping_enabled(scene, brush, unprojected_radius, BRUSH_MAPPING_PRESSURE); + } + + // return brush->flag & BRUSH_SIZE_PRESSURE; } bool BKE_brush_use_alpha_pressure(const Brush *brush) @@ -2348,21 +2401,27 @@ float BKE_brush_unprojected_radius_get(const Scene *scene, const Brush *brush) void BKE_brush_alpha_set(Scene *scene, Brush *brush, float alpha) { - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + BKE_brush_float_set_unified(scene, brush, strength, alpha); +#if 0 + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; if (ups->flag & UNIFIED_PAINT_ALPHA) { ups->alpha = alpha; } else { brush->alpha = alpha; } +#endif } float BKE_brush_alpha_get(const Scene *scene, const Brush *brush) { + return BKE_brush_float_get_unified(scene, brush, strength); +#if 0 UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; return (ups->flag & UNIFIED_PAINT_ALPHA) ? ups->alpha : brush->alpha; +#endif } float BKE_brush_weight_get(const Scene *scene, const Brush *brush) @@ -2558,3 +2617,54 @@ struct ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool return im; } + +float BKE_brush_curve_strength_ex( + int curve_preset, const CurveMapping *curve, float p, const float len, const bool invert) +{ + float strength = 1.0f; + + if (p >= len) { + return invert ? 0.0f : 1.0f; + } + + p = p / len; + + if (invert) { + p = 1.0f - p; + } + + switch (curve_preset) { + case BRUSH_CURVE_CUSTOM: + strength = BKE_curvemapping_evaluateF(curve, 0, p); + break; + case BRUSH_CURVE_SHARP: + strength = p * p; + break; + case BRUSH_CURVE_SMOOTH: + strength = 3.0f * p * p - 2.0f * p * p * p; + break; + case BRUSH_CURVE_SMOOTHER: + strength = pow3f(p) * (p * (p * 6.0f - 15.0f) + 10.0f); + break; + case BRUSH_CURVE_ROOT: + strength = sqrtf(p); + break; + case BRUSH_CURVE_LIN: + strength = p; + break; + case BRUSH_CURVE_CONSTANT: + strength = 1.0f; + break; + case BRUSH_CURVE_SPHERE: + strength = sqrtf(2 * p - p * p); + break; + case BRUSH_CURVE_POW4: + strength = p * p * p * p; + break; + case BRUSH_CURVE_INVSQUARE: + strength = p * (2.0f - p); + break; + } + + return strength; +} diff --git a/source/blender/blenkernel/intern/brush_channel.cc b/source/blender/blenkernel/intern/brush_channel.cc new file mode 100644 index 00000000000..35fd62ba34d --- /dev/null +++ b/source/blender/blenkernel/intern/brush_channel.cc @@ -0,0 +1,1492 @@ +#include "MEM_guardedalloc.h" + +#include "BLI_assert.h" +#include "BLI_compiler_attrs.h" +#include "BLI_compiler_compat.h" +#include "BLI_ghash.h" +#include "BLI_index_range.hh" +#include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_math.h" +#include "BLI_math_vec_types.hh" +#include "BLI_string_ref.hh" +#include "BLI_string_utils.h" +#include "BLI_vector.hh" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_path.h" +#include "RNA_prototypes.h" + +#include "DNA_brush_channel_types.h" +#include "DNA_brush_enums.h" +#include "DNA_brush_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" + +#include "DNA_color_types.h" +#include "DNA_material_types.h" + +#include "BKE_brush.h" +#include "BKE_brush_channel.h" +#include "BKE_colortools.h" +#include "BKE_idprop.h" +#include "BKE_idprop.hh" +#include "BKE_lib_id.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BLO_read_write.h" + +#include <string> +#include <vector> + +const char builtin_brush_categories[][128] = {"Basic", "Smooth", "Color"}; + +static BrushChannelType empty_brush_type = {"error", "error", "error", "error"}; + +#define BRUSH_CHANNEL_DEFINE_INTERNAL_NAMES +#include "brush_channel_define.h" +#undef BRUSH_CHANNEL_DEFINE_INTERNAL_NAMES + +using string = std::string; + +using KeyString = const char *; +using blender::float3; +using blender::float4; +using blender::IndexRange; +using blender::Map; +using blender::StringRef; +using blender::Vector; + +static Map<StringRef, BrushChannelType> builtin_channels; + +using ChannelNameMap = Map<StringRef, BrushChannel *>; + +ATTR_NO_OPT static void init_builtin_brush_channels() +{ + struct UI { + int mode; + Vector<int> tools; + int uiflag; + bool all_tool_modes = false; + bool all_tools = false; + bool exists = true; + + UI(const UI &b) + { + tools = b.tools; + uiflag = b.uiflag; + all_tool_modes = b.all_tool_modes; + all_tools = b.all_tools; + exists = b.exists; + mode = b.mode; + } + + UI(int _mode, int _uiflag, int _tool) : mode(_mode), uiflag(_uiflag) + { + tools.append(_tool); + } + + UI(int _uiflag) : uiflag(_uiflag) + { + all_tool_modes = all_tools = true; + } + + UI(ePaintMode _mode, int _uiflag) : mode((int)_mode), uiflag(_uiflag) + { + all_tools = true; + } + + UI(ePaintMode mode, int uiflag, int tool) + { + UI::UI((int)mode, uiflag, tool); + } + + UI(ePaintMode _mode, int _uiflag, Vector<int> _tools) + : mode((int)_mode), tools(_tools), uiflag(_uiflag) + { + } + }; + + struct ChannelProp { + char path[512]; + char category[64]; + Vector<UI> extra_uiflags; + int flag; + BrushMappingPreset mappings; + }; + +#ifdef BRUSH_CHANNEL_DEFINE_EXTERNAL +# undef BRUSH_CHANNEL_DEFINE_EXTERNAL +#endif + +#ifdef BRUSH_CHANNEL_DEFINE_INTERNAL_NAMES +# undef BRUSH_CHANNEL_DEFINE_INTERNAL_NAMES +#endif +#ifdef MAKE_PROP +# undef MAKE_PROP +#endif +#ifdef MAKE_PROP_EX +# undef MAKE_PROP_EX +#endif + + //#define MAKE_PROP(idname, category, uiflags) MAKE_PROP_EX(idname, category, uiflags, 0) + + //#define SHOW_CONTEXT BRUSH_CHANNEL_SHOW_IN_CONTEXT_MENU + //#define SHOW_WORKSPACE BRUSH_CHANNEL_SHOW_IN_WORKSPACE + +#define BRUSH_CHANNEL_DEFINE_INTERNAL + ChannelProp channel_props[] = { +#include "brush_channel_define.h" + }; + +#undef SHOW_CONTEXT +#undef SHOW_WORKSPACE + + StructRNA *srna = RNA_struct_find("Brush"); + + Brush dummy = {0}; + PointerRNA _ptr = {&dummy.id}, *ptr = &_ptr; + + printf("total properties: %i\n", (int)ARRAY_SIZE(channel_props)); + + for (int i : IndexRange(ARRAY_SIZE(channel_props))) { + ChannelProp &def = channel_props[i]; + BrushChannelType type = {0}; + + BLI_strncpy(type.idname, def.path, sizeof(type.idname)); + BLI_strncpy(type.category, def.category, sizeof(type.category)); + type.flag = def.flag; + + for (auto &ui : def.extra_uiflags) { + for (int mode = 0; mode < (int)PAINT_MODE_INVALID; mode++) { + if (mode != ui.mode && !ui.all_tool_modes) { + continue; + } + + int uiflag = ui.uiflag ? ui.uiflag : -1; + + if (ui.all_tools) { + for (int j = 0; j < ARRAY_SIZE(type.paint_mode_uiflags); j++) { + type.paint_mode_uiflags[mode].tools[j] = uiflag; + } + } + else { + for (auto tool : ui.tools) { + type.paint_mode_uiflags[mode].tools[tool] = uiflag; + } + } + } + } + + PropertyRNA *prop = RNA_struct_type_find_property(srna, def.path); + BLI_assert(prop); + + if (!prop) { + printf("%s: Missing property %s\n", __func__, def.path); + continue; + } + + PropertyType prop_type = RNA_property_type(prop); + PropertySubType prop_subtype = RNA_property_subtype(prop); + + const char *uiname = RNA_property_ui_name(prop); + BLI_strncpy(type.uiname, uiname, sizeof(type.uiname)); + + type.min = 0.0; + type.max = 1.0; + type.soft_min = 0.0; + type.soft_max = 1.0; + + switch (prop_type) { + case PROP_BOOLEAN: + type.type = BRUSH_CHANNEL_TYPE_BOOL; + break; + case PROP_INT: { + int min, max, soft_min, soft_max; + int step; + + RNA_property_int_range(nullptr, prop, &min, &max); + RNA_property_int_ui_range(nullptr, prop, &soft_min, &soft_max, &step); + + type.min = (float)min; + type.max = (float)max; + type.soft_min = (float)soft_min; + type.soft_max = (float)soft_max; + type.type = BRUSH_CHANNEL_TYPE_INT; + break; + } + case PROP_FLOAT: { + float precision, step; + + RNA_property_float_range(ptr, prop, &type.min, &type.max); + RNA_property_float_ui_range(ptr, prop, &type.soft_min, &type.soft_max, &step, &precision); + + if (!RNA_property_array_check(prop)) { + type.type = BRUSH_CHANNEL_TYPE_FLOAT; + } + else { + int dimen = RNA_property_array_length(ptr, prop); + + switch (dimen) { + case 3: + type.type = BRUSH_CHANNEL_TYPE_VEC3; + break; + case 4: + type.type = BRUSH_CHANNEL_TYPE_VEC4; + break; + default: + BLI_assert_unreachable(); + } + } + break; + } + default: + break; + } + + switch (prop_subtype) { + case PROP_COLOR: + case PROP_COLOR_GAMMA: + type.subtype = BRUSH_CHANNEL_COLOR; + break; + case PROP_FACTOR: + type.subtype = BRUSH_CHANNEL_FACTOR; + break; + case PROP_PERCENTAGE: + type.subtype = BRUSH_CHANNEL_PERCENT; + break; + case PROP_ANGLE: + type.subtype = BRUSH_CHANNEL_ANGLE; + break; + case PROP_PIXEL: + type.subtype = BRUSH_CHANNEL_PIXEL; + break; + default: + break; + } + + builtin_channels.add(strdup(type.idname), type); + } +#undef BRUSH_CHANNEL_DEFINE_INTERNAL +} +static void check_builtin_brush_channels() +{ + if (builtin_channels.size() == 0) { + init_builtin_brush_channels(); + } +} + +ATTR_NO_OPT ChannelNameMap *get_namemap(BrushChannelSet *chset) +{ + return reinterpret_cast<ChannelNameMap *>(chset->channelmap); +} + +BrushChannelSet *BKE_brush_channelset_create() +{ + BrushChannelSet *chset = MEM_cnew<BrushChannelSet>("BrushChannelSet"); + + chset->channels.first = chset->channels.last = nullptr; + chset->channels_num = 0; + + ChannelNameMap *map = MEM_new<ChannelNameMap>("ChannelNameMap"); + chset->channelmap = static_cast<void *>(map); + + return chset; +} + +void BKE_brush_channel_free_data(BrushChannel *ch) +{ + MEM_SAFE_FREE(ch->category); + + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BrushMapping *mp = ch->mappings + i; + + if (mp->curve.curve) { + BKE_curvemapping_free(mp->curve.curve); + } + } + + if (ch->curve.curve) { + BKE_curvemapping_free(ch->curve.curve); + } +} + +void BKE_brush_channelset_free(BrushChannelSet *chset) +{ + LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) { + BKE_brush_channel_free_data(ch); + } + + BLI_freelistN(&chset->channels); + + MEM_delete<ChannelNameMap>(get_namemap(chset)); + MEM_freeN(static_cast<void *>(chset)); +} + +static void brush_mapping_reset(BrushMapping *mp, int type) +{ + mp->type = type; + mp->premultiply_factor = 1.0f; + mp->min = 0.0f; + mp->max = 1.0f; + mp->factor = 1.0f; + mp->blendmode = MA_RAMP_MULT; + mp->curve.preset = BRUSH_CURVE_LIN; +} + +BrushChannel *BKE_brush_channelset_add(BrushChannelSet *chset, BrushChannelType *type) +{ + BrushChannel *ch = MEM_cnew<BrushChannel>("BrushChannel"); + + BLI_strncpy(ch->idname, type->idname, sizeof(ch->idname)); + BLI_strncpy(ch->uiname, type->uiname, sizeof(ch->uiname)); + + ch->def = type; + ch->type = type->type; + ch->flag = type->flag; + + ch->ui_order = chset->channels_num; + + BLI_addtail(&chset->channels, static_cast<void *>(ch)); + get_namemap(chset)->add(ch->idname, ch); + chset->channels_num++; + + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BrushMapping *mp = ch->mappings + i; + brush_mapping_reset(mp, i); + } + + BKE_brush_channelset_ui_order_check(chset); + + return ch; +} + +void BKE_brush_channelset_begin(BrushChannelSet *chset, BrushChannelType *type) +{ + LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) { + ch->flag |= BRUSH_CHANNEL_NEEDS_EVALUATE; + } +} + +ATTR_NO_OPT BrushChannel *_BKE_brush_channelset_lookup(BrushChannelSet *chset, const char *idname) +{ + ChannelNameMap *namemap = get_namemap(chset); + + return namemap->lookup(StringRef(idname)); +} + +bool _BKE_brush_channelset_has(BrushChannelSet *chset, const char *idname) +{ + return get_namemap(chset)->contains(idname); +} + +const char *BKE_brush_channel_category_get(BrushChannel *ch) +{ + return ch->category ? ch->category : ch->def->category; +} + +const void BKE_brush_channel_category_set(BrushChannel *ch, const char *category) +{ + if (STREQ(category, ch->def->category)) { + MEM_SAFE_FREE(ch->category); + ch->category = nullptr; + + return; + } + + ch->category = BLI_strdup(category); +} + +ATTR_NO_OPT BrushChannel *_BKE_brush_channelset_ensure(BrushChannelSet *chset, const char *idname) +{ + if (!builtin_channels.contains(idname)) { + printf("channel types:\n"); + for (StringRef key : builtin_channels.keys()) { + printf(" %s\n", key.data()); + } + + printf("Unknown brush channel %s\n", idname); + return nullptr; + } + + ChannelNameMap *namemap = get_namemap(chset); + BrushChannelType &type = builtin_channels.lookup(idname); + + if (!namemap->contains(type.idname)) { + BKE_brush_channelset_add(chset, &type); + } + + return _BKE_brush_channelset_lookup(chset, idname); +} + +void BKE_brush_channelset_ensure_all_modes(Brush *brush) +{ + if (!brush->channels) { + brush->channels = BKE_brush_channelset_create(); + BKE_brush_channelset_ensure_channels(brush->channels, PAINT_MODE_INVALID, 0); + } + + if (brush->sculpt_tool) { + BKE_brush_channelset_ensure_channels(brush->channels, PAINT_MODE_SCULPT, brush->sculpt_tool); + } + + if (brush->vertexpaint_tool) { + BKE_brush_channelset_ensure_channels( + brush->channels, PAINT_MODE_VERTEX, brush->vertexpaint_tool); + } + + if (brush->imagepaint_tool) { + BKE_brush_channelset_ensure_channels( + brush->channels, PAINT_MODE_TEXTURE_3D, brush->imagepaint_tool); + } + + if (brush->curves_sculpt_tool) { + BKE_brush_channelset_ensure_channels( + brush->channels, PAINT_MODE_SCULPT_CURVES, brush->curves_sculpt_tool); + } + + if (brush->weightpaint_tool) { + BKE_brush_channelset_ensure_channels( + brush->channels, PAINT_MODE_WEIGHT, brush->weightpaint_tool); + } +} +void BKE_brush_channelset_ensure_channels(BrushChannelSet *chset, ePaintMode mode, int tool) +{ + check_builtin_brush_channels(); + + BKE_brush_channelset_ensure(chset, size); + BKE_brush_channelset_ensure(chset, unprojected_radius); + BKE_brush_channelset_ensure(chset, strength); + BKE_brush_channelset_ensure(chset, spacing); + + if (mode != PAINT_MODE_INVALID) { + for (BrushChannelType &type : builtin_channels.values()) { + int uiflag = type.paint_mode_uiflags[(int)mode].tools[tool]; + + if (uiflag) { + BrushChannel *ch = _BKE_brush_channelset_ensure(chset, type.idname); + + if (uiflag != -1) { + if (!(ch->ui_flag & BRUSH_CHANNEL_SHOW_IN_HEADER_USER_SET)) { + ch->ui_flag &= ~BRUSH_CHANNEL_SHOW_IN_HEADER; + ch->ui_flag |= uiflag & BRUSH_CHANNEL_SHOW_IN_HEADER; + } + if (!(ch->ui_flag & BRUSH_CHANNEL_SHOW_IN_CONTEXT_MENU_USER_SET)) { + ch->ui_flag &= ~BRUSH_CHANNEL_SHOW_IN_CONTEXT_MENU; + ch->ui_flag |= uiflag & BRUSH_CHANNEL_SHOW_IN_CONTEXT_MENU; + } + if (!(ch->ui_flag & BRUSH_CHANNEL_SHOW_IN_WORKSPACE_USER_SET)) { + ch->ui_flag &= ~BRUSH_CHANNEL_SHOW_IN_WORKSPACE; + ch->ui_flag |= uiflag & BRUSH_CHANNEL_SHOW_IN_WORKSPACE; + } + } + } + } + } + /* Some helper lambdas */ + + auto _ensure = [&](const char *idname) { return _BKE_brush_channelset_ensure(chset, idname); }; + +#ifdef ensure +# undef ensure +#endif + +#define ensure _ensure(make_builtin_ch_name(idname), ui_flag); + + auto _ensure_ui = [&](const char *idname, int ui_flag) { + _ensure(idname); + + BrushChannel *ch = _BKE_brush_channelset_lookup(chset, idname); + ch->ui_flag |= ui_flag; + + return ch; + }; + +#ifdef ensure_ui +# undef ensure_ui +#endif + +#define ensure_ui(idname, ui_flag) _ensure_ui(make_builtin_ch_name(idname), ui_flag) + + const int SHOW_WORKSPACE = BRUSH_CHANNEL_SHOW_IN_WORKSPACE; + const int SHOW_CONTEXT = BRUSH_CHANNEL_SHOW_IN_CONTEXT_MENU; + const int SHOW_HEADER = BRUSH_CHANNEL_SHOW_IN_HEADER; + const int SHOW_ALL = (SHOW_WORKSPACE | SHOW_CONTEXT | SHOW_HEADER); + + if (mode == PAINT_MODE_SCULPT) { + switch (tool) { + case SCULPT_TOOL_PAINT: + ensure_ui(wet_mix, SHOW_WORKSPACE); + ensure_ui(wet_persistence, SHOW_WORKSPACE); + ensure_ui(color, SHOW_ALL); + ensure_ui(secondary_color, SHOW_ALL); + ensure_ui(flow, SHOW_WORKSPACE | SHOW_CONTEXT); + ensure_ui(density, SHOW_WORKSPACE | SHOW_CONTEXT); + ensure_ui(tip_scale_x, SHOW_WORKSPACE | SHOW_CONTEXT); + break; + } + } + +#undef ensure +#undef ensure_ui +} + +char *BKE_brush_channel_rna_path(const ID *owner, const BrushChannel *ch) +{ + switch (GS(owner->name)) { + case ID_BR: + return BLI_strdup(ch->idname); + case ID_SCE: { + string path = "tool_settings.unified_properties[\""; + + path += string(ch->idname) + "\"]"; + + return BLI_strdup(path.c_str()); + } + default: + return BLI_strdup(""); + } +} + +void brush_channel_ensure_value(const ID *id, BrushChannel *ch) +{ + if (!(ch->flag & BRUSH_CHANNEL_NEEDS_EVALUATE)) { + return; + } + + string path; + StructRNA *srna; + + string rnaname = ch->idname; + + if (GS(id->name) == ID_BR) { + path = rnaname; + srna = &RNA_Brush; + } + else { + path = "tool_settings.unified_properties[\""; + path += rnaname; + path += "\"]"; + + rnaname = string("[\"") + rnaname + string("\"]"); + srna = &RNA_Scene; + } + + ch->flag &= ~BRUSH_CHANNEL_NEEDS_EVALUATE; + PointerRNA ptr, ptr2; + PropertyRNA *prop = nullptr; + + RNA_pointer_create(const_cast<ID *>(id), srna, const_cast<ID *>(id), &ptr); + if (RNA_path_resolve(&ptr, path.c_str(), &ptr2, &prop)) { + PropertyType prop_type = RNA_property_type(prop); + + switch (prop_type) { + case PROP_FLOAT: + if (RNA_property_array_check(prop)) { + RNA_float_get_array(&ptr2, rnaname.c_str(), ch->vector); + } + else { + ch->fvalue = RNA_float_get(&ptr2, rnaname.c_str()); + } + break; + case PROP_INT: + ch->ivalue = RNA_int_get(&ptr2, rnaname.c_str()); + break; + default: + printf("Unknown prop type %d\n", (int)prop_type); + break; + } + } + else { + printf("Error looking up path %s", path.c_str()); + return; + } +} + +static bool channel_has_mappings(BrushChannel *ch) +{ + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + if (ch->mappings[i].flag & BRUSH_MAPPING_ENABLED) { + return true; + } + } + + return false; +} + +/* idx is used by vector channels */ +double BKE_brush_channel_eval_mappings(BrushChannel *ch, + BrushMappingData *mapdata, + double f, + int idx) +{ + + if (idx == 3 && !(ch->flag & BRUSH_CHANNEL_APPLY_MAPPING_TO_ALPHA)) { + return f; + } + + if (mapdata) { + double factor = f; + + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BrushMapping *mp = ch->mappings + i; + + if (!(mp->flag & BRUSH_MAPPING_ENABLED)) { + continue; + } + + float inputf = ((float *)mapdata)[i] * mp->premultiply_factor; + + switch ((eBrushMappingFunc)mp->mapfunc) { + case BRUSH_MAPFUNC_NONE: + break; + case BRUSH_MAPFUNC_SAW: + inputf -= floorf(inputf); + break; + case BRUSH_MAPFUNC_TENT: + inputf -= floorf(inputf); + inputf = 1.0f - fabs(inputf - 0.5f) * 2.0f; + break; + case BRUSH_MAPFUNC_COS: + inputf = 1.0f - (cos(inputf * (float)M_PI * 2.0f) * 0.5f + 0.5f); + break; + case BRUSH_MAPFUNC_CUTOFF: + /*Cutoff is meant to create a fadeout effect, + which requires inverting the input. To avoid + user confusion we just do it here instead of making + them check the inverse checkbox.*/ + inputf = 1.0f - inputf; + CLAMP(inputf, 0.0f, mp->func_cutoff * 2.0f); + break; + case BRUSH_MAPFUNC_SQUARE: + inputf -= floorf(inputf); + inputf = inputf > mp->func_cutoff ? 1.0f : 0.0f; + break; + default: + break; + } + + if (mp->flag & BRUSH_MAPPING_INVERT) { + inputf = 1.0f - inputf; + } + + double f2 = BKE_brush_curve_strength_ex( + mp->curve.preset, mp->curve.curve, inputf, 1.0f, false); + f2 = mp->min + (mp->max - mp->min) * f2; + + /* make sure to update blend_items in rna_brush_engine.c + when adding new mode implementations */ + switch (mp->blendmode) { + case MA_RAMP_BLEND: + break; + case MA_RAMP_MULT: + f2 *= factor; + break; + case MA_RAMP_DIV: + f2 = factor / (f2 == 0.0f ? 0.0001f : f2); + break; + case MA_RAMP_ADD: + f2 += factor; + break; + case MA_RAMP_SUB: + f2 = factor - f2; + break; + case MA_RAMP_DIFF: + f2 = fabsf(factor - f2); + break; + default: + printf("Unsupported brush mapping blend mode for %s (%s); will mix instead\n", + ch->uiname, + ch->idname); + break; + } + + factor += (f2 - factor) * mp->factor; + } + + f = factor; + CLAMP(f, ch->def->min, ch->def->max); + } + + return f; +} + +static BrushChannel *brush_channel_final(const Brush *brush, + const Scene *scene, + const char *idname) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(brush->channels, idname); + + if (BKE_brush_channel_inherits(brush, scene->toolsettings, ch)) { + ch = _BKE_brush_channelset_lookup(scene->toolsettings->unified_channels, idname); + brush_channel_ensure_value(&scene->id, ch); + } + else { + brush_channel_ensure_value(&brush->id, ch); + } + + return ch; +} + +float _BKE_brush_eval_float(const Brush *brush, + const Scene *scene, + const char *idname, + BrushMappingData *mapping) +{ + BrushChannel *ch = brush_channel_final(brush, scene, idname); + + return mapping ? BKE_brush_channel_eval_mappings(ch, mapping, ch->fvalue, 0) : ch->fvalue; +} + +int _BKE_brush_eval_int(const Brush *brush, + const Scene *scene, + const char *idname, + BrushMappingData *mapping) +{ + BrushChannel *ch = brush_channel_final(brush, scene, idname); + + return mapping ? (int)BKE_brush_channel_eval_mappings(ch, mapping, (double)ch->ivalue, 0) : + ch->ivalue; +} + +static void brush_channel_evaluate(const Brush *br, + const Scene *scene, + BrushChannelSet *chset, + const char *idname, + BrushMappingData *mapping) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(chset, idname); + + if (!mapping) { + return; + } + + switch (ch->type) { + case BRUSH_CHANNEL_TYPE_FLOAT: + ch->fvalue = BKE_brush_channel_eval_mappings(ch, mapping, ch->fvalue, 0); + break; + case BRUSH_CHANNEL_TYPE_INT: + case BRUSH_CHANNEL_TYPE_BOOL: + case BRUSH_CHANNEL_TYPE_ENUM: + ch->ivalue = (int)BKE_brush_channel_eval_mappings(ch, mapping, ch->ivalue, 0); + break; + case BRUSH_CHANNEL_TYPE_VEC3: + case BRUSH_CHANNEL_TYPE_VEC4: { + int size = ch->type == BRUSH_CHANNEL_TYPE_VEC3 ? 3 : 4; + + for (int i = 0; i < size; i++) { + ch->vector[i] = (int)BKE_brush_channel_eval_mappings(ch, mapping, ch->vector[i], i); + break; + } + break; + } + } +} + +float _BKE_brush_channelset_float_get(BrushChannelSet *chset, const char *idname) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(chset, idname); + return ch->fvalue; +} + +void _BKE_brush_channelset_float_set(BrushChannelSet *chset, const char *idname, float f) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(chset, idname); + ch->fvalue = f; +} + +int _BKE_brush_channelset_int_get(BrushChannelSet *chset, const char *idname) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(chset, idname); + return ch->ivalue; +} + +void _BKE_brush_channelset_int_set(BrushChannelSet *chset, const char *idname, int i) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(chset, idname); + ch->ivalue = i; +} + +bool brush_channelset_rna_path_resolve(const ID *id, + BrushChannelSet *chset, + const char *idname, + PointerRNA *r_ptr, + PropertyRNA **r_prop, + char **final_idname) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(chset, idname); + + if (!ch) { + return false; + } + + bool ok = true; + + StructRNA *srna; + + switch (GS(id->name)) { + case ID_SCE: + srna = &RNA_Scene; + break; + case ID_BR: + srna = &RNA_Brush; + break; + default: + return false; + } + + PointerRNA id_ptr; + RNA_pointer_create( + const_cast<ID *>(id), srna, static_cast<void *>(const_cast<ID *>(id)), &id_ptr); + + char *path = BKE_brush_channel_rna_path(id, ch); + ok = RNA_path_resolve(&id_ptr, path, r_ptr, r_prop); + MEM_SAFE_FREE(path); + + *final_idname = static_cast<char *>( + MEM_mallocN(strlen(idname) + 6, "brush_channelset_rna_path_resolve.final_idname")); + + if (RNA_property_is_idprop(*r_prop)) { + sprintf(*final_idname, "[\"%s\"]", idname); + } + else { + sprintf(*final_idname, "%s", idname); + } + + return ok; +} + +int _BKE_brush_int_get(const ID *id, BrushChannelSet *chset, const char *idname) +{ + PointerRNA ptr; + PropertyRNA *prop; + char *final_idname; + int ret = 0; + + if (brush_channelset_rna_path_resolve(id, chset, idname, &ptr, &prop, &final_idname)) { + ret = RNA_int_get(&ptr, final_idname); + } + + MEM_SAFE_FREE(final_idname); + return ret; +} + +void _BKE_brush_int_set(ID *id, BrushChannelSet *chset, const char *idname, int i) +{ + PointerRNA ptr; + PropertyRNA *prop; + char *final_idname; + + if (brush_channelset_rna_path_resolve(id, chset, idname, &ptr, &prop, &final_idname)) { + RNA_int_set(&ptr, final_idname, i); + _BKE_brush_channelset_lookup(chset, idname)->ivalue = i; + } + + MEM_SAFE_FREE(final_idname); +} + +float _BKE_brush_float_get(const ID *id, BrushChannelSet *chset, const char *idname) +{ + PointerRNA ptr; + PropertyRNA *prop; + char *final_idname; + float ret = 0; + + if (brush_channelset_rna_path_resolve(id, chset, idname, &ptr, &prop, &final_idname)) { + ret = RNA_float_get(&ptr, final_idname); + } + + MEM_SAFE_FREE(final_idname); + return ret; +} + +void _BKE_brush_float_set(ID *id, BrushChannelSet *chset, const char *idname, float f) +{ + PointerRNA ptr; + PropertyRNA *prop; + char *final_idname; + + if (brush_channelset_rna_path_resolve(id, chset, idname, &ptr, &prop, &final_idname)) { + RNA_float_set(&ptr, final_idname, f); + _BKE_brush_channelset_lookup(chset, idname)->fvalue = f; + } + + MEM_SAFE_FREE(final_idname); +} + +void BKE_brush_mapping_copy_data(BrushMapping *dest, BrushMapping *src) +{ + *dest = *src; + + if (dest->curve.curve) { + dest->curve.curve = BKE_curvemapping_copy(dest->curve.curve); + BKE_curvemapping_init(dest->curve.curve); + } +} + +void BKE_brush_mapping_free_data(BrushMapping *mp) +{ + if (mp->curve.curve) { + BKE_curvemapping_free(mp->curve.curve); + } +} + +void BKE_brush_channel_copy_data(BrushChannel *dest, BrushChannel *src) +{ + BrushChannel *prev = dest->prev, *next = dest->next; + + *dest = *src; + dest->prev = prev; + dest->next = next; + + if (dest->category) { + dest->category = static_cast<char *>(MEM_dupallocN(static_cast<void *>(dest->category))); + } + + if (dest->curve.curve) { + dest->curve.curve = BKE_curvemapping_copy(dest->curve.curve); + BKE_curvemapping_init(dest->curve.curve); + } + + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BKE_brush_mapping_copy_data(dest->mappings + i, src->mappings + i); + } +} + +BrushChannel *BKE_brush_channel_copy(BrushChannel *ch) +{ + BrushChannel *ch2 = MEM_cnew<BrushChannel>("BrushChannel"); + BKE_brush_channel_copy_data(ch2, ch); + return ch2; +} + +BrushChannelSet *BKE_brush_channelset_copy(BrushChannelSet *chset) +{ + BrushChannelSet *chset2 = BKE_brush_channelset_create(); + ChannelNameMap *namemap2 = get_namemap(chset2); + + LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) { + BrushChannel *ch2 = BKE_brush_channel_copy(ch); + + chset2->channels_num++; + namemap2->add(ch2->idname, ch2); + BLI_addtail(&chset2->channels, static_cast<void *>(ch2)); + } + + return chset2; +} + +void BKE_brush_channelset_blend_read(BrushChannelSet *chset, BlendDataReader *reader) +{ + check_builtin_brush_channels(); + + BLO_read_list(reader, &chset->channels); + + ChannelNameMap *namemap = MEM_new<ChannelNameMap>("ChannelNameMap"); + chset->channelmap = static_cast<void *>(namemap); + chset->channels_num = 0; + + LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) { + chset->channels_num++; + + namemap->add(StringRef(ch->idname), ch); + + if (builtin_channels.contains(ch->idname)) { + ch->def = &builtin_channels.lookup(ch->idname); + } + else { + ch->def = &empty_brush_type; + } + /* Read user-defined category if it exists. */ + BLO_read_data_address(reader, &ch->category); + BLO_read_data_address(reader, &ch->curve.curve); + + if (ch->curve.curve) { + BKE_curvemapping_blend_read(reader, ch->curve.curve); + BKE_curvemapping_init(ch->curve.curve); + } + + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BrushMapping *mp = ch->mappings + i; + + BLO_read_data_address(reader, &mp->curve.curve); + + if (mp->curve.curve) { + BKE_curvemapping_blend_read(reader, mp->curve.curve); + BKE_curvemapping_init(mp->curve.curve); + } + } + } +} + +void BKE_brush_channelset_blend_write(BrushChannelSet *chset, BlendWriter *writer) +{ + BLO_write_struct(writer, BrushChannelSet, chset); + + LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) { + BLO_write_struct(writer, BrushChannel, ch); + + if (ch->category) { + BLO_write_string(writer, ch->category); + } + + if (ch->curve.curve) { + BKE_curvemapping_blend_write(writer, ch->curve.curve); + } + + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BrushMapping *mp = ch->mappings + i; + + if (mp->curve.curve) { + BKE_curvemapping_blend_write(writer, mp->curve.curve); + } + } + } +} + +bool BKE_brush_channel_inherits(const Brush *brush, + const ToolSettings *tool_settings, + BrushChannel *ch) +{ + BrushChannel *scene_ch = _BKE_brush_channelset_lookup(tool_settings->unified_channels, + ch->idname); + + if (!scene_ch) { + return false; + } + + if (ch->flag & BRUSH_CHANNEL_INHERIT) { + return true; + } + + if (scene_ch->flag & BRUSH_CHANNEL_FORCE_INHERIT) { + return !(ch->flag & BRUSH_CHANNEL_IGNORE_FORCE_INHERIT); + } + + return false; +} + +BrushChannelSet *BKE_brush_channelset_create_final(const Brush *brush, + const Scene *scene, + BrushMappingData *mapdata) +{ + BrushChannelSet *chset = BKE_brush_channelset_copy(brush->channels); + + LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) { + BrushChannel *ch1 = _BKE_brush_channelset_lookup(brush->channels, ch->idname); + BrushChannel *ch2 = _BKE_brush_channelset_lookup(scene->toolsettings->unified_channels, + ch->idname); + + ch1->flag |= BRUSH_CHANNEL_NEEDS_EVALUATE; + brush_channel_ensure_value(&brush->id, ch1); + ch2->flag |= BRUSH_CHANNEL_NEEDS_EVALUATE; + brush_channel_ensure_value(&scene->id, ch2); + + bool inherit = BKE_brush_channel_inherits(brush, scene->toolsettings, ch); + + if (inherit) { + BKE_brush_channel_free_data(ch); + BKE_brush_channel_copy_data(ch, ch2); + } + + for (int i = 0; i < BRUSH_MAPPING_MAX; i++) { + BrushMapping *mp = ch->mappings + i; + BrushMapping *mp1 = ch1->mappings + i; + BrushMapping *mp2 = ch2->mappings + i; + + if ((mp1->flag & BRUSH_MAPPING_INHERIT_NEVER) || !inherit) { + BKE_brush_mapping_free_data(mp); + BKE_brush_mapping_copy_data(mp, mp1); + } + else if ((mp1->flag & BRUSH_MAPPING_INHERIT_ALWAYS) || inherit) { + BKE_brush_mapping_free_data(mp); + BKE_brush_mapping_copy_data(mp, mp2); + } + } + + ch2->flag |= BRUSH_CHANNEL_NEEDS_EVALUATE; + + brush_channel_evaluate(brush, scene, chset, ch->idname, mapdata); + } + + return chset; +} + +void BKE_brush_channelset_toolsettings_init(ToolSettings *ts) +{ + check_builtin_brush_channels(); + + if (!ts->unified_properties) { + ts->unified_properties = IDP_New(IDP_GROUP, nullptr, "group"); + } + + if (!ts->unified_channels) { + ts->unified_channels = BKE_brush_channelset_create(); + } + + for (const BrushChannelType &type : builtin_channels.values()) { + _BKE_brush_channelset_ensure(ts->unified_channels, type.idname); + } + + StructRNA *srna = &RNA_Brush; + Brush defaults = {0}; + + BLI_strncpy(defaults.id.name, "BRDefaults", sizeof(defaults.id.name)); + + defaults.sculpt_tool = SCULPT_TOOL_DRAW; + BKE_brush_sculpt_reset(&defaults); + + PointerRNA ptr; + ptr.owner_id = &defaults.id; + ptr.data = &defaults; + ptr.type = &RNA_Brush; + + LISTBASE_FOREACH (BrushChannel *, ch, &ts->unified_channels->channels) { + IDProperty *idprop = IDP_GetPropertyFromGroup(ts->unified_properties, ch->idname); + + if (!idprop) { + PointerRNA prop_ptr; + PropertyRNA *prop; + double default_value = 0.0; + float vector4[4] = {0.0f, 0.0f, 0.0f, 0.0f}; + + if (RNA_path_resolve(&ptr, ch->idname, &prop_ptr, &prop)) { + switch (RNA_property_type(prop)) { + case PROP_BOOLEAN: + default_value = RNA_boolean_get(&prop_ptr, ch->idname) ? 1.0 : 0.0; + break; + case PROP_INT: + default_value = RNA_int_get(&prop_ptr, ch->idname); + break; + case PROP_FLOAT: + if (RNA_property_array_check(prop)) { + RNA_float_get_array(&prop_ptr, ch->idname, vector4); + } + else { + default_value = RNA_float_get(&prop_ptr, ch->idname); + } + break; + case PROP_ENUM: + default_value = (double)RNA_enum_get(&prop_ptr, ch->idname); + break; + default: + break; + } + printf("found property!\n"); + } + + IDPropertyTemplate tmpl; + char type; + + switch (ch->type) { + case BRUSH_CHANNEL_TYPE_FLOAT: + tmpl.f = (float)default_value; + type = IDP_FLOAT; + break; + case BRUSH_CHANNEL_TYPE_INT: + case BRUSH_CHANNEL_TYPE_ENUM: + case BRUSH_CHANNEL_TYPE_BITMASK: + case BRUSH_CHANNEL_TYPE_BOOL: + tmpl.i = (int)default_value; + type = IDP_INT; + break; + case BRUSH_CHANNEL_TYPE_VEC4: + tmpl.array.type = IDP_FLOAT; + tmpl.array.len = 4; + type = IDP_ARRAY; + break; + default: + printf("%s: unsupported brush channel type for unified channel %s: %d\n", + __func__, + ch->idname, + ch->type); + continue; + } + + idprop = IDP_New(type, &tmpl, ch->idname); + IDP_AddToGroup(ts->unified_properties, idprop); + + if (ch->type == BRUSH_CHANNEL_TYPE_VEC4) { + memcpy(idprop->data.pointer, static_cast<void *>(vector4), sizeof(vector4)); + } + } + + IDPropertyUIData *uidata = IDP_ui_data_ensure(idprop); + + MEM_SAFE_FREE(uidata->description); + + PropertyRNA *prop = RNA_struct_type_find_property(srna, ch->def->idname); + BLI_assert(prop); + + uidata->description = BLI_strdup(RNA_property_description(prop)); + + PropertySubType prop_subtype = RNA_property_subtype(prop); + + switch (ch->type) { + case BRUSH_CHANNEL_TYPE_FLOAT: { + IDPropertyUIDataFloat *uidataf = reinterpret_cast<IDPropertyUIDataFloat *>(uidata); + + float min, max, soft_min, soft_max, step, precision; + + RNA_property_float_range(nullptr, prop, &min, &max); + RNA_property_float_ui_range(nullptr, prop, &soft_min, &soft_max, &step, &precision); + + uidataf->min = (float)min; + uidataf->max = (float)max; + uidataf->soft_min = (float)soft_min; + uidataf->soft_max = (float)soft_max; + uidataf->step = (float)step; + uidataf->precision = (int)precision; + break; + } + case BRUSH_CHANNEL_TYPE_INT: { + IDPropertyUIDataInt *uidatai = reinterpret_cast<IDPropertyUIDataInt *>(uidata); + + RNA_property_int_range(nullptr, prop, &uidatai->min, &uidatai->max); + RNA_property_int_ui_range( + nullptr, prop, &uidatai->soft_min, &uidatai->soft_max, &uidatai->step); + break; + } + case BRUSH_CHANNEL_TYPE_ENUM: + case BRUSH_CHANNEL_TYPE_BITMASK: + break; + case BRUSH_CHANNEL_TYPE_BOOL: { + IDPropertyUIDataInt *uidatai = reinterpret_cast<IDPropertyUIDataInt *>(uidata); + + uidatai->min = uidatai->soft_min = 0; + uidatai->max = uidatai->soft_max = 1; + uidatai->step = 1; + + break; + } + } + + uidata->rna_subtype = prop_subtype; + } + + BKE_libblock_free_data(&defaults.id, false); +} + +void _BKE_brush_channelset_mark_update(BrushChannelSet *chset, const char *idname) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(chset, idname); + + if (ch) { + ch->flag |= BRUSH_CHANNEL_NEEDS_EVALUATE; + } +} + +void BKE_brush_channelset_ui_order_check(BrushChannelSet *chset) +{ + Vector<BrushChannel *> channels; + + LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) { + channels.append(ch); + } + + auto cmp = [](const BrushChannel *ch1, const BrushChannel *ch2) { + return ch1->ui_order < ch2->ui_order; + }; + + std::sort(channels.begin(), channels.end(), cmp); + + for (int i = 0; i < channels.size(); i++) { + channels[i]->ui_order = i; + } +} + +void BKE_brush_channelset_ui_order_move(BrushChannelSet *chset, + BrushChannel *ch, + int uiflag, + int dir) +{ + Vector<BrushChannel *> channels; + + LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) { + channels.append(ch); + } + + auto cmp = [](const BrushChannel *ch1, const BrushChannel *ch2) { + return ch1->ui_order < ch2->ui_order; + }; + + std::sort(channels.begin(), channels.end(), cmp); + + const char *cat1 = BKE_brush_channel_category_get(ch); + + for (int i = 0; i < channels.size(); i++) { + if (channels[i] == ch) { + int j = i; + BrushChannel *ch2 = NULL; + const char *cat2; + + do { + j += dir < 0 ? -1 : 1; + + if (j < 0 || j >= channels.size()) { + break; + } + + ch2 = channels[j]; + cat2 = BKE_brush_channel_category_get(ch2); + } while ((!(ch2->ui_flag & uiflag) || !STREQ(cat1, cat2))); + + int neworder; + + if (ch2) { + neworder = ch2->ui_order; + ch2->ui_order = ch->ui_order; + } + else { + neworder = dir < 0 ? 0 : channels.size(); + } + + ch->ui_order = neworder; + } + } + + BKE_brush_channelset_ui_order_check(chset); +} + +#if 0 +void BKE_brush_channels_update(Brush *active_brush, Scene *scene) +{ + if (!scene->toolsettings) { + return; + } + + /* Sync evaluated inheritance flags */ + LISTBASE_FOREACH (BrushChannel *, ch1, &active_brush->channels->channels) { + BrushChannel *ch2 = _BKE_brush_channelset_lookup(scene->toolsettings->unified_channels, + ch1->idname); + + ch1->evaluated_flag = ch1->flag & ~(BRUSH_CHANNEL_INHERIT | BRUSH_CHANNEL_FORCE_INHERIT); + ch2->evaluated_flag = ch2->flag & ~(BRUSH_CHANNEL_INHERIT | BRUSH_CHANNEL_FORCE_INHERIT); + + if (ch1->flag & BRUSH_CHANNEL_INHERIT) { + ch1->evaluated_flag |= BRUSH_CHANNEL_INHERIT; + ch2->evaluated_flag |= BRUSH_CHANNEL_FORCE_INHERIT; + } + + if (ch2->flag & BRUSH_CHANNEL_FORCE_INHERIT) { + ch1->evaluated_flag |= BRUSH_CHANNEL_INHERIT; + ch2->evaluated_flag |= BRUSH_CHANNEL_FORCE_INHERIT; + } + } +} +#endif + +int _BKE_brush_int_get_unified(const struct Scene *scene, + const struct Brush *brush, + const char *idname) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(brush->channels, idname); + + if (!ch) { + printf("Unknown channel %s\n", idname); + return 0; + } + + if (BKE_brush_channel_inherits(brush, scene->toolsettings, ch)) { + return _BKE_brush_int_get(&scene->id, scene->toolsettings->unified_channels, idname); + } + else { + return _BKE_brush_int_get(&brush->id, brush->channels, idname); + } +} + +void _BKE_brush_int_set_unified(struct Scene *scene, + struct Brush *brush, + const char *idname, + int i) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(brush->channels, idname); + + if (!ch) { + printf("Unknown channel %s\n", idname); + return; + } + + if (BKE_brush_channel_inherits(brush, scene->toolsettings, ch)) { + _BKE_brush_int_set(&scene->id, scene->toolsettings->unified_channels, idname, i); + } + else { + _BKE_brush_int_set(&brush->id, brush->channels, idname, i); + } +} + +float _BKE_brush_float_get_unified(const struct Scene *scene, + const struct Brush *brush, + const char *idname) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(brush->channels, idname); + + if (!ch) { + printf("Unknown channel %s\n", idname); + return 0.0f; + } + + if (BKE_brush_channel_inherits(brush, scene->toolsettings, ch)) { + return _BKE_brush_float_get(&scene->id, scene->toolsettings->unified_channels, idname); + } + else { + return _BKE_brush_float_get(&brush->id, brush->channels, idname); + } +} + +void _BKE_brush_float_set_unified(struct Scene *scene, + struct Brush *brush, + const char *idname, + float f) +{ + BrushChannel *ch = _BKE_brush_channelset_lookup(brush->channels, idname); + + if (!ch) { + printf("Unknown channel %s\n", idname); + return; + } + + if (BKE_brush_channel_inherits(brush, scene->toolsettings, ch)) { + _BKE_brush_float_set(&scene->id, scene->toolsettings->unified_channels, idname, f); + } + else { + _BKE_brush_float_set(&brush->id, brush->channels, idname, f); + } +} + +static bool brush_mapping_inherits(BrushChannel *owner, + BrushChannel *unified, + BrushMapping *mapping) +{ + bool inherit_ch = owner->flag & BRUSH_CHANNEL_INHERIT; + + if ((unified->flag & BRUSH_CHANNEL_FORCE_INHERIT) && + !(owner->flag & BRUSH_CHANNEL_IGNORE_FORCE_INHERIT)) { + inherit_ch = true; + } + + bool inherit = mapping->inherit_mode == BRUSH_MAPPING_INHERIT_ALWAYS; + inherit = inherit || (mapping->inherit_mode == BRUSH_MAPPING_INHERIT_CHANNEL && inherit_ch); + + return inherit; +} +bool _BKE_brush_mapping_enabled(const struct Scene *scene, + const struct Brush *brush, + const char *idname, + eBrushMappingType mapping_type) +{ + BrushChannel *ch1 = _BKE_brush_channelset_lookup(brush->channels, idname); + BrushChannel *ch2 = _BKE_brush_channelset_lookup(scene->toolsettings->unified_channels, idname); + + if (!ch1) { + return false; + } + + if (brush_mapping_inherits(ch1, ch2, ch1->mappings + mapping_type)) { + return ch2->mappings[mapping_type].flag & BRUSH_MAPPING_ENABLED; + } + else { + return ch1->mappings[mapping_type].flag & BRUSH_MAPPING_ENABLED; + } +} diff --git a/source/blender/blenkernel/intern/brush_channel_define.h b/source/blender/blenkernel/intern/brush_channel_define.h new file mode 100644 index 00000000000..4caf74d1077 --- /dev/null +++ b/source/blender/blenkernel/intern/brush_channel_define.h @@ -0,0 +1,235 @@ +#if defined(BRUSH_CHANNEL_DEFINE_EXTERNAL) || defined(BRUSH_CHANNEL_DEFINE_INTERNAL_NAMES) || \ + defined(BRUSH_CHANNEL_DEFINE_INTERNAL) +# ifdef MAKE_PROP +# undef MAKE_PROP +# endif +# ifdef MAKE_PROP_EX +# undef MAKE_PROP_EX +# endif +#endif + +#ifdef BRUSH_CHANNEL_DEFINE_EXTERNAL +# define MAKE_PROP_EX(idname, category, flag, ...) MAKE_PROP(idname, category, uiflags) +# define MAKE_PROP(idname, category, ...) extern const char *BRUSH_BUILTIN_##idname; +#elif defined(BRUSH_CHANNEL_DEFINE_INTERNAL_NAMES) +# define MAKE_PROP_EX(idname, category, flag, ...) MAKE_PROP(idname, category) +# define MAKE_PROP(idname, category, ...) const char *BRUSH_BUILTIN_##idname = # idname; +#elif defined(BRUSH_CHANNEL_DEFINE_INTERNAL) +# define MAKE_PROP(idname, category, ...) {# idname, category, {__VA_ARGS__}, 0, {}}, +# define MAKE_PROP_EX(idname, category, flag, ...) {# idname, category, {__VA_ARGS__}, flag, {}}, + +#endif + +#ifdef SHOW_WORKSPACE +# undef SHOW_WORKSPACE +#endif +#ifdef SHOW_CONTEXT +# undef SHOW_CONTEXT +#endif +#ifdef SHOW_CONTEXT +# undef SHOW_CONTEXT +#endif +#ifdef SHOW_ALL +# undef SHOW_ALL +#endif + +/* + UI visibility flags. Note that some brush types + may override these in their own channels, see BKE_brush_channelset_ensure_channels +*/ + +#define SHOW_WORKSPACE BRUSH_CHANNEL_SHOW_IN_WORKSPACE +#define SHOW_CONTEXT BRUSH_CHANNEL_SHOW_IN_CONTEXT_MENU +#define SHOW_HEADER BRUSH_CHANNEL_SHOW_IN_HEADER +#define SHOW_ALL (SHOW_WORKSPACE | SHOW_CONTEXT | SHOW_HEADER) + +/* Note that channel sets for individual brush types are built in + * BKE_brush_channelset_ensure_channels + */ + +#define SCULPT_GEO_TOOLS \ + SCULPT_TOOL_DRAW, SCULPT_TOOL_CLAY, SCULPT_TOOL_CLAY_STRIPS, SCULPT_TOOL_FLATTEN, \ + SCULPT_TOOL_PINCH, SCULPT_TOOL_FILL, SCULPT_TOOL_INFLATE, SCULPT_TOOL_THUMB, \ + SCULPT_TOOL_SNAKE_HOOK, SCULPT_TOOL_CREASE, SCULPT_TOOL_BLOB, SCULPT_TOOL_CLAY_STRIPS, \ + SCULPT_TOOL_LAYER + +#ifdef GEOMETRY +#undef GEOMETRY +#endif + +#define GEOMETRY(flag) UI(PAINT_MODE_SCULPT, flag, {SCULPT_GEO_TOOLS}) + +#ifdef AUTOMASKING +# undef AUTOMASKING +#endif +#define AUTOMASKING UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE) + +#ifdef SCULPT_PAINT +# undef SCULPT_PAINT +#endif +#ifdef SCULPT_CLOTHABLE_TOOLS +# undef SCULPT_CLOTHABLE_TOOLS +#endif + +#define SCULPT_CLOTHABLE_TOOLS \ + SCULPT_TOOL_CLOTH, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_ELASTIC_DEFORM, SCULPT_TOOL_POSE + +#define SCULPT_PAINT UI(PAINT_MODE_SCULPT, SHOW_CONTEXT | SHOW_WORKSPACE, {SCULPT_TOOL_PAINT}) + +MAKE_PROP(size, "Basic", UI(SHOW_ALL)) +MAKE_PROP(unprojected_radius, "Basic", UI(SHOW_ALL)) +MAKE_PROP(strength, "Basic", UI(SHOW_ALL)) +MAKE_PROP(automasking_boundary_edges_propagation_steps, "Automasking", AUTOMASKING) +MAKE_PROP(use_automasking_boundary_edges, "Automasking", AUTOMASKING) +MAKE_PROP(use_automasking_boundary_face_sets, "Automasking", AUTOMASKING) +MAKE_PROP(use_automasking_face_sets, "Automasking", AUTOMASKING) +MAKE_PROP(use_automasking_topology, "Automasking", AUTOMASKING) +MAKE_PROP(area_radius_factor, "Basic", GEOMETRY(SHOW_WORKSPACE|SHOW_CONTEXT)) +MAKE_PROP(blur_kernel_radius, "Basic") +MAKE_PROP(boundary_offset, "Basic", UI(PAINT_MODE_SCULPT, SHOW_ALL, {SCULPT_TOOL_BOUNDARY})) +MAKE_PROP(clone_alpha, + "Basic", + UI(PAINT_MODE_TEXTURE_2D, SHOW_WORKSPACE), + UI(PAINT_MODE_TEXTURE_3D, SHOW_WORKSPACE)) +MAKE_PROP(clone_offset, + "Basic", + UI(PAINT_MODE_TEXTURE_2D, SHOW_WORKSPACE), + UI(PAINT_MODE_TEXTURE_3D, SHOW_WORKSPACE)) +MAKE_PROP(crease_pinch_factor, "Basic") +MAKE_PROP(cursor_color_add, "Basic") +MAKE_PROP(cursor_color_subtract, "Basic") +MAKE_PROP(cursor_overlay_alpha, "Basic") +MAKE_PROP(disconnected_distance_max, "Basic") +MAKE_PROP(elastic_deform_volume_preservation, + "Basic", + UI(PAINT_MODE_SCULPT, + SHOW_WORKSPACE, + {SCULPT_TOOL_ELASTIC_DEFORM | SCULPT_TOOL_BOUNDARY | SCULPT_TOOL_CLOTH})) +MAKE_PROP(falloff_angle, "Basic") +MAKE_PROP(fill_threshold, "Basic") +MAKE_PROP(flow, "Basic") +MAKE_PROP(grad_spacing, "Basic") +MAKE_PROP(hardness, "Basic") +MAKE_PROP(height, "Basic", UI(PAINT_MODE_SCULPT, SHOW_ALL, {SCULPT_TOOL_LAYER})) +MAKE_PROP(invert_to_scrape_fill, "Basic") +MAKE_PROP(mask_overlay_alpha, "Basic") +MAKE_PROP(mask_stencil_dimension, "Basic") +MAKE_PROP(mask_stencil_pos, "Basic") +MAKE_PROP(multiplane_scrape_angle, "Basic") +MAKE_PROP(normal_radius_factor, "Basic") +MAKE_PROP(normal_weight, "Basic") +MAKE_PROP(plane_offset, "Basic") +MAKE_PROP(plane_trim, "Basic") +MAKE_PROP(rake_factor, "Basic") +MAKE_PROP(sharp_threshold, "Basic") +MAKE_PROP(show_multiplane_scrape_planes_preview, "Basic") +MAKE_PROP(stencil_dimension, "Basic") +MAKE_PROP(stencil_pos, "Basic") +MAKE_PROP(surface_smooth_current_vertex, "Basic") +MAKE_PROP(surface_smooth_iterations, "Basic") +MAKE_PROP(surface_smooth_shape_preservation, "Basic") +MAKE_PROP(texture_overlay_alpha, "Basic") +MAKE_PROP(texture_sample_bias, "Basic") +MAKE_PROP(tilt_strength_factor, "Basic") +MAKE_PROP(tip_roundness, "Basic") +MAKE_PROP(tip_scale_x, "Basic") +MAKE_PROP(use_accumulate, "Basic", UI(SHOW_ALL)) +MAKE_PROP(use_adaptive_space, "Basic") +MAKE_PROP(use_airbrush, "Basic") +MAKE_PROP(use_alpha, "Basic") +MAKE_PROP(use_anchor, "Basic") +MAKE_PROP(use_connected_only, "Basic") +MAKE_PROP(use_cursor_overlay, "Basic") +MAKE_PROP(use_cursor_overlay_override, "Basic") +MAKE_PROP(use_curve, "Basic") +MAKE_PROP(use_custom_icon, "Basic") +MAKE_PROP(use_edge_to_edge, "Basic") +MAKE_PROP(use_frontface, "Basic") +MAKE_PROP(use_frontface_falloff, "Basic") +MAKE_PROP(use_grab_active_vertex, "Basic") +MAKE_PROP(use_grab_silhouette, "Basic") +MAKE_PROP(use_line, "Basic") +MAKE_PROP(use_multiplane_scrape_dynamic, "Basic") +MAKE_PROP(use_original_normal, "Basic") +MAKE_PROP(use_original_plane, "Basic") +MAKE_PROP(use_persistent, "Basic") +MAKE_PROP(use_plane_trim, "Basic") +MAKE_PROP(use_primary_overlay, "Basic") +MAKE_PROP(use_primary_overlay_override, "Basic") +MAKE_PROP(use_restore_mesh, "Basic") +MAKE_PROP(use_secondary_overlay, "Basic") +MAKE_PROP(use_secondary_overlay_override, "Basic") +MAKE_PROP(use_smooth_stroke, "Basic", UI(SHOW_WORKSPACE)) +MAKE_PROP(use_space, "Basic", UI(SHOW_WORKSPACE)) +MAKE_PROP(use_space_attenuation, "Basic", UI(SHOW_WORKSPACE)) +MAKE_PROP(use_vertex_grease_pencil, "Basic") +MAKE_PROP(weight, "Basic") +MAKE_PROP(wet_paint_radius_factor, "Basic") +MAKE_PROP(cloth_constraint_softbody_strength, + "Cloth", + UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_CLOTHABLE_TOOLS})) +MAKE_PROP(cloth_damping, "Cloth", UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_CLOTHABLE_TOOLS})) +MAKE_PROP(cloth_mass, "Cloth", UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_CLOTHABLE_TOOLS})) +MAKE_PROP(cloth_sim_falloff, + "Cloth", + UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_CLOTHABLE_TOOLS})) +MAKE_PROP(cloth_sim_limit, + "Cloth", + UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_CLOTHABLE_TOOLS})) +MAKE_PROP(use_cloth_collision, + "Cloth", + UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_CLOTHABLE_TOOLS})) +MAKE_PROP(use_cloth_pin_simulation_boundary, + "Cloth", + UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_CLOTHABLE_TOOLS})) +MAKE_PROP(color, + "Paint", + UI(PAINT_MODE_SCULPT, SHOW_ALL, {SCULPT_TOOL_PAINT}), + UI(PAINT_MODE_TEXTURE_2D, SHOW_ALL), + UI(PAINT_MODE_TEXTURE_3D, SHOW_ALL), + UI(PAINT_MODE_VERTEX, SHOW_ALL)) +MAKE_PROP(density, "Paint", SCULPT_PAINT) +MAKE_PROP(rate, "Paint") +MAKE_PROP(secondary_color, + "Paint", + UI(PAINT_MODE_SCULPT, SHOW_ALL, {SCULPT_TOOL_PAINT}), + UI(PAINT_MODE_TEXTURE_2D, SHOW_ALL), + UI(PAINT_MODE_TEXTURE_3D, SHOW_ALL), + UI(PAINT_MODE_VERTEX, SHOW_ALL)) +MAKE_PROP(use_paint_antialiasing, "Paint", UI(PAINT_MODE_TEXTURE_2D, SHOW_WORKSPACE)) +MAKE_PROP(use_paint_weight, "Paint") +MAKE_PROP(wet_mix, "Paint", SCULPT_PAINT) +MAKE_PROP(wet_persistence, "Paint", SCULPT_PAINT) +MAKE_PROP(pose_ik_segments, "Pose", UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_TOOL_POSE})) +MAKE_PROP(pose_offset, "Pose", UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_TOOL_POSE})) +MAKE_PROP(pose_smooth_iterations, + "Pose", + UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_TOOL_POSE})) +MAKE_PROP(use_pose_ik_anchored, "Pose", UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_TOOL_POSE})) +MAKE_PROP(use_pose_lock_rotation, + "Pose", + UI(PAINT_MODE_SCULPT, SHOW_WORKSPACE, {SCULPT_TOOL_POSE})) +MAKE_PROP(auto_smooth_factor, "Smooth", GEOMETRY(SHOW_WORKSPACE | SHOW_CONTEXT)) +MAKE_PROP(topology_rake_factor, "Smooth", GEOMETRY(SHOW_WORKSPACE)) +MAKE_PROP(dash_ratio, "Stroke", UI(SHOW_WORKSPACE)) +MAKE_PROP(dash_samples, "Stroke", UI(SHOW_WORKSPACE)) +MAKE_PROP(jitter, "Stroke", UI(SHOW_WORKSPACE)) +MAKE_PROP(jitter_absolute, "Stroke", UI(SHOW_WORKSPACE)) +MAKE_PROP(smooth_stroke_factor, "Stroke", UI(SHOW_WORKSPACE)) +MAKE_PROP(smooth_stroke_radius, "Stroke", UI(SHOW_WORKSPACE)) +MAKE_PROP(spacing, "Stroke", UI(SHOW_WORKSPACE)) + + + +// tst +#undef MAKE_PROP +#undef MAKE_PROP_EX +#undef SHOW_WORKSPACE +#undef SHOW_HEADER +#undef SHOW_CONTEXT +#undef SHOW_ALL +#undef AUTOMASKING +#undef SCULPT_GEO_TOOLS +#undef SCULPT_PAINT +#undef SCULPT_CLOTHABLE_TOOLS +#undef GEOMETRY diff --git a/source/blender/blenkernel/intern/scene.cc b/source/blender/blenkernel/intern/scene.cc index c921cf603de..a9106f63351 100644 --- a/source/blender/blenkernel/intern/scene.cc +++ b/source/blender/blenkernel/intern/scene.cc @@ -56,6 +56,7 @@ #include "BKE_animsys.h" #include "BKE_armature.h" #include "BKE_bpath.h" +#include "BKE_brush_channel.h" #include "BKE_cachefile.h" #include "BKE_collection.h" #include "BKE_colortools.h" @@ -162,6 +163,7 @@ static void scene_init_data(ID *id) CURVEMAP_SLOPE_POS_NEG); scene->toolsettings = DNA_struct_default_alloc(ToolSettings); + BKE_brush_channelset_toolsettings_init(scene->toolsettings); scene->toolsettings->autokey_mode = uchar(U.autokey_mode); @@ -1108,6 +1110,10 @@ static void scene_blend_write(BlendWriter *writer, ID *id, const void *id_addres BKE_screen_view3d_shading_blend_write(writer, &sce->display.shading); + if (sce->toolsettings && sce->toolsettings->unified_channels) { + BKE_brush_channelset_blend_write(sce->toolsettings->unified_channels, writer); + } + /* Freed on `do_versions()`. */ BLI_assert(sce->layer_properties == nullptr); } @@ -1165,6 +1171,16 @@ static void scene_blend_read_data(BlendDataReader *reader, ID *id) BLO_read_data_address(reader, &sce->toolsettings); if (sce->toolsettings) { + BLO_read_data_address(reader, &sce->toolsettings->unified_channels); + + BLO_read_data_address(reader, &sce->toolsettings->unified_properties); + IDP_BlendDataRead(reader, &sce->toolsettings->unified_properties); + + if (sce->toolsettings->unified_channels) { + BKE_brush_channelset_blend_read(sce->toolsettings->unified_channels, reader); + } + + BKE_brush_channelset_toolsettings_init(sce->toolsettings); /* Reset last_location and last_hit, so they are not remembered across sessions. In some files * these are also NaN, which could lead to crashes in painting. */ |