This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/brush_engine.c

2261 lines
62 KiB
C

#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_ghash.h"
#include "BLI_listbase.h"
#include "BLI_math.h"
#include "BLI_memarena.h"
#include "BLI_mempool.h"
#include "BLI_rand.h"
#include "BLI_rect.h"
#include "BLI_smallhash.h"
#include "BLI_string.h"
#include "BLI_string_utils.h"
#include "BLI_utildefines.h"
#include "RNA_access.h"
#include "RNA_define.h"
#include "RNA_types.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_brush_engine.h"
#include "BKE_colorband.h"
#include "BKE_colortools.h"
#include "BKE_context.h"
#include "BKE_curvemapping_cache.h"
#include "BKE_curveprofile.h"
#include "BKE_lib_override.h"
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_paint.h"
#include "BLO_read_write.h"
#if defined(_MSC_VER) && !defined(__clang__)
# pragma warning(error : 4018) /* signed/unsigned mismatch */
# pragma warning(error : 4245) /* conversion from 'int' to 'unsigned int' */
# pragma warning(error : 4389) /* signed/unsigned mismatch */
# pragma warning(error : 4002) /* too many actual maeters for macro 'identifier' */
# pragma warning(error : 4003) /* not enough actual parameters for macro 'identifier' */
# pragma warning( \
error : 4022) /* 'function': pointer mismatch for actual parameter 'parameter number' */
# pragma warning(error : 4033) /* 'function' must return a value */
#endif
#define IS_CACHE_CURVE(curve) BKE_curvemapping_in_cache(curve)
// frees curve if it wasn't cached, returns cache curved
#define GET_CACHE_CURVE(curve) BKE_curvemapping_cache_get(brush_curve_cache, curve, true)
#define RELEASE_CACHE_CURVE(curve) BKE_curvemapping_cache_release(brush_curve_cache, curve)
#define RELEASE_OR_FREE_CURVE(curve) \
curve ? (BKE_curvemapping_cache_release_or_free(brush_curve_cache, curve), NULL) : NULL
#define CURVE_ADDREF(curve) BKE_curvemapping_cache_aquire(brush_curve_cache, curve)
#ifdef DEBUG_CURVE_MAPPING_ALLOC
static struct {
char tag[4192];
} namestack[256] = {0};
int namestack_i = 1;
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(void *passthru)
{
namestack_i--;
return passthru;
}
# 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)
#else
# define namestack_push(name)
# define namestack_pop(passthru)
#endif
struct CurveMappingCache *brush_curve_cache = NULL;
extern BrushChannelType brush_builtin_channels[];
extern int brush_builtin_channel_len;
static bool brush_mapping_inherits(const BrushChannel *ch, const BrushMapping *mp)
{
switch (mp->inherit_mode) {
case BRUSH_MAPPING_INHERIT_NEVER:
return false;
case BRUSH_MAPPING_INHERIT_ALWAYS:
return true;
case BRUSH_MAPPING_INHERIT_CHANNEL:
return ch->flag & BRUSH_CHANNEL_INHERIT;
}
return false;
}
void BKE_brush_channel_system_init()
{
brush_curve_cache = BKE_curvemapping_cache_create();
}
void BKE_brush_channel_system_exit()
{
BKE_curvemapping_cache_free(brush_curve_cache);
}
/* returns true if curve was duplicated or initialized. */
bool BKE_brush_mapping_ensure_write(BrushMapping *mp)
{
if (mp->mapping_curve.curve && IS_CACHE_CURVE(mp->mapping_curve.curve)) {
CurveMapping *newcurve = BKE_curvemapping_copy(mp->mapping_curve.curve);
RELEASE_CACHE_CURVE(mp->mapping_curve.curve);
mp->mapping_curve.curve = newcurve;
return true;
}
if (mp->mapping_curve.preset != BRUSH_CURVE_CUSTOM) {
return false;
}
if (!mp->mapping_curve.curve) {
CurveMapping *curve = mp->mapping_curve.curve = MEM_callocN(sizeof(CurveMapping),
"brsh mapping curve");
BKE_curvemapping_set_defaults(curve, 1, 0, 0.0f, 1, 1.0f);
BKE_curvemap_reset(curve->cm,
&(struct rctf){.xmin = 0.0f, .ymin = 0.0f, .xmax = 1.0f, .ymax = 1.0f},
CURVE_PRESET_LINE,
CURVEMAP_SLOPE_POSITIVE);
BKE_curvemapping_init(curve);
return true;
}
return false;
}
void BKE_brush_channel_curve_assign(BrushChannel *ch, BrushCurve *curve)
{
RELEASE_OR_FREE_CURVE(ch->curve.curve);
if (curve->curve) {
if (IS_CACHE_CURVE(curve->curve)) {
ch->curve.curve = curve->curve;
CURVE_ADDREF(curve->curve);
}
else {
ch->curve.curve = BKE_curvemapping_copy(curve->curve);
BKE_curvemapping_init(curve->curve);
}
}
else {
ch->curve.curve = NULL;
}
ch->curve.preset = curve->preset;
}
// returns true if curve was duplicated
bool BKE_brush_channel_curve_ensure_write(BrushCurve *curve)
{
if (curve->preset != BRUSH_CURVE_CUSTOM) {
return false;
}
BKE_brush_channel_curvemapping_get(curve, true);
if (IS_CACHE_CURVE(curve->curve)) {
curve->curve = BKE_curvemapping_copy(curve->curve);
return true;
}
return false;
}
/*
Brush command lists.
Command lists are built dynamically from
brush flags, pen input settings, etc.
Eventually they will be generated by node
networks. BrushCommandPreset will be
generated from the node group inputs.
*/
void BKE_brush_channeltype_rna_check(BrushChannelType *def,
int (*getIconFromName)(const char *name))
{
if (def->rna_enumdef) {
return;
}
if (!def->user_defined) {
// builtin channel types are never freed, don't use guardedalloc
def->rna_enumdef = malloc(sizeof(EnumPropertyItem) * ARRAY_SIZE(def->enumdef));
}
else {
def->rna_enumdef = MEM_calloc_arrayN(
ARRAY_SIZE(def->enumdef), sizeof(EnumPropertyItem), "def->rna_enumdef");
}
for (int i = 0; i < ARRAY_SIZE(def->enumdef); i++) {
if (def->enumdef[i].value == -1 || i == ARRAY_SIZE(def->enumdef) - 1) {
def->rna_enumdef[i].value = 0;
def->rna_enumdef[i].identifier = NULL;
def->rna_enumdef[i].icon = 0;
def->rna_enumdef[i].name = NULL;
def->rna_enumdef[i].value = 0;
break;
}
EnumPropertyItem *item = def->rna_enumdef + i;
item->value = def->enumdef[i].value;
item->identifier = def->enumdef[i].identifier;
item->icon = getIconFromName ? getIconFromName(def->enumdef[i].icon) : -1;
item->name = def->enumdef[i].name;
item->description = def->enumdef[i].description;
// detect seperaters
if (!item->value && !item->identifier[0] && !item->name[0] && !item->description[0]) {
item->description = NULL;
item->name = NULL;
item->icon = 0;
}
}
}
void BKE_brush_channel_free_data(BrushChannel *ch)
{
MEM_SAFE_FREE(ch->category);
if (ch->curve.curve) {
RELEASE_OR_FREE_CURVE(ch->curve.curve);
}
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
BrushMapping *mp = ch->mappings + i;
if (mp->mapping_curve.curve) {
RELEASE_OR_FREE_CURVE(mp->mapping_curve.curve);
}
}
}
void BKE_brush_channel_free(BrushChannel *ch)
{
BKE_brush_channel_free_data(ch);
MEM_freeN(ch);
}
static void copy_channel_data_keep_mappings(BrushChannel *dst,
BrushChannel *src,
bool keep_idname_and_def)
{
if (!keep_idname_and_def) {
BLI_strncpy(dst->name, src->name, sizeof(dst->name));
BLI_strncpy(dst->idname, src->idname, sizeof(dst->idname));
dst->def = src->def;
}
dst->flag = src->flag;
dst->type = src->type;
dst->ui_order = src->ui_order;
switch (src->type) {
case BRUSH_CHANNEL_TYPE_CURVE:
dst->curve.preset = src->curve.preset;
if (dst->curve.curve && IS_CACHE_CURVE(dst->curve.curve)) {
RELEASE_CACHE_CURVE(dst->curve.curve);
}
else if (dst->curve.curve) {
BKE_curvemapping_free(dst->curve.curve);
}
if (src->curve.curve && !IS_CACHE_CURVE(src->curve.curve)) {
dst->curve.curve = BKE_curvemapping_cache_get(brush_curve_cache, src->curve.curve, false);
}
else {
dst->curve.curve = src->curve.curve;
if (dst->curve.curve) {
CURVE_ADDREF(dst->curve.curve);
}
}
break;
case BRUSH_CHANNEL_TYPE_FLOAT:
dst->fvalue = src->fvalue;
break;
case BRUSH_CHANNEL_TYPE_BOOL:
case BRUSH_CHANNEL_TYPE_ENUM:
case BRUSH_CHANNEL_TYPE_BITMASK:
case BRUSH_CHANNEL_TYPE_INT:
dst->ivalue = src->ivalue;
break;
case BRUSH_CHANNEL_TYPE_VEC3:
case BRUSH_CHANNEL_TYPE_VEC4:
copy_v4_v4(dst->vector, src->vector);
break;
}
}
void BKE_brush_channel_copy_final_data(BrushChannel *dst,
BrushChannel *src_child,
BrushChannel *src_parent,
bool keep_mapping,
bool keep_idname_and_def)
{
if (!src_child || !src_parent) {
BKE_brush_channel_copy_data(
dst, src_child ? src_child : src_parent, keep_mapping, keep_idname_and_def);
return;
}
if (src_child->flag & BRUSH_CHANNEL_INHERIT) {
BKE_brush_channel_copy_data(dst, src_parent, keep_mapping, keep_idname_and_def);
}
else {
BKE_brush_channel_copy_data(dst, src_child, keep_mapping, keep_idname_and_def);
}
if (keep_mapping) {
return;
}
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
BrushMapping *mp = NULL;
switch (src_child->mappings[i].inherit_mode) {
case BRUSH_MAPPING_INHERIT_CHANNEL:
mp = src_child->flag & BRUSH_CHANNEL_INHERIT ? src_parent->mappings + i :
src_child->mappings + i;
break;
case BRUSH_MAPPING_INHERIT_ALWAYS:
mp = src_parent->mappings + i;
break;
case BRUSH_MAPPING_INHERIT_NEVER:
mp = src_child->mappings + i;
break;
}
if (UNLIKELY(!mp)) {
continue;
}
BKE_brush_mapping_copy_data(dst->mappings + i, mp);
}
}
void BKE_brush_channel_copy_data(BrushChannel *dst,
BrushChannel *src,
bool keep_mapping,
bool keep_idname_and_def)
{
if (keep_mapping) {
copy_channel_data_keep_mappings(dst, src, keep_idname_and_def);
return;
}
if (src->type == BRUSH_CHANNEL_TYPE_CURVE) {
if (dst->curve.curve && IS_CACHE_CURVE(dst->curve.curve)) {
RELEASE_CACHE_CURVE(dst->curve.curve);
}
else if (dst->curve.curve) {
BKE_curvemapping_free(dst->curve.curve);
dst->curve.curve = NULL;
}
}
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
BrushMapping *mp = dst->mappings + i;
if (!mp->mapping_curve.curve) {
continue;
}
if (IS_CACHE_CURVE(mp->mapping_curve.curve)) {
RELEASE_CACHE_CURVE(mp->mapping_curve.curve);
}
else {
BKE_curvemapping_free(mp->mapping_curve.curve);
}
mp->mapping_curve.curve = NULL;
}
// preserve linked list pointers
void *next = dst->next, *prev = dst->prev;
char *idname = NULL;
BrushChannelType *def = dst->def;
if (keep_idname_and_def) {
idname = BLI_array_alloca(idname, sizeof(dst->idname));
memcpy(idname, dst->idname, sizeof(dst->idname));
}
*dst = *src;
if (keep_idname_and_def) {
memcpy(dst->idname, idname, sizeof(dst->idname));
dst->def = def;
}
if (dst->category) {
dst->category = BLI_strdup(dst->category);
}
if (src->curve.curve) {
if (!IS_CACHE_CURVE(src->curve.curve)) {
/* Can't use GET_CACHE_CURVE macro since it will free src->curve.curve. */
dst->curve.curve = BKE_curvemapping_cache_get(brush_curve_cache, src->curve.curve, false);
}
else {
CURVE_ADDREF(dst->curve.curve);
}
}
dst->next = next;
dst->prev = prev;
namestack_push(__func__);
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
dst->mappings[i].mapping_curve.curve = NULL;
BKE_brush_mapping_copy_data(dst->mappings + i, src->mappings + i);
dst->mappings[i].type = i;
}
namestack_pop(NULL);
}
void BKE_brush_channel_init(BrushChannel *ch, BrushChannelType *def)
{
// preserve linked list pointers
BrushChannel *next = ch->next, *prev = ch->prev;
memset(ch, 0, sizeof(*ch));
ch->next = next;
ch->prev = prev;
strcpy(ch->name, def->name);
strcpy(ch->idname, def->idname);
ch->flag = def->flag;
ch->curve.preset = def->curve_preset;
ch->curve.preset_slope_negative = def->curve_preset_slope_neg;
ch->fvalue = def->fvalue;
ch->ivalue = def->ivalue;
copy_v4_v4(ch->vector, def->vector);
ch->type = def->type;
ch->def = def;
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
BrushMapping *mp = ch->mappings + i;
if (mp->mapping_curve.curve) {
RELEASE_OR_FREE_CURVE(mp->mapping_curve.curve);
}
mp->type = i;
float min, max;
BrushMappingDef *mdef = (&def->mappings.pressure) + i;
if (!mdef->no_default) {
min = 0.0f;
max = 1.0f;
}
else {
min = mdef->min;
max = mdef->max;
}
if (mdef->inv) {
mp->flag |= BRUSH_MAPPING_INVERT;
}
if (mdef->inherit) {
mp->inherit_mode = BRUSH_MAPPING_INHERIT_ALWAYS;
}
mp->min = min;
mp->max = max;
if (mdef->curve != CURVE_PRESET_LINE) {
mp->mapping_curve.preset = BRUSH_CURVE_CUSTOM;
BKE_brush_mapping_ensure_write(mp);
mp->mapping_curve.curve = MEM_callocN(sizeof(CurveMapping), "CurveMapping");
BKE_curvemapping_set_defaults(mp->mapping_curve.curve, 1, 0, 0.0f, 1, 1.0f);
BKE_curvemap_reset(mp->mapping_curve.curve->cm,
&(struct rctf){.xmin = 0.0f, .ymin = 0.0f, .xmax = 1.0f, .ymax = 1.0f},
mdef->curve,
CURVEMAP_SLOPE_POSITIVE);
BKE_curvemapping_init(mp->mapping_curve.curve);
mp->mapping_curve.curve = GET_CACHE_CURVE(
mp->mapping_curve.curve); /* Frees original curve */
}
else {
mp->mapping_curve.preset = BRUSH_CURVE_LIN;
}
mp->blendmode = !mdef->no_default ? MA_RAMP_MULT : mdef->blendmode;
mp->factor = mdef->factor == 0.0f ? 1.0f : mdef->factor;
mp->premultiply_factor = 1.0f;
mp->func_cutoff = mdef->func_cutoff != 0.0f ? mdef->func_cutoff : 0.5f;
if (i == BRUSH_MAPPING_STROKE_T) {
mp->mapfunc = BRUSH_MAPFUNC_COS;
}
if (mdef->enabled) {
mp->flag |= BRUSH_MAPPING_ENABLED;
}
}
}
CurveMapping *BKE_brush_channel_curvemapping_get(BrushCurve *curve, bool force_create)
{
if ((force_create || curve->preset == BRUSH_CURVE_CUSTOM) && !curve->curve) {
CurveMapping *cumap = curve->curve = MEM_callocN(sizeof(CurveMapping), "channel CurveMapping");
int preset = CURVE_PRESET_LINE;
/* brush and curvemapping presets aren't perfectly compatible,
try to convert in reasonable manner*/
switch (curve->preset) {
case BRUSH_CURVE_SMOOTH:
case BRUSH_CURVE_SMOOTHER:
preset = CURVE_PRESET_SMOOTH;
break;
case BRUSH_CURVE_SHARP:
preset = CURVE_PRESET_SHARP;
break;
case BRUSH_CURVE_POW4:
preset = CURVE_PRESET_POW3;
break;
}
BKE_curvemapping_set_defaults(cumap, 1, 0.0f, 0.0f, 1.0f, 1.0f);
BKE_curvemap_reset(cumap->cm,
&(struct rctf){.xmin = 0, .ymin = 0.0, .xmax = 1.0, .ymax = 1.0},
preset,
curve->preset_slope_negative ? 0 : 1);
BKE_curvemapping_init(cumap);
}
return curve->curve;
}
float BKE_brush_channel_curve_evaluate(BrushChannel *ch, float val, const float maxval)
{
BKE_brush_channel_curvemapping_get(&ch->curve, false);
return BKE_brush_curve_strength_ex(ch->curve.preset, ch->curve.curve, val, maxval, true);
}
BrushChannelSet *BKE_brush_channelset_create(const char *info)
{
static char *tags[512] = {0};
char buf[512] = "BrushChannelSet", *tag;
if (info) {
strcat(buf, " ");
strcat(buf, info);
for (int i = 0; i < ARRAY_SIZE(tags); i++) {
if (tags[i] && STREQ(tags[i], buf)) {
tag = tags[i];
}
else if (!tags[i]) {
tags[i] = tag = strdup(buf);
}
}
}
BrushChannelSet *chset = (BrushChannelSet *)MEM_callocN(sizeof(BrushChannelSet),
info ? tag : "BrushChannelSet");
chset->channelmap = BLI_ghash_str_new("BrushChannelSet ghash");
return chset;
}
void BKE_brush_channelset_free(BrushChannelSet *chset)
{
BrushChannel *ch, *next;
BLI_ghash_free(chset->channelmap, NULL, NULL);
for (ch = chset->channels.first; ch; ch = next) {
next = ch->next;
BKE_brush_channel_free(ch);
}
MEM_freeN(chset);
}
static int _rng_seed = 0;
void BKE_brush_channelset_add(BrushChannelSet *chset, BrushChannel *ch)
{
BrushChannel *existing = BKE_brush_channelset_lookup(chset, ch->idname);
if (existing) {
printf("%s: channel %s already exists; removing existing.\n", __func__, ch->idname);
BLI_remlink(&chset->channels, existing);
BLI_ghash_remove(chset->channelmap, existing->idname, NULL, NULL);
BKE_brush_channel_free(existing);
}
BLI_addtail(&chset->channels, ch);
BLI_ghash_insert(chset->channelmap, ch->idname, ch);
chset->totchannel++;
}
void BKE_brush_channelset_remove(BrushChannelSet *chset, BrushChannel *ch)
{
BLI_ghash_remove(chset->channelmap, ch->idname, NULL, NULL);
BLI_remlink(&chset->channels, ch);
chset->totchannel--;
}
bool BKE_brush_channelset_remove_named(BrushChannelSet *chset, const char *idname)
{
BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
if (ch) {
BKE_brush_channelset_remove(chset, ch);
return true;
}
return false;
}
void BKE_brush_channelset_add_duplicate(BrushChannelSet *chset, BrushChannel *ch)
{
namestack_push(__func__);
#ifdef DEBUG_CURVE_MAPPING_ALLOC
BrushChannel *chnew = MEM_callocN(sizeof(*chnew), namestack_head_name);
#else
BrushChannel *chnew = MEM_callocN(sizeof(*chnew), "brush channel copy");
#endif
BKE_brush_channel_copy_data(chnew, ch, false, false);
BKE_brush_channelset_add(chset, chnew);
namestack_pop(NULL);
}
BrushChannel *BKE_brush_channelset_lookup(BrushChannelSet *chset, const char *idname)
{
return BLI_ghash_lookup(chset->channelmap, idname);
}
BrushChannel *BKE_brush_channelset_lookup_final(BrushChannelSet *child,
BrushChannelSet *parent,
const char *idname)
{
BrushChannel *ch = child ? BKE_brush_channelset_lookup(child, idname) : NULL;
BrushChannel *pch = parent ? BKE_brush_channelset_lookup(parent, idname) : NULL;
if (ch && pch) {
if (ch->flag & BRUSH_CHANNEL_INHERIT) {
return pch;
}
return ch;
}
else if (pch) {
return pch;
}
else {
return ch;
}
}
bool BKE_brush_channelset_has(BrushChannelSet *chset, const char *idname)
{
return BKE_brush_channelset_lookup(chset, idname) != NULL;
}
BrushChannelType brush_default_channel_type = {
.name = "Channel",
.idname = "CHANNEL",
.min = 0.0f,
.max = 1.0f,
.soft_min = 0.0f,
.soft_max = 1.0f,
.type = BRUSH_CHANNEL_TYPE_FLOAT,
.flag = 0,
.ivalue = 0,
.fvalue = 0.0f,
.mappings = {.pressure = {.curve = CURVE_PRESET_LINE,
.enabled = false,
.inv = false,
.blendmode = MA_RAMP_BLEND}}};
BrushChannelType *BKE_brush_default_channel_def()
{
return &brush_default_channel_type;
}
void BKE_brush_channel_def_copy(BrushChannelType *dst, BrushChannelType *src)
{
memcpy(dst, src, sizeof(*dst));
}
BrushChannelType *BKE_brush_builtin_channel_def_find(const char *name)
{
for (int i = 0; i < brush_builtin_channel_len; i++) {
BrushChannelType *def = brush_builtin_channels + i;
if (STREQ(def->idname, name)) {
return def;
}
}
return NULL;
}
BrushChannel *BKE_brush_channelset_add_builtin(BrushChannelSet *chset, const char *idname)
{
BrushChannelType *def = BKE_brush_builtin_channel_def_find(idname);
if (!def) {
printf("%s: Could not find brush %s\n", __func__, idname);
return NULL;
}
namestack_push(__func__);
BrushChannel *ch = MEM_callocN(sizeof(*ch), "BrushChannel");
BKE_brush_channel_init(ch, def);
BKE_brush_channelset_add(chset, ch);
namestack_pop(NULL);
return ch;
}
BrushChannel *BKE_brush_channelset_ensure_builtin(BrushChannelSet *chset, const char *idname)
{
namestack_push(__func__);
BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
if (ch) {
namestack_pop(NULL);
return ch;
}
ch = BKE_brush_channelset_add_builtin(chset, idname);
namestack_pop(NULL);
return ch;
}
void BKE_brush_channelset_clear_inherit(BrushChannelSet *chset)
{
BrushChannel *ch;
for (ch = chset->channels.first; ch; ch = ch->next) {
ch->flag &= ~(BRUSH_CHANNEL_INHERIT | BRUSH_CHANNEL_INHERIT_IF_UNSET);
}
}
void BKE_brush_channelset_ensure_existing(BrushChannelSet *chset, BrushChannel *existing)
{
if (BKE_brush_channelset_has(chset, existing->idname)) {
return;
}
namestack_push(__func__);
BKE_brush_channelset_add_duplicate(chset, existing);
namestack_pop(NULL);
}
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;
BrushChannel *ch;
for (ch = chset->channels.first; ch; ch = ch->next) {
BrushChannel *ch2 = BKE_brush_channelset_lookup(dst, ch->idname);
if (ch2 && step > 0) {
continue;
}
if (!ch2) {
BKE_brush_channelset_add_duplicate(dst, ch);
}
else {
BKE_brush_channel_copy_data(ch2, ch, false, false);
}
}
}
BrushChannel *pch;
for (pch = parent->channels.first; pch; pch = pch->next) {
BrushChannel *mch = BKE_brush_channelset_lookup(dst, pch->idname);
BrushChannel *ch = BKE_brush_channelset_lookup(child, pch->idname);
if (!ch) {
continue;
}
if (ch->flag & BRUSH_CHANNEL_INHERIT) {
BKE_brush_channel_copy_data(mch, pch, true, false);
}
/* apply mapping inheritance flags, which are respected
even for non inherited channels.*/
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
if (brush_mapping_inherits(ch, ch->mappings + i)) {
BKE_brush_mapping_copy_data(mch->mappings + i, pch->mappings + i);
}
else {
BKE_brush_mapping_copy_data(mch->mappings + i, ch->mappings + i);
}
}
if (ch->flag & BRUSH_CHANNEL_INHERIT) {
continue;
}
/*TODO: should inherit if unset should always apply, i.e. this block should be moved above the
* previous one?*/
if (ch->type == BRUSH_CHANNEL_TYPE_BITMASK && (ch->flag & BRUSH_CHANNEL_INHERIT_IF_UNSET)) {
mch->ivalue = ch->ivalue | pch->ivalue;
}
}
namestack_pop(NULL);
}
#ifdef DEBUG_CURVE_MAPPING_ALLOC
BrushChannelSet *_BKE_brush_channelset_copy(BrushChannelSet *src)
#else
BrushChannelSet *BKE_brush_channelset_copy(BrushChannelSet *src)
#endif
{
BrushChannelSet *chset = BKE_brush_channelset_create(NULL);
if (!src->totchannel) {
return chset;
}
namestack_push(__func__);
BrushChannel *ch;
for (ch = src->channels.first; ch; ch = ch->next) {
BKE_brush_channelset_add_duplicate(chset, ch);
}
namestack_pop(NULL);
return chset;
}
void BKE_brush_channelset_apply_mapping(BrushChannelSet *chset, BrushMappingData *mapdata)
{
BrushChannel *ch;
int n;
for (ch = chset->channels.first; ch; ch = ch->next) {
switch (ch->type) {
case BRUSH_CHANNEL_TYPE_FLOAT:
ch->fvalue = BKE_brush_channel_get_float(ch, mapdata);
break;
case BRUSH_CHANNEL_TYPE_INT:
case BRUSH_CHANNEL_TYPE_ENUM:
case BRUSH_CHANNEL_TYPE_BITMASK:
case BRUSH_CHANNEL_TYPE_BOOL:
ch->ivalue = BKE_brush_channel_get_int(ch, mapdata);
break;
case BRUSH_CHANNEL_TYPE_VEC4:
n = 4;
case BRUSH_CHANNEL_TYPE_VEC3:
n = 3;
for (int i = 0; i < n; i++) {
ch->vector[i] = (float)BKE_brush_channel_eval_mappings(
ch, mapdata, (double)ch->vector[i], i);
}
break;
}
// disable input mapping
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
ch->mappings[i].flag &= ~BRUSH_MAPPING_ENABLED;
}
}
}
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("params_final");
BKE_brush_channelset_merge(cmd->params_final, cmd->params, chset_final);
if (cmd->params_mapped) {
BKE_brush_channelset_free(cmd->params_mapped);
}
cmd->params_mapped = BKE_brush_channelset_copy(cmd->params_final);
}
}
void BKE_brush_resolve_channels(Brush *brush, Sculpt *sd)
{
if (brush->channels_final) {
BKE_brush_channelset_free(brush->channels_final);
}
brush->channels_final = BKE_brush_channelset_create("channels_final");
BKE_brush_channelset_merge(brush->channels_final, brush->channels, sd->channels);
}
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; // 1.0f;
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->mapping_curve.preset, mp->mapping_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 = fabs(factor - f2);
break;
default:
printf("Unsupported brush mapping blend mode for %s (%s); will mix instead\n",
ch->name,
ch->idname);
break;
}
factor += (f2 - factor) * mp->factor;
}
f = factor;
CLAMP(f, ch->def->min, ch->def->max);
// f *= factor;
}
return f;
}
int BKE_brush_channelset_get_int(BrushChannelSet *chset,
const char *idname,
BrushMappingData *mapdata)
{
BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
if (!ch) {
printf("%s, unknown channel %s", __func__, idname);
return 0;
}
return BKE_brush_channel_get_int(ch, mapdata);
}
int BKE_brush_channel_get_int(BrushChannel *ch, BrushMappingData *mapdata)
{
if (channel_has_mappings(ch)) {
return (int)BKE_brush_channel_eval_mappings(ch, mapdata, (double)ch->ivalue, 0);
}
else {
return ch->ivalue;
}
}
void BKE_brush_channel_set_int(BrushChannel *ch, int val)
{
ch->ivalue = val;
}
bool BKE_brush_mapping_is_enabled(BrushChannel *child, BrushChannel *parent, int mapping)
{
if (child && parent) {
if (brush_mapping_inherits(child, child->mappings + mapping)) {
return child->mappings[mapping].flag & BRUSH_MAPPING_ENABLED;
}
else {
return parent->mappings[mapping].flag & BRUSH_MAPPING_ENABLED;
}
}
else if (child) {
return child->mappings[mapping].flag & BRUSH_MAPPING_ENABLED;
}
else if (parent) {
return parent->mappings[mapping].flag & BRUSH_MAPPING_ENABLED;
}
else {
return false;
}
}
void BKE_brush_channel_apply_mapping_flags(BrushChannel *dst,
const BrushChannel *child,
const BrushChannel *parent)
{
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
BrushMapping *mp = dst->mappings + i;
const BrushMapping *cmp = child ? child->mappings + i : NULL;
const BrushMapping *pmp = parent ? parent->mappings + i : NULL;
if (!cmp) {
if (pmp) {
*mp = *pmp;
}
continue;
}
if (pmp && brush_mapping_inherits(child, cmp)) {
*mp = *pmp;
}
else {
*mp = *cmp;
}
}
}
static BrushChannel *brush_channel_final(BrushChannelSet *child,
BrushChannelSet *parent,
const char *idname,
BrushChannel **r_child,
BrushChannel **r_parent)
{
if (!parent) {
*r_parent = NULL;
*r_child = BKE_brush_channelset_lookup(child, idname);
return *r_child;
}
BrushChannel *ch = BKE_brush_channelset_lookup(child, idname);
*r_child = ch;
*r_parent = BKE_brush_channelset_lookup(parent, idname);
if (!ch || (ch->flag & BRUSH_CHANNEL_INHERIT)) {
return *r_parent;
}
return ch;
}
int BKE_brush_channelset_get_final_int(BrushChannelSet *child,
BrushChannelSet *parent,
const char *idname,
BrushMappingData *mapdata)
{
BrushChannel *parentch, *childch;
BrushChannel *ch = brush_channel_final(child, parent, idname, &childch, &parentch);
if (ch) {
BrushChannel cpy = *ch;
BKE_brush_channel_apply_mapping_flags(&cpy, childch, parentch);
return BKE_brush_channel_get_int(&cpy, mapdata);
}
printf("%s: failed to find brush channel %s\n", __func__, idname);
return 0;
}
void BKE_brush_channelset_set_final_int(BrushChannelSet *child,
BrushChannelSet *parent,
const char *idname,
int value)
{
BrushChannel *ch = BKE_brush_channelset_lookup(child, idname);
if (!parent) {
BKE_brush_channelset_set_int(child, idname, value);
}
if (!ch || (ch->flag & BRUSH_CHANNEL_INHERIT)) {
BrushChannel *pch = BKE_brush_channelset_lookup(parent, idname);
if (pch) {
BKE_brush_channel_set_int(pch, value);
return;
}
}
if (!ch) {
printf("%s: failed to find brush channel %s\n", __func__, idname);
return;
}
BKE_brush_channel_set_int(ch, value);
}
float old_BKE_brush_channelset_get_final_float(BrushChannelSet *child,
BrushChannelSet *parent,
const char *idname,
BrushMappingData *mapdata)
{
BrushChannel *parentch, *childch;
BrushChannel *ch = brush_channel_final(child, parent, idname, &childch, &parentch);
if (ch) {
BrushChannel cpy = *ch;
BKE_brush_channel_apply_mapping_flags(&cpy, childch, parentch);
return BKE_brush_channel_get_float(&cpy, mapdata);
}
printf("%s: failed to find brush channel %s\n", __func__, idname);
return 0.0f;
}
void BKE_brush_channelset_set_final_float(BrushChannelSet *brushset,
BrushChannelSet *toolset,
const 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,
const 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)
{
return (float)BKE_brush_channel_eval_mappings(ch, mapdata, (double)ch->fvalue, 0);
}
void BKE_brush_channel_set_vector(BrushChannel *ch, float vec[4])
{
if (ch->type == BRUSH_CHANNEL_TYPE_VEC4) {
copy_v4_v4(ch->vector, vec);
}
else {
copy_v3_v3(ch->vector, vec);
}
}
int BKE_brush_channel_get_vector_size(BrushChannel *ch)
{
switch (ch->type) {
case BRUSH_CHANNEL_TYPE_VEC3:
return 3;
case BRUSH_CHANNEL_TYPE_VEC4:
return 4;
default:
return 1;
}
}
int BKE_brush_channel_get_vector(BrushChannel *ch, float out[4], BrushMappingData *mapdata)
{
int size = 3;
if (ch->type == BRUSH_CHANNEL_TYPE_VEC4) {
size = 4;
}
for (int i = 0; i < 4; i++) {
out[i] = BKE_brush_channel_eval_mappings(ch, mapdata, (float)ch->vector[i], i);
}
return size;
}
int old_BKE_brush_channelset_get_final_vector(BrushChannelSet *child,
BrushChannelSet *parent,
const char *idname,
float r_vec[4],
BrushMappingData *mapdata)
{
BrushChannel *parentch, *childch;
BrushChannel *ch = brush_channel_final(child, parent, idname, &childch, &parentch);
if (ch) {
BrushChannel cpy = *ch;
BKE_brush_channel_apply_mapping_flags(&cpy, childch, parentch);
return BKE_brush_channel_get_vector(&cpy, r_vec, mapdata);
}
printf("%s: failed to find brush channel %s\n", __func__, idname);
zero_v4(r_vec);
return 0;
}
void BKE_brush_channelset_set_final_vector(BrushChannelSet *brushset,
BrushChannelSet *toolset,
const char *idname,
float vec[4])
{
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_vector(pch, vec);
return;
}
}
if (!ch) {
printf("%s: failed to find brush channel %s\n", __func__, idname);
return;
}
BKE_brush_channel_set_vector(ch, vec);
}
int BKE_brush_channelset_get_vector(BrushChannelSet *chset,
const char *idname,
float r_vec[4],
BrushMappingData *mapdata)
{
BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
if (!ch) {
printf("%s, unknown channel %s", __func__, idname);
return 0;
}
return BKE_brush_channel_get_vector(ch, r_vec, mapdata);
}
bool BKE_brush_channelset_set_vector(BrushChannelSet *chset, const char *idname, float vec[4])
{
BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
if (!ch) {
printf("%s, unknown channel %s", __func__, idname);
return false;
}
BKE_brush_channel_set_vector(ch, vec);
return true;
}
void BKE_brush_channel_set_float(BrushChannel *ch, float val)
{
ch->fvalue = val;
}
bool BKE_brush_channelset_set_float(BrushChannelSet *chset, const char *idname, float val)
{
BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
if (!ch) {
printf("%s, unknown channel %s", __func__, idname);
return 0;
}
BKE_brush_channel_set_float(ch, val);
return true;
}
bool BKE_brush_channelset_set_int(BrushChannelSet *chset, const char *idname, int val)
{
BrushChannel *ch = BKE_brush_channelset_lookup(chset, idname);
if (!ch) {
printf("%s, unknown channel %s", __func__, idname);
return 0;
}
BKE_brush_channel_set_int(ch, val);
return true;
}
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;
}
BrushCommandList *BKE_brush_commandlist_create()
{
return MEM_callocN(sizeof(BrushCommandList), "BrushCommandList");
}
void BKE_brush_commandlist_free(BrushCommandList *cl)
{
for (int i = 0; i < cl->totcommand; i++) {
BrushCommand *cmd = cl->commands + i;
if (cmd->params) {
BKE_brush_channelset_free(cmd->params);
}
if (cmd->params_final) {
BKE_brush_channelset_free(cmd->params_final);
}
if (cmd->params_mapped) {
BKE_brush_channelset_free(cmd->params_mapped);
}
}
MEM_SAFE_FREE(cl->commands);
MEM_freeN(cl);
}
BrushCommand *BKE_brush_commandlist_add(BrushCommandList *cl,
BrushChannelSet *chset_template,
bool auto_inherit)
{
cl->totcommand++;
if (!cl->commands) {
cl->commands = MEM_callocN(sizeof(BrushCommand) * cl->totcommand, "BrushCommand");
}
else {
cl->commands = MEM_recallocN_id(
cl->commands, sizeof(BrushCommand) * cl->totcommand, "cl->commands");
}
BrushCommand *cmd = cl->commands + cl->totcommand - 1;
if (chset_template) {
cmd->params = BKE_brush_channelset_copy(chset_template);
if (auto_inherit) {
BrushChannel *ch;
for (ch = cmd->params->channels.first; ch; ch = ch->next) {
ch->flag |= BRUSH_CHANNEL_INHERIT;
}
}
}
else {
cmd->params = BKE_brush_channelset_create("params");
}
cmd->params_final = NULL;
return cmd;
}
#ifdef ADDCH
# undef ADDCH
#endif
#define ADDCH(name) BKE_brush_channelset_ensure_builtin(chset, name)
BrushCommand *BKE_brush_command_init(BrushCommand *command, int tool)
{
BrushChannelSet *chset = command->params;
namestack_push(__func__);
command->tool = tool;
ADDCH("spacing");
ADDCH("radius");
ADDCH("strength");
ADDCH("hard_edge_mode");
switch (tool) {
case SCULPT_TOOL_DRAW:
break;
case SCULPT_TOOL_SMOOTH:
ADDCH("boundary_smooth");
ADDCH("projection");
ADDCH("boundary_smooth");
ADDCH("fset_slide");
ADDCH("preserve_faceset_boundary");
break;
case SCULPT_TOOL_TOPOLOGY_RAKE:
ADDCH("fset_slide");
ADDCH("preserve_faceset_boundary");
ADDCH("boundary_smooth");
ADDCH("projection");
ADDCH("topology_rake_mode");
break;
case SCULPT_TOOL_DYNTOPO:
ADDCH("fset_slide");
ADDCH("preserve_faceset_boundary");
break;
}
BrushChannel *ch;
for (ch = command->params->channels.first; ch; ch = ch->next) {
ch->flag |= BRUSH_CHANNEL_INHERIT;
}
namestack_pop(NULL);
return command;
}
#define float_set_uninherit(chset, channel, val) \
_float_set_uninherit(chset, MAKE_BUILTIN_CH_NAME(channel), val)
#define int_set_uninherit(chset, channel, val) \
_int_set_uninherit(chset, MAKE_BUILTIN_CH_NAME(channel), val)
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;
}
static void _int_set_uninherit(BrushChannelSet *chset, const char *channel, int val)
{
BrushChannel *ch = BKE_brush_channelset_lookup(chset, channel);
if (!ch) {
printf("%s: unknown channel %s\n", __func__, channel);
return;
}
ch->ivalue = val;
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
}
static void commandlist_add_auto_fset(BrushChannelSet *chset,
BrushCommandList *cl,
Brush *brush,
int tool,
BrushMappingData *mapdata)
{
if (!BRUSHSET_GET_INT(chset, use_autofset, NULL)) {
return;
}
BrushCommand *cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true),
SCULPT_TOOL_AUTO_FSET);
float radius = BRUSHSET_GET_FLOAT(chset, radius, NULL) *
BRUSHSET_GET_FLOAT(chset, autofset_radius_scale, NULL);
float spacing = BRUSHSET_GET_FLOAT(chset, spacing, NULL);
if (BRUSHSET_GET_INT(chset, autofset_use_spacing, NULL)) {
spacing = BRUSHSET_GET_FLOAT(chset, autofset_spacing, NULL);
float_set_uninherit(cmd->params, spacing, spacing);
}
float_set_uninherit(cmd->params, radius, radius);
BrushChannel *ch = BRUSHSET_ENSURE_BUILTIN(cmd->params, falloff_curve);
BrushChannel *ch2 = BRUSHSET_LOOKUP(chset, autofset_curve);
if (ch2) {
BKE_brush_channel_curve_assign(ch, &ch2->curve);
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
}
else {
ch->flag |= BRUSH_CHANNEL_INHERIT;
}
}
static void commandlist_add_dyntopo(BrushChannelSet *chset,
BrushCommandList *cl,
Brush *brush,
int tool,
bool hard_edge_mode,
float radius_base)
{
if (!BKE_brush_channelset_get_int(chset, "dyntopo_disabled", NULL)) {
BrushCommand *cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true),
SCULPT_TOOL_DYNTOPO);
BKE_builtin_apply_hard_edge_mode(cmd->params, hard_edge_mode);
float spacing = BKE_brush_channelset_get_float(chset, "dyntopo_spacing", NULL);
float radius2 = BKE_brush_channelset_get_float(chset, "dyntopo_radius_scale", NULL);
radius2 *= radius_base;
int_set_uninherit(cmd->params, use_ctrl_invert, false);
float_set_uninherit(cmd->params, spacing, spacing);
float_set_uninherit(cmd->params, radius, radius2);
BKE_brush_channelset_inherit_mappings(cmd->params);
}
}
static void bke_builtin_commandlist_create_paint(Brush *brush,
BrushChannelSet *chset,
BrushCommandList *cl,
int tool,
BrushMappingData *mapdata)
{
BrushCommand *cmd;
cmd = BKE_brush_commandlist_add(cl, chset, true);
BKE_brush_command_init(cmd, tool);
BKE_brush_channelset_inherit_mappings(cmd->params);
float radius = BRUSHSET_GET_FLOAT(chset, radius, NULL);
/* build autosmooth command */
float autosmooth_scale = BRUSHSET_GET_FLOAT(chset, autosmooth_radius_scale, NULL);
float autosmooth_projection = BRUSHSET_GET_FLOAT(chset, autosmooth_projection, NULL);
float autosmooth_spacing;
if (BRUSHSET_GET_INT(chset, autosmooth_use_spacing, NULL)) {
autosmooth_spacing = BRUSHSET_GET_FLOAT(chset, autosmooth_spacing, NULL);
}
else {
autosmooth_spacing = BRUSHSET_GET_FLOAT(chset, spacing, NULL);
}
bool hard_edge_mode = BRUSHSET_GET_INT(chset, hard_edge_mode, NULL);
commandlist_add_dyntopo(chset, cl, brush, tool, hard_edge_mode, radius);
/*build auto fset command*/
commandlist_add_auto_fset(chset, cl, brush, tool, mapdata);
float autosmooth = BRUSHSET_GET_FLOAT(chset, autosmooth, NULL);
if (autosmooth > 0.0f) {
cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true), SCULPT_TOOL_SMOOTH);
BrushChannel *ch = BRUSHSET_ENSURE_BUILTIN(cmd->params, falloff_curve);
BrushChannel *ch2 = BRUSHSET_LOOKUP(chset, autosmooth_falloff_curve);
if (ch2) {
BKE_brush_channel_curve_assign(ch, &ch2->curve);
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
}
else {
ch->flag |= BRUSH_CHANNEL_INHERIT;
}
int_set_uninherit(cmd->params, use_ctrl_invert, false);
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);
BKE_brush_channelset_inherit_mappings(cmd->params);
}
float vcol_boundary = BKE_brush_channelset_get_float(chset, "vcol_boundary_factor", NULL);
#define GETF(key) BKE_brush_channelset_get_float(chset, key, NULL)
if (vcol_boundary > 0.0f) {
cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true),
SCULPT_TOOL_VCOL_BOUNDARY);
float_set_uninherit(cmd->params, radius, radius * GETF("vcol_boundary_radius_scale"));
float_set_uninherit(cmd->params, spacing, GETF("vcol_boundary_spacing"));
float_set_uninherit(cmd->params, strength, vcol_boundary);
BKE_brush_channelset_inherit_mappings(cmd->params);
}
#undef GETF
}
void BKE_builtin_apply_hard_edge_mode(BrushChannelSet *chset, bool do_apply)
{
if (!do_apply) {
return;
}
// hard edge mode overrides fset_slide to be 0.0.
BrushChannel *ch = BRUSHSET_LOOKUP(chset, fset_slide);
if (ch) {
// clear inheritance flag
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
ch->fvalue = 0.0f;
}
// make sure preserve faceset boundaries is on
ch = BRUSHSET_LOOKUP(chset, preserve_faceset_boundary);
if (ch) {
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
ch->ivalue = 1;
}
// turn off dyntopo surface smoothing
ch = BRUSHSET_LOOKUP(chset, dyntopo_disable_smooth);
if (ch) {
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
ch->ivalue = 1;
}
}
void BKE_builtin_commandlist_create(Brush *brush,
BrushChannelSet *chset,
BrushCommandList *cl,
int tool,
BrushMappingData *mapdata)
{
BrushCommand *cmd;
BrushChannel *ch;
bool hard_edge_mode = BRUSHSET_GET_INT(chset, hard_edge_mode, NULL);
/* add main tool */
if (ELEM(tool, SCULPT_TOOL_PAINT, SCULPT_TOOL_SMEAR)) {
bke_builtin_commandlist_create_paint(brush, chset, cl, tool, mapdata);
return;
}
float radius = BKE_brush_channelset_get_float(chset, "radius", NULL);
/* Build dyntopo command. */
if (tool == SCULPT_TOOL_SNAKE_HOOK) {
/* Add twice for snake hook. */
commandlist_add_dyntopo(chset, cl, brush, tool, hard_edge_mode, radius * 1.25);
commandlist_add_dyntopo(chset, cl, brush, tool, hard_edge_mode, radius * 1.25);
}
else {
commandlist_add_dyntopo(chset, cl, brush, tool, hard_edge_mode, radius);
}
/* build main brush command */
cmd = BKE_brush_commandlist_add(cl, chset, true);
BKE_brush_command_init(cmd, tool);
BKE_builtin_apply_hard_edge_mode(cmd->params, hard_edge_mode);
BKE_brush_channelset_inherit_mappings(cmd->params);
bool no_autosmooth = ELEM(tool, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_SMOOTH, SCULPT_TOOL_MASK);
bool no_rake = ELEM(tool, SCULPT_TOOL_BOUNDARY, 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, "autosmooth_projection", NULL);
bool is_cloth = tool == SCULPT_TOOL_CLOTH;
is_cloth = is_cloth ||
(ELEM(tool, SCULPT_TOOL_BOUNDARY, SCULPT_TOOL_POSE) &&
BRUSHSET_GET_INT(chset, deform_target, NULL) == BRUSH_DEFORM_TARGET_CLOTH_SIM);
float cloth_radius_mul = 1.0f;
if (is_cloth && (ch = BRUSHSET_LOOKUP(chset, cloth_sim_limit))) {
cloth_radius_mul += ch->fvalue;
autosmooth_scale *= cloth_radius_mul;
}
float autosmooth_spacing;
if (BKE_brush_channelset_get_int(chset, "autosmooth_use_spacing", NULL)) {
autosmooth_spacing = BKE_brush_channelset_get_float(chset, "autosmooth_spacing", NULL);
}
else {
autosmooth_spacing = BKE_brush_channelset_get_float(chset, "spacing", NULL);
}
/*build auto fset command*/
commandlist_add_auto_fset(chset, cl, brush, tool, mapdata);
float autosmooth = BKE_brush_channelset_get_float(chset, "autosmooth", NULL);
if (!no_autosmooth && autosmooth > 0.0f) {
int smooth_tool = tool != SCULPT_TOOL_SLIDE_RELAX ? SCULPT_TOOL_SMOOTH : SCULPT_TOOL_RELAX;
cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true), smooth_tool);
BKE_builtin_apply_hard_edge_mode(cmd->params, hard_edge_mode);
BKE_brush_channelset_inherit_mappings(cmd->params);
BrushChannel *ch = BRUSHSET_ENSURE_BUILTIN(cmd->params, falloff_curve);
BrushChannel *ch2 = BRUSHSET_LOOKUP(chset, autosmooth_falloff_curve);
if (ch2) {
BKE_brush_channel_curve_assign(ch, &ch2->curve);
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
}
else {
ch->flag |= BRUSH_CHANNEL_INHERIT;
}
ch = BRUSHSET_LOOKUP(cmd->params, strength);
ch2 = BRUSHSET_LOOKUP(chset, autosmooth);
for (int j = 0; j < BRUSH_MAPPING_MAX; j++) {
BKE_brush_mapping_copy_data(ch->mappings + j, ch2->mappings + j);
}
int_set_uninherit(cmd->params, use_ctrl_invert, false);
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) *
cloth_radius_mul;
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", NULL)) {
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 (!no_rake && topology_rake > 0.0f) {
cmd = BKE_brush_command_init(BKE_brush_commandlist_add(cl, chset, true),
SCULPT_TOOL_TOPOLOGY_RAKE);
BKE_builtin_apply_hard_edge_mode(cmd->params, hard_edge_mode);
BrushChannel *ch = BRUSHSET_ENSURE_BUILTIN(cmd->params, falloff_curve);
BrushChannel *ch2 = BRUSHSET_LOOKUP(chset, topology_rake_falloff_curve);
if (ch2) {
BKE_brush_channel_curve_assign(ch, &ch2->curve);
ch->flag &= ~BRUSH_CHANNEL_INHERIT;
}
else {
ch->flag |= BRUSH_CHANNEL_INHERIT;
}
int_set_uninherit(cmd->params, use_ctrl_invert, false);
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);
BKE_brush_channelset_inherit_mappings(cmd->params);
}
}
void BKE_brush_channelset_read_lib(BlendLibReader *reader, ID *id, BrushChannelSet *chset)
{
}
void BKE_brush_channelset_expand(BlendExpander *expander, ID *id, BrushChannelSet *chset)
{
}
void BKE_brush_channelset_foreach_id(void *userdata,
BrushChannelSet *chset,
BrushChannelIDCallback callback)
{
// for now, do nothing; in the future brush textures (might) have ID references
}
void BKE_brush_channelset_read(BlendDataReader *reader, BrushChannelSet *chset)
{
BLO_read_list(reader, &chset->channels);
chset->channelmap = BLI_ghash_str_new("BrushChannelSet");
BrushChannel *ch;
// regenerate chset->totchannel just to be safe
chset->totchannel = 0;
for (ch = chset->channels.first; ch; ch = ch->next) {
chset->totchannel++;
BLI_ghash_insert(chset->channelmap, ch->idname, ch);
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;
if (mp->premultiply_factor == 0.0f) {
mp->premultiply_factor = 1.0f;
}
if (mp->func_cutoff == 0.0f) {
mp->func_cutoff = 0.5f;
}
if (mp->factor == 0.0f) {
mp->factor = 1.0f;
}
if (mp->min == mp->max) {
mp->max = 1.0f;
}
BLO_read_data_address(reader, &mp->curve);
BLO_read_data_address(reader, &mp->mapping_curve.curve);
/* Handle olf files. */
if (mp->curve) {
BKE_curvemapping_blend_read(reader, mp->curve);
BKE_curvemapping_init(mp->curve);
/* Make a linear curve for comparison. */
CurveMapping linear_curve = {0};
BKE_curvemapping_set_defaults(&linear_curve, 1, 0, 0.0f, 1, 1.0f);
BKE_curvemap_reset(linear_curve.cm,
&(struct rctf){.xmin = 0.0f, .ymin = 0.0f, .xmax = 1.0f, .ymax = 1.0f},
CURVE_PRESET_LINE,
CURVEMAP_SLOPE_POSITIVE);
BKE_curvemapping_init(&linear_curve);
/* Only convert curve if it's not linear. */
if (!BKE_curvemapping_equals(mp->curve, &linear_curve)) {
mp->mapping_curve.preset = BRUSH_CURVE_CUSTOM;
mp->mapping_curve.curve = GET_CACHE_CURVE(mp->curve);
}
else {
mp->mapping_curve.preset = BRUSH_CURVE_LIN;
BKE_curvemapping_free_data(mp->curve);
MEM_freeN(mp->curve);
}
mp->curve = NULL;
BKE_curvemapping_free_data(&linear_curve);
}
else if (mp->mapping_curve.curve) {
CurveMapping *curve = mp->mapping_curve.curve;
BKE_curvemapping_blend_read(reader, curve);
BKE_curvemapping_init(curve);
mp->mapping_curve.curve = GET_CACHE_CURVE(curve); /* Frees existing curve. */
}
else if (mp->mapping_curve.preset ==
BRUSH_CURVE_CUSTOM) { /* Ensure we have a curve instance. */
BKE_brush_mapping_ensure_write(mp);
mp->mapping_curve.curve = GET_CACHE_CURVE(mp->mapping_curve.curve);
}
/* Paranoia check to make sure BrushMapping.type is correct. */
mp->type = i;
}
ch->def = BKE_brush_builtin_channel_def_find(ch->idname);
if (!ch->def) {
//printf("failed to find brush definition for %s\n", ch->idname);
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 *chset)
{
/* Instantiate cached curves to ensure they get written
* (and susequently read) seperately.
*/
LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) {
BKE_brush_channel_curve_ensure_write(&ch->curve);
if (ch->type == BRUSH_CHANNEL_TYPE_CURVE && ch->curve.curve) {
BKE_curvemapping_blend_write(writer, ch->curve.curve);
}
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
BKE_brush_mapping_ensure_write(ch->mappings + i);
}
}
BLO_write_struct(writer, BrushChannelSet, chset);
LISTBASE_FOREACH (BrushChannel *, ch, &chset->channels) {
BLO_write_struct(writer, BrushChannel, ch);
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->mapping_curve.curve) {
BKE_curvemapping_blend_write(writer, mp->mapping_curve.curve);
}
}
}
}
const char *BKE_brush_mapping_type_to_str(eBrushMappingType mapping)
{
switch (mapping) {
case BRUSH_MAPPING_PRESSURE:
return "Pressure";
case BRUSH_MAPPING_ANGLE:
return "Angle";
case BRUSH_MAPPING_SPEED:
return "Speed";
case BRUSH_MAPPING_XTILT:
return "X Tilt";
case BRUSH_MAPPING_YTILT:
return "Y Tilt";
case BRUSH_MAPPING_RANDOM:
return "Random";
case BRUSH_MAPPING_STROKE_T:
return "Distance";
case BRUSH_MAPPING_MAX:
return "Error";
}
return "Error";
}
const char *BKE_brush_mapping_type_to_typename(eBrushMappingType mapping)
{
switch (mapping) {
case BRUSH_MAPPING_PRESSURE:
return "PRESSURE";
case BRUSH_MAPPING_ANGLE:
return "ANGLE";
case BRUSH_MAPPING_SPEED:
return "SPEED";
case BRUSH_MAPPING_XTILT:
return "XTILT";
case BRUSH_MAPPING_YTILT:
return "YTILT";
case BRUSH_MAPPING_RANDOM:
return "RANDOM";
case BRUSH_MAPPING_STROKE_T:
return "DISTANCE";
case BRUSH_MAPPING_MAX:
return "Error";
}
return "Error";
}
void BKE_brush_mapping_copy_data(BrushMapping *dst, BrushMapping *src)
{
if (src->mapping_curve.curve) {
RELEASE_OR_FREE_CURVE(dst->mapping_curve.curve);
dst->mapping_curve = src->mapping_curve;
if (!IS_CACHE_CURVE(src->mapping_curve.curve)) {
/* Remember that GET_CACHE_CURVE can free the input curve, call underlying API directly. */
dst->mapping_curve.curve = BKE_curvemapping_cache_get(
brush_curve_cache, src->mapping_curve.curve, false);
}
else {
dst->mapping_curve.curve = src->mapping_curve.curve;
CURVE_ADDREF(dst->mapping_curve.curve);
}
}
dst->mapping_curve.preset = src->mapping_curve.preset;
dst->blendmode = src->blendmode;
dst->min = src->min;
dst->max = src->max;
dst->factor = src->factor;
dst->flag = src->flag;
dst->blendmode = src->blendmode;
dst->func_cutoff = src->func_cutoff;
dst->mapfunc = src->mapfunc;
dst->premultiply_factor = src->premultiply_factor;
}
void BKE_brush_channelset_to_unified_settings(BrushChannelSet *chset, UnifiedPaintSettings *ups)
{
BrushChannel *ch;
if ((ch = BRUSHSET_LOOKUP(chset, radius))) {
ups->size = (int)ch->fvalue;
}
if ((ch = BRUSHSET_LOOKUP(chset, strength))) {
ups->alpha = ch->fvalue;
}
if ((ch = BRUSHSET_LOOKUP(chset, weight))) {
ups->weight = ch->fvalue;
}
}
BrushTex *BKE_brush_tex_create()
{
BrushTex *bt = MEM_callocN(sizeof(BrushTex), "BrushTex");
bt->channels = BKE_brush_channelset_create("brush tex");
return bt;
}
void BKE_brush_tex_free(BrushTex *btex)
{
BKE_brush_channelset_free(btex->channels);
MEM_freeN(btex);
}
const char *BKE_brush_channel_category_get(BrushChannel *ch)
{
return ch->category ? ch->category : ch->def->category;
}
void BKE_brush_channel_category_set(BrushChannel *ch, const char *str)
{
MEM_SAFE_FREE(ch->category);
ch->category = BLI_strdup(str);
}
void BKE_brush_channelset_inherit_mappings(BrushChannelSet *chset)
{
for (BrushChannel *ch = chset->channels.first; ch; ch = ch->next) {
BKE_brush_mapping_inherit_all(ch);
}
}
void BKE_brush_mapping_inherit_all(BrushChannel *ch)
{
for (int i = 0; i < BRUSH_MAPPING_MAX; i++) {
ch->mappings[i].inherit_mode = BRUSH_MAPPING_INHERIT_ALWAYS;
}
}
/* idea for building built-in preset node graphs:
from brush_builder import Builder;
def build(input, output):
input.add("Strength", "float", "strength").range(0.0, 3.0)
input.add("Radius", "float", "radius").range(0.01, 1024.0)
input.add("Autosmooth", "float", "autosmooth").range(0.0, 4.0)
input.add("Topology Rake", "float", "topology rake").range(0.0, 4.0)
input.add("Smooth Radius Scale", "float", "autosmooth_radius_scale").range(0.01, 5.0)
input.add("Rake Radius Scale", "float", "toporake_radius_scale").range(0.01, 5.0)
draw = input.make.tool("DRAW")
draw.radius = input.radius
draw.strength = input.strength
smooth = input.make.tool("SMOOTH")
smooth.radius = input.radius * input.autosmooth_radius_scale
smooth.strength = input.autosmooth;
smooth.flow = draw.outflow
rake = input.make.tool("TOPORAKE")
rake.radius = input.radius * input.toporake_radius_scale
rake.strength = input.topology;
rake.flow = smooth.outflow
output.out = rake.outflow
preset = Builder(build)
*/
/*
bNodeType sculpt_tool_node = {
.idname = "SculptTool",
.ui_name = "SculptTool",
};*/
/* cland-format on */