Files
test2/source/blender/blenkernel/intern/brush.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

1773 lines
54 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup bke
2011-02-27 20:40:57 +00:00
*/
#include <array>
#include <optional>
2011-02-27 20:40:57 +00:00
#include "MEM_guardedalloc.h"
#include "DNA_brush_types.h"
2019-09-12 04:34:55 +10:00
#include "DNA_defaults.h"
#include "DNA_material_types.h"
#include "DNA_scene_types.h"
#include "BLI_listbase.h"
#include "BLI_math_base.hh"
#include "BLI_math_rotation.h"
#include "BLI_rand.h"
#include "BLT_translation.hh"
#include "BKE_asset.hh"
#include "BKE_bpath.hh"
#include "BKE_brush.hh"
#include "BKE_colortools.hh"
#include "BKE_gpencil_legacy.h"
#include "BKE_grease_pencil.hh"
#include "BKE_idprop.hh"
#include "BKE_idtype.hh"
2024-01-15 12:44:04 -05:00
#include "BKE_lib_id.hh"
#include "BKE_lib_query.hh"
#include "BKE_lib_remap.hh"
#include "BKE_main.hh"
#include "BKE_material.h"
#include "BKE_paint.hh"
#include "BKE_preview_image.hh"
#include "BKE_texture.h"
2024-01-18 22:50:23 +02:00
#include "IMB_colormanagement.hh"
#include "IMB_imbuf.hh"
#include "IMB_imbuf_types.hh"
#include "RE_texture.h" /* RE_texture_evaluate */
#include "BLO_read_write.hh"
static void brush_init_data(ID *id)
{
Brush *brush = (Brush *)id;
BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(brush, id));
MEMCPY_STRUCT_AFTER(brush, DNA_struct_default_get(Brush), id);
/* enable fake user by default */
id_fake_user_set(&brush->id);
/* the default alpha falloff curve */
BKE_brush_curve_preset(brush, CURVE_PRESET_SMOOTH);
}
static void brush_copy_data(Main * /*bmain*/,
std::optional<Library *> /*owner_library*/,
ID *id_dst,
const ID *id_src,
const int flag)
{
Brush *brush_dst = (Brush *)id_dst;
const Brush *brush_src = (const Brush *)id_src;
if (brush_src->icon_imbuf) {
brush_dst->icon_imbuf = IMB_dupImBuf(brush_src->icon_imbuf);
}
if ((flag & LIB_ID_COPY_NO_PREVIEW) == 0) {
BKE_previewimg_id_copy(&brush_dst->id, &brush_src->id);
}
else {
brush_dst->preview = nullptr;
}
brush_dst->curve = BKE_curvemapping_copy(brush_src->curve);
brush_dst->automasking_cavity_curve = BKE_curvemapping_copy(brush_src->automasking_cavity_curve);
if (brush_src->gpencil_settings != nullptr) {
brush_dst->gpencil_settings = MEM_cnew<BrushGpencilSettings>(__func__,
*(brush_src->gpencil_settings));
brush_dst->gpencil_settings->curve_sensitivity = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_sensitivity);
brush_dst->gpencil_settings->curve_strength = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_strength);
brush_dst->gpencil_settings->curve_jitter = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_jitter);
brush_dst->gpencil_settings->curve_rand_pressure = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_rand_pressure);
brush_dst->gpencil_settings->curve_rand_strength = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_rand_strength);
brush_dst->gpencil_settings->curve_rand_uv = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_rand_uv);
brush_dst->gpencil_settings->curve_rand_hue = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_rand_hue);
brush_dst->gpencil_settings->curve_rand_saturation = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_rand_saturation);
brush_dst->gpencil_settings->curve_rand_value = BKE_curvemapping_copy(
brush_src->gpencil_settings->curve_rand_value);
}
if (brush_src->curves_sculpt_settings != nullptr) {
brush_dst->curves_sculpt_settings = MEM_cnew<BrushCurvesSculptSettings>(
__func__, *(brush_src->curves_sculpt_settings));
brush_dst->curves_sculpt_settings->curve_parameter_falloff = BKE_curvemapping_copy(
brush_src->curves_sculpt_settings->curve_parameter_falloff);
}
/* enable fake user by default */
id_fake_user_set(&brush_dst->id);
}
static void brush_free_data(ID *id)
{
Brush *brush = (Brush *)id;
if (brush->icon_imbuf) {
IMB_freeImBuf(brush->icon_imbuf);
}
BKE_curvemapping_free(brush->curve);
BKE_curvemapping_free(brush->automasking_cavity_curve);
if (brush->gpencil_settings != nullptr) {
BKE_curvemapping_free(brush->gpencil_settings->curve_sensitivity);
BKE_curvemapping_free(brush->gpencil_settings->curve_strength);
BKE_curvemapping_free(brush->gpencil_settings->curve_jitter);
BKE_curvemapping_free(brush->gpencil_settings->curve_rand_pressure);
BKE_curvemapping_free(brush->gpencil_settings->curve_rand_strength);
BKE_curvemapping_free(brush->gpencil_settings->curve_rand_uv);
BKE_curvemapping_free(brush->gpencil_settings->curve_rand_hue);
BKE_curvemapping_free(brush->gpencil_settings->curve_rand_saturation);
BKE_curvemapping_free(brush->gpencil_settings->curve_rand_value);
MEM_SAFE_FREE(brush->gpencil_settings);
}
if (brush->curves_sculpt_settings != nullptr) {
BKE_curvemapping_free(brush->curves_sculpt_settings->curve_parameter_falloff);
MEM_freeN(brush->curves_sculpt_settings);
}
MEM_SAFE_FREE(brush->gradient);
BKE_previewimg_free(&(brush->preview));
}
static void brush_make_local(Main *bmain, ID *id, const int flags)
{
if (!ID_IS_LINKED(id)) {
return;
}
Brush *brush = (Brush *)id;
const bool lib_local = (flags & LIB_ID_MAKELOCAL_FULL_LIBRARY) != 0;
bool force_local, force_copy;
BKE_lib_id_make_local_generic_action_define(bmain, id, flags, &force_local, &force_copy);
if (brush->clone.image) {
/* Special case: `ima` always local immediately.
* Clone image should only have one user anyway. */
/* FIXME: Recursive calls affecting other non-embedded IDs are really bad and should be avoided
* in IDType callbacks. Higher-level ID management code usually does not expect such things and
* does not deal properly with it. */
/* NOTE: assert below ensures that the comment above is valid, and that exception is
* acceptable for the time being. */
BKE_lib_id_make_local(bmain, &brush->clone.image->id, LIB_ID_MAKELOCAL_ASSET_DATA_CLEAR);
BLI_assert(!ID_IS_LINKED(brush->clone.image) && brush->clone.image->id.newid == nullptr);
}
if (force_local) {
BKE_lib_id_clear_library_data(bmain, &brush->id, flags);
BKE_lib_id_expand_local(bmain, &brush->id, flags);
/* enable fake user by default */
id_fake_user_set(&brush->id);
}
else if (force_copy) {
Brush *brush_new = (Brush *)BKE_id_copy(bmain, &brush->id); /* Ensures FAKE_USER is set */
brush_new->id.us = 0;
/* setting newid is mandatory for complex make_lib_local logic... */
ID_NEW_SET(brush, brush_new);
if (!lib_local) {
BKE_libblock_remap(bmain, brush, brush_new, ID_REMAP_SKIP_INDIRECT_USAGE);
}
}
}
static void brush_foreach_id(ID *id, LibraryForeachIDData *data)
{
Brush *brush = (Brush *)id;
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->toggle_brush, IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->clone.image, IDWALK_CB_NOP);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->paint_curve, IDWALK_CB_USER);
if (brush->gpencil_settings) {
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material, IDWALK_CB_USER);
BKE_LIB_FOREACHID_PROCESS_IDSUPER(data, brush->gpencil_settings->material_alt, IDWALK_CB_USER);
}
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data, BKE_texture_mtex_foreach_id(data, &brush->mtex));
BKE_LIB_FOREACHID_PROCESS_FUNCTION_CALL(data,
BKE_texture_mtex_foreach_id(data, &brush->mask_mtex));
}
static void brush_foreach_path(ID *id, BPathForeachPathData *bpath_data)
{
Brush *brush = (Brush *)id;
if (brush->icon_filepath[0] != '\0') {
BKE_bpath_foreach_path_fixed_process(
bpath_data, brush->icon_filepath, sizeof(brush->icon_filepath));
}
}
static void brush_blend_write(BlendWriter *writer, ID *id, const void *id_address)
{
Brush *brush = (Brush *)id;
BLO_write_id_struct(writer, Brush, id_address, &brush->id);
BKE_id_blend_write(writer, &brush->id);
if (brush->curve) {
BKE_curvemapping_blend_write(writer, brush->curve);
}
if (brush->automasking_cavity_curve) {
BKE_curvemapping_blend_write(writer, brush->automasking_cavity_curve);
}
if (brush->gpencil_settings) {
BLO_write_struct(writer, BrushGpencilSettings, brush->gpencil_settings);
if (brush->gpencil_settings->curve_sensitivity) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_sensitivity);
}
if (brush->gpencil_settings->curve_strength) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_strength);
}
if (brush->gpencil_settings->curve_jitter) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_jitter);
}
if (brush->gpencil_settings->curve_rand_pressure) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_pressure);
}
if (brush->gpencil_settings->curve_rand_strength) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_strength);
}
if (brush->gpencil_settings->curve_rand_uv) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_uv);
}
if (brush->gpencil_settings->curve_rand_hue) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_hue);
}
if (brush->gpencil_settings->curve_rand_saturation) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_saturation);
}
if (brush->gpencil_settings->curve_rand_value) {
BKE_curvemapping_blend_write(writer, brush->gpencil_settings->curve_rand_value);
}
}
if (brush->curves_sculpt_settings) {
BLO_write_struct(writer, BrushCurvesSculptSettings, brush->curves_sculpt_settings);
BKE_curvemapping_blend_write(writer, brush->curves_sculpt_settings->curve_parameter_falloff);
}
if (brush->gradient) {
BLO_write_struct(writer, ColorBand, brush->gradient);
}
BKE_previewimg_blend_write(writer, brush->preview);
}
static void brush_blend_read_data(BlendDataReader *reader, ID *id)
{
Brush *brush = (Brush *)id;
/* Falloff curve. */
BLO_read_struct(reader, CurveMapping, &brush->curve);
BLO_read_struct(reader, ColorBand, &brush->gradient);
if (brush->curve) {
BKE_curvemapping_blend_read(reader, brush->curve);
}
else {
BKE_brush_curve_preset(brush, CURVE_PRESET_SHARP);
}
BLO_read_struct(reader, CurveMapping, &brush->automasking_cavity_curve);
if (brush->automasking_cavity_curve) {
BKE_curvemapping_blend_read(reader, brush->automasking_cavity_curve);
}
else {
brush->automasking_cavity_curve = BKE_sculpt_default_cavity_curve();
}
/* grease pencil */
BLO_read_struct(reader, BrushGpencilSettings, &brush->gpencil_settings);
if (brush->gpencil_settings != nullptr) {
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_sensitivity);
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_strength);
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_jitter);
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_rand_pressure);
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_rand_strength);
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_rand_uv);
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_rand_hue);
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_rand_saturation);
BLO_read_struct(reader, CurveMapping, &brush->gpencil_settings->curve_rand_value);
if (brush->gpencil_settings->curve_sensitivity) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_sensitivity);
}
if (brush->gpencil_settings->curve_strength) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_strength);
}
if (brush->gpencil_settings->curve_jitter) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_jitter);
}
if (brush->gpencil_settings->curve_rand_pressure) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_pressure);
}
if (brush->gpencil_settings->curve_rand_strength) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_strength);
}
if (brush->gpencil_settings->curve_rand_uv) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_uv);
}
if (brush->gpencil_settings->curve_rand_hue) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_hue);
}
if (brush->gpencil_settings->curve_rand_saturation) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_saturation);
}
if (brush->gpencil_settings->curve_rand_value) {
BKE_curvemapping_blend_read(reader, brush->gpencil_settings->curve_rand_value);
}
}
BLO_read_struct(reader, BrushCurvesSculptSettings, &brush->curves_sculpt_settings);
if (brush->curves_sculpt_settings) {
BLO_read_struct(reader, CurveMapping, &brush->curves_sculpt_settings->curve_parameter_falloff);
if (brush->curves_sculpt_settings->curve_parameter_falloff) {
BKE_curvemapping_blend_read(reader, brush->curves_sculpt_settings->curve_parameter_falloff);
}
}
BLO_read_struct(reader, PreviewImage, &brush->preview);
BKE_previewimg_blend_read(reader, brush->preview);
brush->icon_imbuf = nullptr;
}
static void brush_blend_read_after_liblink(BlendLibReader * /*reader*/, ID *id)
{
Brush *brush = reinterpret_cast<Brush *>(id);
/* Update brush settings depending on availability of other IDs. */
if (brush->gpencil_settings != nullptr) {
if (brush->gpencil_settings->flag & GP_BRUSH_MATERIAL_PINNED) {
if (!brush->gpencil_settings->material) {
brush->gpencil_settings->flag &= ~GP_BRUSH_MATERIAL_PINNED;
}
}
else {
brush->gpencil_settings->material = nullptr;
}
}
}
static void brush_asset_metadata_ensure(void *asset_ptr, AssetMetaData *asset_data)
{
using namespace blender;
using namespace blender::bke;
Brush *brush = reinterpret_cast<Brush *>(asset_ptr);
BLI_assert(GS(brush->id.name) == ID_BR);
/* Most names copied from brush RNA (not all are available there though). */
constexpr std::array mode_map{
std::tuple{"use_paint_sculpt", OB_MODE_SCULPT, "sculpt_brush_type"},
std::tuple{"use_paint_vertex", OB_MODE_VERTEX_PAINT, "vertex_brush_type"},
std::tuple{"use_paint_weight", OB_MODE_WEIGHT_PAINT, "weight_brush_type"},
std::tuple{"use_paint_image", OB_MODE_TEXTURE_PAINT, "image_brush_type"},
/* Sculpt UVs in the image editor while in edit mode. */
std::tuple{"use_paint_uv_sculpt", OB_MODE_EDIT, "image_brush_type"},
std::tuple{"use_paint_grease_pencil", OB_MODE_PAINT_GPENCIL_LEGACY, "gpencil_brush_type"},
/* Note: Not defined in brush RNA, own name. */
std::tuple{
"use_sculpt_grease_pencil", OB_MODE_SCULPT_GPENCIL_LEGACY, "gpencil_sculpt_brush_type"},
std::tuple{
"use_vertex_grease_pencil", OB_MODE_VERTEX_GPENCIL_LEGACY, "gpencil_vertex_brush_type"},
std::tuple{"use_weight_gpencil", OB_MODE_WEIGHT_GPENCIL_LEGACY, "gpencil_weight_brush_type"},
std::tuple{"use_paint_sculpt_curves", OB_MODE_SCULPT_CURVES, "curves_sculpt_brush_type"},
};
for (const auto &[prop_name, mode, tool_prop_name] : mode_map) {
2024-09-02 16:55:19 +10:00
/* Only add booleans for supported modes. */
if (!(brush->ob_mode & mode)) {
continue;
}
auto mode_property = idprop::create_bool(prop_name, true);
BKE_asset_metadata_idprop_ensure(asset_data, mode_property.release());
if (std::optional<int> brush_tool = BKE_paint_get_brush_type_from_obmode(brush, mode)) {
auto type_property = idprop::create(tool_prop_name, *brush_tool);
BKE_asset_metadata_idprop_ensure(asset_data, type_property.release());
}
else {
BLI_assert_unreachable();
}
}
}
static AssetTypeInfo AssetType_BR = {
/*pre_save_fn*/ brush_asset_metadata_ensure,
/*on_mark_asset_fn*/ brush_asset_metadata_ensure,
};
IDTypeInfo IDType_ID_BR = {
/*id_code*/ ID_BR,
/*id_filter*/ FILTER_ID_BR,
/*dependencies_id_types*/
(FILTER_ID_BR | FILTER_ID_IM | FILTER_ID_PC | FILTER_ID_TE | FILTER_ID_MA),
/*main_listbase_index*/ INDEX_ID_BR,
/*struct_size*/ sizeof(Brush),
/*name*/ "Brush",
/*name_plural*/ N_("brushes"),
/*translation_context*/ BLT_I18NCONTEXT_ID_BRUSH,
/*flags*/ IDTYPE_FLAGS_NO_ANIMDATA | IDTYPE_FLAGS_NO_MEMFILE_UNDO,
/*asset_type_info*/ &AssetType_BR,
/*init_data*/ brush_init_data,
/*copy_data*/ brush_copy_data,
/*free_data*/ brush_free_data,
/*make_local*/ brush_make_local,
/*foreach_id*/ brush_foreach_id,
/*foreach_cache*/ nullptr,
/*foreach_path*/ brush_foreach_path,
/*owner_pointer_get*/ nullptr,
/*blend_write*/ brush_blend_write,
/*blend_read_data*/ brush_blend_read_data,
/*blend_read_after_liblink*/ brush_blend_read_after_liblink,
/*blend_read_undo_preserve*/ nullptr,
/*lib_override_apply_post*/ nullptr,
};
static RNG *brush_rng;
void BKE_brush_system_init()
{
brush_rng = BLI_rng_new(0);
BLI_rng_srandom(brush_rng, 31415682);
}
void BKE_brush_system_exit()
{
if (brush_rng == nullptr) {
return;
}
BLI_rng_free(brush_rng);
brush_rng = nullptr;
}
2012-05-05 00:58:22 +00:00
static void brush_defaults(Brush *brush)
{
2019-09-12 04:34:55 +10:00
const Brush *brush_def = DNA_struct_default_get(Brush);
2022-07-09 15:08:02 +10:00
#define FROM_DEFAULT(member) \
memcpy((void *)&brush->member, (void *)&brush_def->member, sizeof(brush->member))
2019-09-12 04:34:55 +10:00
#define FROM_DEFAULT_PTR(member) memcpy(brush->member, brush_def->member, sizeof(brush->member))
FROM_DEFAULT(blend);
FROM_DEFAULT(flag);
FROM_DEFAULT(weight);
FROM_DEFAULT(size);
FROM_DEFAULT(alpha);
FROM_DEFAULT(hardness);
2019-09-12 04:34:55 +10:00
FROM_DEFAULT(autosmooth_factor);
FROM_DEFAULT(topology_rake_factor);
FROM_DEFAULT(crease_pinch_factor);
FROM_DEFAULT(normal_radius_factor);
FROM_DEFAULT(wet_paint_radius_factor);
FROM_DEFAULT(area_radius_factor);
FROM_DEFAULT(disconnected_distance_max);
2019-09-12 04:34:55 +10:00
FROM_DEFAULT(sculpt_plane);
FROM_DEFAULT(plane_offset);
FROM_DEFAULT(clone.alpha);
FROM_DEFAULT(normal_weight);
FROM_DEFAULT(fill_threshold);
FROM_DEFAULT(flag);
FROM_DEFAULT(sampling_flag);
2019-09-12 04:34:55 +10:00
FROM_DEFAULT_PTR(rgb);
FROM_DEFAULT_PTR(secondary_rgb);
FROM_DEFAULT(spacing);
FROM_DEFAULT(smooth_stroke_radius);
FROM_DEFAULT(smooth_stroke_factor);
FROM_DEFAULT(rate);
FROM_DEFAULT(jitter);
FROM_DEFAULT(texture_sample_bias);
FROM_DEFAULT(texture_overlay_alpha);
FROM_DEFAULT(mask_overlay_alpha);
FROM_DEFAULT(cursor_overlay_alpha);
FROM_DEFAULT(overlay_flags);
FROM_DEFAULT_PTR(add_col);
FROM_DEFAULT_PTR(sub_col);
FROM_DEFAULT(stencil_pos);
FROM_DEFAULT(stencil_dimension);
FROM_DEFAULT(mtex);
FROM_DEFAULT(mask_mtex);
FROM_DEFAULT(falloff_shape);
FROM_DEFAULT(tip_scale_x);
FROM_DEFAULT(tip_roundness);
2019-09-12 04:34:55 +10:00
#undef FROM_DEFAULT
#undef FROM_DEFAULT_PTR
}
/* Datablock add/copy/free/make_local */
2018-02-06 23:34:58 +11:00
Brush *BKE_brush_add(Main *bmain, const char *name, const eObjectMode ob_mode)
{
Brush *brush = (Brush *)BKE_id_new(bmain, ID_BR, name);
brush->ob_mode = ob_mode;
if (ob_mode == OB_MODE_SCULPT_CURVES) {
BKE_brush_init_curves_sculpt_settings(brush);
}
else if (ELEM(ob_mode,
OB_MODE_PAINT_GPENCIL_LEGACY,
OB_MODE_SCULPT_GPENCIL_LEGACY,
OB_MODE_WEIGHT_GPENCIL_LEGACY,
OB_MODE_VERTEX_GPENCIL_LEGACY))
{
BKE_brush_init_gpencil_settings(brush);
}
return brush;
}
void BKE_brush_init_gpencil_settings(Brush *brush)
{
if (brush->gpencil_settings == nullptr) {
brush->gpencil_settings = MEM_cnew<BrushGpencilSettings>("BrushGpencilSettings");
}
brush->gpencil_settings->draw_smoothlvl = 1;
brush->gpencil_settings->flag = 0;
brush->gpencil_settings->flag |= GP_BRUSH_USE_PRESSURE;
brush->gpencil_settings->draw_strength = 1.0f;
brush->gpencil_settings->draw_jitter = 0.0f;
brush->gpencil_settings->flag |= GP_BRUSH_USE_JITTER_PRESSURE;
/* curves */
brush->gpencil_settings->curve_sensitivity = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
brush->gpencil_settings->curve_strength = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
brush->gpencil_settings->curve_jitter = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
brush->gpencil_settings->curve_rand_pressure = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
brush->gpencil_settings->curve_rand_strength = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
brush->gpencil_settings->curve_rand_uv = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
brush->gpencil_settings->curve_rand_hue = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
brush->gpencil_settings->curve_rand_saturation = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
brush->gpencil_settings->curve_rand_value = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
bool BKE_brush_delete(Main *bmain, Brush *brush)
{
if (brush->id.tag & ID_TAG_INDIRECT) {
return false;
}
if (ID_REAL_USERS(brush) <= 1 && ID_EXTRA_USERS(brush) == 0 &&
BKE_library_ID_is_indirectly_used(bmain, brush))
{
return false;
}
BKE_id_delete(bmain, brush);
return true;
}
void BKE_brush_init_curves_sculpt_settings(Brush *brush)
{
if (brush->curves_sculpt_settings == nullptr) {
brush->curves_sculpt_settings = MEM_cnew<BrushCurvesSculptSettings>(__func__);
}
BrushCurvesSculptSettings *settings = brush->curves_sculpt_settings;
settings->flag = BRUSH_CURVES_SCULPT_FLAG_INTERPOLATE_RADIUS;
settings->add_amount = 1;
settings->points_per_curve = 8;
settings->minimum_length = 0.01f;
settings->curve_length = 0.3f;
settings->curve_radius = 0.01f;
settings->density_add_attempts = 100;
settings->curve_parameter_falloff = BKE_curvemapping_add(1, 0.0f, 0.0f, 1.0f, 1.0f);
}
Brush *BKE_brush_first_search(Main *bmain, const eObjectMode ob_mode)
{
LISTBASE_FOREACH (Brush *, brush, &bmain->brushes) {
if (brush->ob_mode & ob_mode) {
return brush;
}
}
return nullptr;
}
2012-05-05 00:58:22 +00:00
void BKE_brush_debug_print_state(Brush *br)
{
/* create a fake brush and set it to the defaults */
Brush def = blender::dna::shallow_zero_initialize();
2012-05-05 00:58:22 +00:00
brush_defaults(&def);
#define BR_TEST(field, t) \
if (br->field != def.field) { \
printf("br->" #field " = %" #t ";\n", br->field); \
} \
((void)0)
#define BR_TEST_FLAG(_f) \
if ((br->flag & _f) && !(def.flag & _f)) { \
printf("br->flag |= " #_f ";\n"); \
} \
else if (!(br->flag & _f) && (def.flag & _f)) { \
printf("br->flag &= ~" #_f ";\n"); \
} \
((void)0)
#define BR_TEST_FLAG_OVERLAY(_f) \
if ((br->overlay_flags & _f) && !(def.overlay_flags & _f)) { \
printf("br->overlay_flags |= " #_f ";\n"); \
} \
else if (!(br->overlay_flags & _f) && (def.overlay_flags & _f)) { \
printf("br->overlay_flags &= ~" #_f ";\n"); \
} \
((void)0)
/* print out any non-default brush state */
BR_TEST(normal_weight, f);
BR_TEST(blend, d);
BR_TEST(size, d);
/* br->flag */
BR_TEST_FLAG(BRUSH_AIRBRUSH);
BR_TEST_FLAG(BRUSH_ALPHA_PRESSURE);
BR_TEST_FLAG(BRUSH_SIZE_PRESSURE);
BR_TEST_FLAG(BRUSH_JITTER_PRESSURE);
BR_TEST_FLAG(BRUSH_SPACING_PRESSURE);
BR_TEST_FLAG(BRUSH_ANCHORED);
BR_TEST_FLAG(BRUSH_DIR_IN);
BR_TEST_FLAG(BRUSH_SPACE);
BR_TEST_FLAG(BRUSH_SMOOTH_STROKE);
BR_TEST_FLAG(BRUSH_PERSISTENT);
BR_TEST_FLAG(BRUSH_ACCUMULATE);
BR_TEST_FLAG(BRUSH_LOCK_ALPHA);
BR_TEST_FLAG(BRUSH_ORIGINAL_NORMAL);
BR_TEST_FLAG(BRUSH_OFFSET_PRESSURE);
BR_TEST_FLAG(BRUSH_SPACE_ATTEN);
BR_TEST_FLAG(BRUSH_ADAPTIVE_SPACE);
BR_TEST_FLAG(BRUSH_LOCK_SIZE);
BR_TEST_FLAG(BRUSH_EDGE_TO_EDGE);
BR_TEST_FLAG(BRUSH_DRAG_DOT);
BR_TEST_FLAG(BRUSH_INVERSE_SMOOTH_PRESSURE);
BR_TEST_FLAG(BRUSH_PLANE_TRIM);
BR_TEST_FLAG(BRUSH_FRONTFACE);
BR_TEST_FLAG(BRUSH_CUSTOM_ICON);
BR_TEST_FLAG_OVERLAY(BRUSH_OVERLAY_CURSOR);
BR_TEST_FLAG_OVERLAY(BRUSH_OVERLAY_PRIMARY);
BR_TEST_FLAG_OVERLAY(BRUSH_OVERLAY_SECONDARY);
BR_TEST_FLAG_OVERLAY(BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE);
BR_TEST_FLAG_OVERLAY(BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE);
BR_TEST_FLAG_OVERLAY(BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE);
BR_TEST(jitter, f);
BR_TEST(spacing, d);
BR_TEST(smooth_stroke_radius, d);
BR_TEST(smooth_stroke_factor, f);
BR_TEST(rate, f);
BR_TEST(alpha, f);
BR_TEST(sculpt_plane, d);
BR_TEST(plane_offset, f);
BR_TEST(autosmooth_factor, f);
BR_TEST(topology_rake_factor, f);
BR_TEST(crease_pinch_factor, f);
BR_TEST(plane_trim, f);
BR_TEST(texture_sample_bias, f);
BR_TEST(texture_overlay_alpha, d);
BR_TEST(add_col[0], f);
BR_TEST(add_col[1], f);
BR_TEST(add_col[2], f);
BR_TEST(add_col[3], f);
BR_TEST(sub_col[0], f);
BR_TEST(sub_col[1], f);
BR_TEST(sub_col[2], f);
BR_TEST(sub_col[3], f);
printf("\n");
2018-06-17 17:05:51 +02:00
#undef BR_TEST
#undef BR_TEST_FLAG
}
2012-05-05 00:58:22 +00:00
void BKE_brush_sculpt_reset(Brush *br)
{
/* enable this to see any non-default
* settings used by a brush: */
2012-05-05 00:58:22 +00:00
// BKE_brush_debug_print_state(br);
2012-05-05 00:58:22 +00:00
brush_defaults(br);
BKE_brush_curve_preset(br, CURVE_PRESET_SMOOTH);
/* Use the curve presets by default */
br->curve_preset = BRUSH_CURVE_SMOOTH;
/* Note that sculpt defaults where set when 0.5 was the default (now it's 1.0)
* assign this so logic below can remain the same. */
br->alpha = 0.5f;
/* Brush settings */
switch (br->sculpt_brush_type) {
case SCULPT_BRUSH_TYPE_DRAW_SHARP:
br->flag |= BRUSH_DIR_IN;
br->curve_preset = BRUSH_CURVE_POW4;
br->spacing = 5;
2019-09-26 16:32:27 +02:00
break;
case SCULPT_BRUSH_TYPE_DISPLACEMENT_ERASER:
br->curve_preset = BRUSH_CURVE_SMOOTHER;
br->spacing = 10;
br->alpha = 1.0f;
break;
case SCULPT_BRUSH_TYPE_SLIDE_RELAX:
br->spacing = 10;
br->alpha = 1.0f;
br->slide_deform_type = BRUSH_SLIDE_DEFORM_DRAG;
break;
case SCULPT_BRUSH_TYPE_CLAY:
br->flag |= BRUSH_SIZE_PRESSURE;
br->spacing = 3;
br->autosmooth_factor = 0.25f;
br->normal_radius_factor = 0.75f;
br->hardness = 0.65f;
break;
case SCULPT_BRUSH_TYPE_CLAY_THUMB:
br->alpha = 0.5f;
br->normal_radius_factor = 1.0f;
br->spacing = 6;
br->hardness = 0.5f;
br->flag |= BRUSH_SIZE_PRESSURE;
br->flag &= ~BRUSH_SPACE_ATTEN;
break;
case SCULPT_BRUSH_TYPE_CLAY_STRIPS:
br->flag |= BRUSH_ACCUMULATE | BRUSH_SIZE_PRESSURE;
br->flag &= ~BRUSH_SPACE_ATTEN;
br->alpha = 0.6f;
br->spacing = 5;
br->normal_radius_factor = 1.55f;
br->tip_roundness = 0.18f;
br->curve_preset = BRUSH_CURVE_SMOOTHER;
break;
case SCULPT_BRUSH_TYPE_MULTIPLANE_SCRAPE:
br->flag2 |= BRUSH_MULTIPLANE_SCRAPE_DYNAMIC | BRUSH_MULTIPLANE_SCRAPE_PLANES_PREVIEW;
br->alpha = 0.7f;
br->normal_radius_factor = 0.70f;
br->multiplane_scrape_angle = 60;
br->curve_preset = BRUSH_CURVE_SMOOTH;
br->spacing = 5;
break;
case SCULPT_BRUSH_TYPE_CREASE:
br->flag |= BRUSH_DIR_IN;
br->alpha = 0.25;
break;
case SCULPT_BRUSH_TYPE_SCRAPE:
case SCULPT_BRUSH_TYPE_FILL:
br->alpha = 0.7f;
br->area_radius_factor = 0.5f;
br->spacing = 7;
br->flag |= BRUSH_ACCUMULATE;
br->flag |= BRUSH_INVERT_TO_SCRAPE_FILL;
break;
case SCULPT_BRUSH_TYPE_ROTATE:
br->alpha = 1.0;
break;
case SCULPT_BRUSH_TYPE_SMOOTH:
br->flag &= ~BRUSH_SPACE_ATTEN;
br->spacing = 5;
br->alpha = 0.7f;
br->surface_smooth_shape_preservation = 0.5f;
br->surface_smooth_current_vertex = 0.5f;
br->surface_smooth_iterations = 4;
break;
case SCULPT_BRUSH_TYPE_SNAKE_HOOK:
br->alpha = 1.0f;
br->rake_factor = 1.0f;
break;
case SCULPT_BRUSH_TYPE_THUMB:
br->size = 75;
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE;
br->flag &= ~BRUSH_SPACE_ATTEN;
break;
case SCULPT_BRUSH_TYPE_ELASTIC_DEFORM:
br->elastic_deform_volume_preservation = 0.4f;
br->elastic_deform_type = BRUSH_ELASTIC_DEFORM_GRAB_TRISCALE;
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE;
br->flag &= ~BRUSH_SPACE_ATTEN;
break;
case SCULPT_BRUSH_TYPE_POSE:
br->pose_smooth_iterations = 4;
br->pose_ik_segments = 1;
br->flag2 |= BRUSH_POSE_IK_ANCHORED | BRUSH_USE_CONNECTED_ONLY;
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE;
br->flag &= ~BRUSH_SPACE_ATTEN;
break;
case SCULPT_BRUSH_TYPE_BOUNDARY:
Sculpt: Boundary Brush This brush includes a set of deformation modes designed to deform and control the shape of the mesh boundaries, which are really hard to do with regular sculpt brushes (and even in edit mode). This is useful for creating cloth assets and hard surface base meshes. The brush detects the mesh boundary closest to the active vertex and propagates the deformation using the brush falloff into the mesh. It includes bend, expand, inflate, grab and twist deform modes. The main use cases of this brush are the Bend and Expand deformation modes, which depend on a grid topology to create the best results. In order to do further adjustments and tweaks to the result of these deformation modes, the brush also includes the Inflate, Grab and Twist deformation modes, which do not depend that much on the topology. Grab and Inflate are the same operation that is implemented in the Grab and Inflate tools, they are also available in the boundary brush as producing deformations with regular brushes in these areas is very hard to control. Even if this brush can produce deformations in triangle meshes and meshes with a non-regular quad grid, the more regular and clean the topology is, the better. Most of the assets this brush is intended to deform are always created from a cylindrical or plane quad grid, so it should be fine. Also, its algorithms can be improved in future versions to handle more corner cases and topology patterns. Reviewed By: sergey Differential Revision: https://developer.blender.org/D8356
2020-08-10 17:57:01 +02:00
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE;
br->flag &= ~BRUSH_SPACE_ATTEN;
br->curve_preset = BRUSH_CURVE_CONSTANT;
break;
case SCULPT_BRUSH_TYPE_DRAW_FACE_SETS:
2020-03-05 14:53:23 +01:00
br->alpha = 0.5f;
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE;
br->flag &= ~BRUSH_SPACE_ATTEN;
break;
case SCULPT_BRUSH_TYPE_GRAB:
br->alpha = 0.4f;
br->size = 75;
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE;
br->flag &= ~BRUSH_SPACE_ATTEN;
break;
case SCULPT_BRUSH_TYPE_CLOTH:
br->cloth_mass = 1.0f;
br->cloth_damping = 0.01f;
br->cloth_sim_limit = 2.5f;
br->cloth_sim_falloff = 0.75f;
br->cloth_deform_type = BRUSH_CLOTH_DEFORM_DRAG;
br->flag &= ~(BRUSH_ALPHA_PRESSURE | BRUSH_SIZE_PRESSURE);
break;
case SCULPT_BRUSH_TYPE_LAYER:
br->flag &= ~BRUSH_SPACE_ATTEN;
br->hardness = 0.35f;
br->alpha = 1.0f;
br->height = 0.05f;
break;
case SCULPT_BRUSH_TYPE_PAINT:
br->hardness = 0.4f;
br->spacing = 10;
br->alpha = 1.0f;
br->flow = 1.0f;
br->density = 1.0f;
br->flag &= ~BRUSH_SPACE_ATTEN;
zero_v3(br->rgb);
copy_v3_fl(br->secondary_rgb, 1.0f);
break;
case SCULPT_BRUSH_TYPE_SMEAR:
br->alpha = 0.6f;
br->spacing = 5;
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE_ATTEN;
br->curve_preset = BRUSH_CURVE_SPHERE;
break;
case SCULPT_BRUSH_TYPE_DISPLACEMENT_SMEAR:
br->alpha = 1.0f;
br->spacing = 5;
br->hardness = 0.7f;
br->flag &= ~BRUSH_ALPHA_PRESSURE;
br->flag &= ~BRUSH_SPACE_ATTEN;
br->curve_preset = BRUSH_CURVE_SMOOTHER;
break;
default:
break;
}
/* Cursor colors */
/* Default Alpha */
br->add_col[3] = 0.90f;
br->sub_col[3] = 0.90f;
switch (br->sculpt_brush_type) {
case SCULPT_BRUSH_TYPE_DRAW:
case SCULPT_BRUSH_TYPE_DRAW_SHARP:
case SCULPT_BRUSH_TYPE_CLAY:
case SCULPT_BRUSH_TYPE_CLAY_STRIPS:
case SCULPT_BRUSH_TYPE_CLAY_THUMB:
case SCULPT_BRUSH_TYPE_LAYER:
case SCULPT_BRUSH_TYPE_INFLATE:
case SCULPT_BRUSH_TYPE_BLOB:
case SCULPT_BRUSH_TYPE_CREASE:
br->add_col[0] = 0.0f;
br->add_col[1] = 0.5f;
br->add_col[2] = 1.0f;
br->sub_col[0] = 0.0f;
br->sub_col[1] = 0.5f;
br->sub_col[2] = 1.0f;
break;
case SCULPT_BRUSH_TYPE_SMOOTH:
case SCULPT_BRUSH_TYPE_FLATTEN:
case SCULPT_BRUSH_TYPE_FILL:
case SCULPT_BRUSH_TYPE_SCRAPE:
case SCULPT_BRUSH_TYPE_MULTIPLANE_SCRAPE:
br->add_col[0] = 0.877f;
br->add_col[1] = 0.142f;
br->add_col[2] = 0.117f;
br->sub_col[0] = 0.877f;
br->sub_col[1] = 0.142f;
br->sub_col[2] = 0.117f;
break;
case SCULPT_BRUSH_TYPE_PINCH:
case SCULPT_BRUSH_TYPE_GRAB:
case SCULPT_BRUSH_TYPE_SNAKE_HOOK:
case SCULPT_BRUSH_TYPE_THUMB:
case SCULPT_BRUSH_TYPE_NUDGE:
case SCULPT_BRUSH_TYPE_ROTATE:
case SCULPT_BRUSH_TYPE_ELASTIC_DEFORM:
case SCULPT_BRUSH_TYPE_POSE:
case SCULPT_BRUSH_TYPE_BOUNDARY:
case SCULPT_BRUSH_TYPE_SLIDE_RELAX:
br->add_col[0] = 1.0f;
br->add_col[1] = 0.95f;
br->add_col[2] = 0.005f;
br->sub_col[0] = 1.0f;
br->sub_col[1] = 0.95f;
br->sub_col[2] = 0.005f;
break;
case SCULPT_BRUSH_TYPE_SIMPLIFY:
case SCULPT_BRUSH_TYPE_PAINT:
case SCULPT_BRUSH_TYPE_MASK:
case SCULPT_BRUSH_TYPE_DRAW_FACE_SETS:
case SCULPT_BRUSH_TYPE_DISPLACEMENT_ERASER:
case SCULPT_BRUSH_TYPE_DISPLACEMENT_SMEAR:
br->add_col[0] = 0.75f;
br->add_col[1] = 0.75f;
br->add_col[2] = 0.75f;
br->sub_col[0] = 0.75f;
br->sub_col[1] = 0.75f;
br->sub_col[2] = 0.75f;
break;
case SCULPT_BRUSH_TYPE_CLOTH:
br->add_col[0] = 1.0f;
br->add_col[1] = 0.5f;
br->add_col[2] = 0.1f;
br->sub_col[0] = 1.0f;
br->sub_col[1] = 0.5f;
br->sub_col[2] = 0.1f;
break;
default:
break;
}
}
void BKE_brush_curve_preset(Brush *b, eCurveMappingPreset preset)
{
CurveMapping *cumap = nullptr;
CurveMap *cuma = nullptr;
if (!b->curve) {
b->curve = BKE_curvemapping_add(1, 0, 0, 1, 1);
}
cumap = b->curve;
cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
cumap->preset = preset;
cuma = b->curve->cm;
BKE_curvemap_reset(cuma, &cumap->clipr, cumap->preset, CURVEMAP_SLOPE_NEGATIVE);
BKE_curvemapping_changed(cumap, false);
}
const MTex *BKE_brush_mask_texture_get(const Brush *brush, const eObjectMode object_mode)
{
if (object_mode == OB_MODE_SCULPT) {
return &brush->mtex;
}
return &brush->mask_mtex;
}
const MTex *BKE_brush_color_texture_get(const Brush *brush, const eObjectMode object_mode)
{
if (object_mode == OB_MODE_SCULPT) {
return &brush->mask_mtex;
}
return &brush->mtex;
}
float BKE_brush_sample_tex_3d(const Scene *scene,
const Brush *br,
const MTex *mtex,
const float point[3],
float rgba[4],
const int thread,
ImagePool *pool)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
float intensity = 1.0;
bool hasrgb = false;
if (mtex == nullptr || mtex->tex == nullptr) {
intensity = 1;
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) {
/* Get strength by feeding the vertex
* location directly into a texture */
hasrgb = RE_texture_evaluate(mtex, point, thread, pool, false, false, &intensity, rgba);
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) {
float rotation = -mtex->rot;
const float point_2d[2] = {point[0], point[1]};
float x, y;
float co[3];
x = point_2d[0] - br->stencil_pos[0];
y = point_2d[1] - br->stencil_pos[1];
if (rotation > 0.001f || rotation < -0.001f) {
const float angle = atan2f(y, x) + rotation;
const float flen = sqrtf(x * x + y * y);
x = flen * cosf(angle);
y = flen * sinf(angle);
}
if (fabsf(x) > br->stencil_dimension[0] || fabsf(y) > br->stencil_dimension[1]) {
zero_v4(rgba);
return 0.0f;
}
x /= (br->stencil_dimension[0]);
y /= (br->stencil_dimension[1]);
co[0] = x;
co[1] = y;
co[2] = 0.0f;
hasrgb = RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba);
}
else {
float rotation = -mtex->rot;
const float point_2d[2] = {point[0], point[1]};
float x = 0.0f, y = 0.0f; /* Quite warnings */
float invradius = 1.0f; /* Quite warnings */
float co[3];
2013-03-26 22:45:06 +00:00
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
/* keep coordinates relative to mouse */
rotation -= ups->brush_rotation;
x = point_2d[0] - ups->tex_mouse[0];
y = point_2d[1] - ups->tex_mouse[1];
/* use pressure adjusted size for fixed mode */
2013-03-31 03:28:46 +00:00
invradius = 1.0f / ups->pixel_radius;
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
/* leave the coordinates relative to the screen */
/* use unadjusted size for tiled mode */
invradius = 1.0f / ups->start_pixel_radius;
x = point_2d[0];
y = point_2d[1];
2013-03-26 22:45:06 +00:00
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_RANDOM) {
rotation -= ups->brush_rotation;
/* these contain a random coordinate */
x = point_2d[0] - ups->tex_mouse[0];
y = point_2d[1] - ups->tex_mouse[1];
2013-03-31 03:28:46 +00:00
invradius = 1.0f / ups->pixel_radius;
}
x *= invradius;
y *= invradius;
/* it is probably worth optimizing for those cases where
* the texture is not rotated by skipping the calls to
* atan2, sqrtf, sin, and cos. */
if (rotation > 0.001f || rotation < -0.001f) {
const float angle = atan2f(y, x) + rotation;
const float flen = sqrtf(x * x + y * y);
x = flen * cosf(angle);
y = flen * sinf(angle);
}
co[0] = x;
co[1] = y;
co[2] = 0.0f;
hasrgb = RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba);
}
intensity += br->texture_sample_bias;
if (!hasrgb) {
rgba[0] = intensity;
rgba[1] = intensity;
rgba[2] = intensity;
rgba[3] = 1.0f;
}
/* For consistency, sampling always returns color in linear space */
else if (ups->do_linear_conversion) {
IMB_colormanagement_colorspace_to_scene_linear_v3(rgba, ups->colorspace);
}
return intensity;
}
float BKE_brush_sample_masktex(
const Scene *scene, Brush *br, const float point[2], const int thread, ImagePool *pool)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
MTex *mtex = &br->mask_mtex;
float rgba[4], intensity;
if (!mtex->tex) {
return 1.0f;
}
if (mtex->brush_map_mode == MTEX_MAP_MODE_STENCIL) {
float rotation = -mtex->rot;
const float point_2d[2] = {point[0], point[1]};
float x, y;
float co[3];
x = point_2d[0] - br->mask_stencil_pos[0];
y = point_2d[1] - br->mask_stencil_pos[1];
if (rotation > 0.001f || rotation < -0.001f) {
const float angle = atan2f(y, x) + rotation;
const float flen = sqrtf(x * x + y * y);
x = flen * cosf(angle);
y = flen * sinf(angle);
}
if (fabsf(x) > br->mask_stencil_dimension[0] || fabsf(y) > br->mask_stencil_dimension[1]) {
zero_v4(rgba);
return 0.0f;
}
x /= (br->mask_stencil_dimension[0]);
y /= (br->mask_stencil_dimension[1]);
co[0] = x;
co[1] = y;
co[2] = 0.0f;
RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba);
}
else {
float rotation = -mtex->rot;
const float point_2d[2] = {point[0], point[1]};
float x = 0.0f, y = 0.0f; /* Quite warnings */
float invradius = 1.0f; /* Quite warnings */
float co[3];
if (mtex->brush_map_mode == MTEX_MAP_MODE_VIEW) {
/* keep coordinates relative to mouse */
rotation -= ups->brush_rotation_sec;
x = point_2d[0] - ups->mask_tex_mouse[0];
y = point_2d[1] - ups->mask_tex_mouse[1];
/* use pressure adjusted size for fixed mode */
invradius = 1.0f / ups->pixel_radius;
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_TILED) {
/* leave the coordinates relative to the screen */
/* use unadjusted size for tiled mode */
invradius = 1.0f / ups->start_pixel_radius;
x = point_2d[0];
y = point_2d[1];
}
else if (mtex->brush_map_mode == MTEX_MAP_MODE_RANDOM) {
rotation -= ups->brush_rotation_sec;
/* these contain a random coordinate */
x = point_2d[0] - ups->mask_tex_mouse[0];
y = point_2d[1] - ups->mask_tex_mouse[1];
invradius = 1.0f / ups->pixel_radius;
}
x *= invradius;
y *= invradius;
/* it is probably worth optimizing for those cases where
* the texture is not rotated by skipping the calls to
* atan2, sqrtf, sin, and cos. */
if (rotation > 0.001f || rotation < -0.001f) {
const float angle = atan2f(y, x) + rotation;
const float flen = sqrtf(x * x + y * y);
x = flen * cosf(angle);
y = flen * sinf(angle);
}
co[0] = x;
co[1] = y;
co[2] = 0.0f;
RE_texture_evaluate(mtex, co, thread, pool, false, false, &intensity, rgba);
}
CLAMP(intensity, 0.0f, 1.0f);
switch (br->mask_pressure) {
case BRUSH_MASK_PRESSURE_CUTOFF:
intensity = ((1.0f - intensity) < ups->size_pressure_value) ? 1.0f : 0.0f;
break;
case BRUSH_MASK_PRESSURE_RAMP:
intensity = ups->size_pressure_value + intensity * (1.0f - ups->size_pressure_value);
break;
default:
break;
}
return intensity;
}
/* Unified Size / Strength / Color */
2012-10-20 20:20:02 +00:00
/* XXX: be careful about setting size and unprojected radius
* because they depend on one another
* these functions do not set the other corresponding value
* this can lead to odd behavior if size and unprojected
* radius become inconsistent.
* the biggest problem is that it isn't possible to change
* unprojected radius because a view context is not
2013-10-31 14:10:01 +00:00
* available. my usual solution to this is to use the
2012-10-20 20:20:02 +00:00
* ratio of change of the size to change the unprojected
* radius. Not completely convinced that is correct.
* In any case, a better solution is needed to prevent
2012-10-20 20:20:02 +00:00
* inconsistency. */
const float *BKE_brush_color_get(const Scene *scene, const Brush *brush)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
return (ups->flag & UNIFIED_PAINT_COLOR) ? ups->rgb : brush->rgb;
}
const float *BKE_brush_secondary_color_get(const Scene *scene, const Brush *brush)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
return (ups->flag & UNIFIED_PAINT_COLOR) ? ups->secondary_rgb : brush->secondary_rgb;
}
void BKE_brush_color_set(Scene *scene, Brush *brush, const float color[3])
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
if (ups->flag & UNIFIED_PAINT_COLOR) {
copy_v3_v3(ups->rgb, color);
}
else {
copy_v3_v3(brush->rgb, color);
}
}
2012-05-05 00:58:22 +00:00
void BKE_brush_size_set(Scene *scene, Brush *brush, int size)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
/* make sure range is sane */
CLAMP(size, 1, MAX_BRUSH_PIXEL_RADIUS);
if (ups->flag & UNIFIED_PAINT_SIZE) {
ups->size = size;
}
else {
brush->size = size;
}
}
2015-04-17 03:10:57 +10:00
int BKE_brush_size_get(const Scene *scene, const Brush *brush)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
int size = (ups->flag & UNIFIED_PAINT_SIZE) ? ups->size : brush->size;
2018-06-17 17:05:51 +02:00
return size;
}
2017-10-05 12:51:36 +11:00
bool BKE_brush_use_locked_size(const Scene *scene, const Brush *brush)
{
const short us_flag = scene->toolsettings->unified_paint_settings.flag;
return (us_flag & UNIFIED_PAINT_SIZE) ? (us_flag & UNIFIED_PAINT_BRUSH_LOCK_SIZE) :
(brush->flag & BRUSH_LOCK_SIZE);
}
bool BKE_brush_use_size_pressure(const Brush *brush)
{
return brush->flag & BRUSH_SIZE_PRESSURE;
}
bool BKE_brush_use_alpha_pressure(const Brush *brush)
{
return brush->flag & BRUSH_ALPHA_PRESSURE;
}
bool BKE_brush_sculpt_has_secondary_color(const Brush *brush)
{
return ELEM(brush->sculpt_brush_type,
SCULPT_BRUSH_TYPE_BLOB,
SCULPT_BRUSH_TYPE_DRAW,
SCULPT_BRUSH_TYPE_DRAW_SHARP,
SCULPT_BRUSH_TYPE_INFLATE,
SCULPT_BRUSH_TYPE_CLAY,
SCULPT_BRUSH_TYPE_CLAY_STRIPS,
SCULPT_BRUSH_TYPE_CLAY_THUMB,
SCULPT_BRUSH_TYPE_PINCH,
SCULPT_BRUSH_TYPE_CREASE,
SCULPT_BRUSH_TYPE_LAYER,
SCULPT_BRUSH_TYPE_FLATTEN,
SCULPT_BRUSH_TYPE_FILL,
SCULPT_BRUSH_TYPE_SCRAPE,
SCULPT_BRUSH_TYPE_MASK);
}
2012-05-05 00:58:22 +00:00
void BKE_brush_unprojected_radius_set(Scene *scene, Brush *brush, float unprojected_radius)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
if (ups->flag & UNIFIED_PAINT_SIZE) {
ups->unprojected_radius = unprojected_radius;
}
else {
brush->unprojected_radius = unprojected_radius;
}
}
2015-04-17 03:10:57 +10:00
float BKE_brush_unprojected_radius_get(const Scene *scene, const Brush *brush)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
return (ups->flag & UNIFIED_PAINT_SIZE) ? ups->unprojected_radius : brush->unprojected_radius;
}
void BKE_brush_alpha_set(Scene *scene, Brush *brush, float alpha)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
if (ups->flag & UNIFIED_PAINT_ALPHA) {
ups->alpha = alpha;
}
else {
brush->alpha = alpha;
}
}
2015-04-17 03:10:57 +10:00
float BKE_brush_alpha_get(const Scene *scene, const Brush *brush)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
return (ups->flag & UNIFIED_PAINT_ALPHA) ? ups->alpha : brush->alpha;
}
2015-04-17 03:10:57 +10:00
float BKE_brush_weight_get(const Scene *scene, const Brush *brush)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
return (ups->flag & UNIFIED_PAINT_WEIGHT) ? ups->weight : brush->weight;
}
2012-05-05 00:58:22 +00:00
void BKE_brush_weight_set(const Scene *scene, Brush *brush, float value)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
if (ups->flag & UNIFIED_PAINT_WEIGHT) {
ups->weight = value;
}
else {
brush->weight = value;
}
}
int BKE_brush_input_samples_get(const Scene *scene, const Brush *brush)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
return (ups->flag & UNIFIED_PAINT_INPUT_SAMPLES) ? ups->input_samples : brush->input_samples;
}
void BKE_brush_input_samples_set(const Scene *scene, Brush *brush, int value)
{
UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
if (ups->flag & UNIFIED_PAINT_INPUT_SAMPLES) {
ups->input_samples = value;
}
else {
brush->input_samples = value;
}
}
2012-05-05 00:58:22 +00:00
void BKE_brush_scale_unprojected_radius(float *unprojected_radius,
int new_brush_size,
int old_brush_size)
{
float scale = new_brush_size;
/* avoid division by zero */
if (old_brush_size != 0) {
scale /= float(old_brush_size);
}
(*unprojected_radius) *= scale;
}
2015-04-17 03:10:57 +10:00
void BKE_brush_scale_size(int *r_brush_size,
float new_unprojected_radius,
float old_unprojected_radius)
{
float scale = new_unprojected_radius;
/* avoid division by zero */
if (old_unprojected_radius != 0) {
scale /= new_unprojected_radius;
}
(*r_brush_size) = int(float(*r_brush_size) * scale);
}
void BKE_brush_jitter_pos(const Scene &scene,
const Brush &brush,
const float pos[2],
float jitterpos[2])
{
float rand_pos[2];
float spread;
int diameter;
do {
rand_pos[0] = BLI_rng_get_float(brush_rng) - 0.5f;
rand_pos[1] = BLI_rng_get_float(brush_rng) - 0.5f;
} while (len_squared_v2(rand_pos) > square_f(0.5f));
if (brush.flag & BRUSH_ABSOLUTE_JITTER) {
diameter = 2 * brush.jitter_absolute;
spread = 1.0;
}
else {
diameter = 2 * BKE_brush_size_get(&scene, &brush);
spread = brush.jitter;
}
/* find random position within a circle of diameter 1 */
jitterpos[0] = pos[0] + 2 * rand_pos[0] * diameter * spread;
jitterpos[1] = pos[1] + 2 * rand_pos[1] * diameter * spread;
}
void BKE_brush_randomize_texture_coords(UnifiedPaintSettings *ups, bool mask)
2013-03-26 22:45:06 +00:00
{
/* we multiply with brush radius as an optimization for the brush
* texture sampling functions */
if (mask) {
ups->mask_tex_mouse[0] = BLI_rng_get_float(brush_rng) * ups->pixel_radius;
ups->mask_tex_mouse[1] = BLI_rng_get_float(brush_rng) * ups->pixel_radius;
}
else {
ups->tex_mouse[0] = BLI_rng_get_float(brush_rng) * ups->pixel_radius;
ups->tex_mouse[1] = BLI_rng_get_float(brush_rng) * ups->pixel_radius;
}
}
void BKE_brush_calc_curve_factors(const eBrushCurvePreset preset,
const CurveMapping *cumap,
const blender::Span<float> distances,
const float brush_radius,
const blender::MutableSpan<float> factors)
{
BLI_assert(factors.size() == distances.size());
const float radius_rcp = blender::math::rcp(brush_radius);
switch (preset) {
case BRUSH_CURVE_CUSTOM: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
factors[i] *= BKE_curvemapping_evaluateF(cumap, 0, distance * radius_rcp);
}
break;
}
case BRUSH_CURVE_SHARP: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
const float factor = 1.0f - distance * radius_rcp;
factors[i] *= factor * factor;
}
break;
}
case BRUSH_CURVE_SMOOTH: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
const float factor = 1.0f - distance * radius_rcp;
factors[i] *= 3.0f * factor * factor - 2.0f * factor * factor * factor;
}
break;
}
case BRUSH_CURVE_SMOOTHER: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
const float factor = 1.0f - distance * radius_rcp;
factors[i] *= pow3f(factor) * (factor * (factor * 6.0f - 15.0f) + 10.0f);
}
break;
}
case BRUSH_CURVE_ROOT: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
const float factor = 1.0f - distance * radius_rcp;
factors[i] *= sqrtf(factor);
}
break;
}
case BRUSH_CURVE_LIN: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
const float factor = 1.0f - distance * radius_rcp;
factors[i] *= factor;
}
break;
}
case BRUSH_CURVE_CONSTANT: {
break;
}
case BRUSH_CURVE_SPHERE: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
const float factor = 1.0f - distance * radius_rcp;
factors[i] *= sqrtf(2 * factor - factor * factor);
}
break;
}
case BRUSH_CURVE_POW4: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
const float factor = 1.0f - distance * radius_rcp;
factors[i] *= factor * factor * factor * factor;
}
break;
}
case BRUSH_CURVE_INVSQUARE: {
for (const int i : distances.index_range()) {
const float distance = distances[i];
if (distance >= brush_radius) {
factors[i] = 0.0f;
continue;
}
const float factor = 1.0f - distance * radius_rcp;
factors[i] *= factor * (2.0f - factor);
}
break;
}
}
}
float BKE_brush_curve_strength(const eBrushCurvePreset preset,
const CurveMapping *cumap,
const float distance,
const float brush_radius)
{
float p = distance;
float strength = 1.0f;
if (p >= brush_radius) {
return 0;
}
p = p / brush_radius;
p = 1.0f - p;
switch (preset) {
case BRUSH_CURVE_CUSTOM:
strength = BKE_curvemapping_evaluateF(cumap, 0, 1.0f - 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;
}
float BKE_brush_curve_strength(const Brush *br, float p, const float len)
{
return BKE_brush_curve_strength(eBrushCurvePreset(br->curve_preset), br->curve, p, len);
}
float BKE_brush_curve_strength_clamped(const Brush *br, float p, const float len)
{
float strength = BKE_brush_curve_strength(br, p, len);
CLAMP(strength, 0.0f, 1.0f);
return strength;
}
Texture paint refactoring commit This is as close as I can get to keeping the old code intact. After this commit, I will have to change existing code paths, making testing of functionality harder. Changes: * Keep only projective texturing code in paint_image_proj.c * Move 2D code to paint_image_2d.c. This needed the introduction of allocation/cleanup functions for the relevant structures. * Common code interface for both modes stays in paint_image.c (which still includes all old code, system should work as it did with the exception of non-projective 3D paint mode) and is made public. This is not a lot of code, only rectangle invalidation and undo system. * Changed the naming in the new code slightly: imapaint_ prefixed functions refer to common functions used by both systems, paint_2d_ prefixed to 2d painting. There will be an interface for the projection painting as well. Probably there is some leftover naming conversions to do. TODO: * Move operator init/exec/modal to common interface file * Get rid of old BKE_brush_painter_paint, now brush_painter_2d_paint. All code uses stroke system for the stroke management * Write space pressure management for the paint stroke system (for other systems to access as well :) ) * Move texture paint tablet presssure exception code for old bugs to stroke system (makes me wonder...aren't other systems also influenced by these pressure issues?) or up in the function hierarchy inside texture paint. This code is still not there so users with tablets may notice some issues. * possibly change other systems to pre-multiply pressure with the relevant influenced attributes in the stroke function. This could get tricky though and it's possible that it could backfire.
2013-03-07 12:11:38 +00:00
/* TODO: should probably be unified with BrushPainter stuff? */
static bool brush_gen_texture(const Brush *br,
const int side,
const bool use_secondary,
float *rect)
Texture paint refactoring commit This is as close as I can get to keeping the old code intact. After this commit, I will have to change existing code paths, making testing of functionality harder. Changes: * Keep only projective texturing code in paint_image_proj.c * Move 2D code to paint_image_2d.c. This needed the introduction of allocation/cleanup functions for the relevant structures. * Common code interface for both modes stays in paint_image.c (which still includes all old code, system should work as it did with the exception of non-projective 3D paint mode) and is made public. This is not a lot of code, only rectangle invalidation and undo system. * Changed the naming in the new code slightly: imapaint_ prefixed functions refer to common functions used by both systems, paint_2d_ prefixed to 2d painting. There will be an interface for the projection painting as well. Probably there is some leftover naming conversions to do. TODO: * Move operator init/exec/modal to common interface file * Get rid of old BKE_brush_painter_paint, now brush_painter_2d_paint. All code uses stroke system for the stroke management * Write space pressure management for the paint stroke system (for other systems to access as well :) ) * Move texture paint tablet presssure exception code for old bugs to stroke system (makes me wonder...aren't other systems also influenced by these pressure issues?) or up in the function hierarchy inside texture paint. This code is still not there so users with tablets may notice some issues. * possibly change other systems to pre-multiply pressure with the relevant influenced attributes in the stroke function. This could get tricky though and it's possible that it could backfire.
2013-03-07 12:11:38 +00:00
{
const MTex *mtex = (use_secondary) ? &br->mask_mtex : &br->mtex;
if (mtex->tex == nullptr) {
return false;
}
const float step = 2.0 / side;
int ix, iy;
float x, y;
/* Do normalized canonical view coords for texture. */
for (y = -1.0, iy = 0; iy < side; iy++, y += step) {
for (x = -1.0, ix = 0; ix < side; ix++, x += step) {
const float co[3] = {x, y, 0.0f};
float intensity;
float rgba_dummy[4];
RE_texture_evaluate(mtex, co, 0, nullptr, false, false, &intensity, rgba_dummy);
rect[iy * side + ix] = intensity;
Texture paint refactoring commit This is as close as I can get to keeping the old code intact. After this commit, I will have to change existing code paths, making testing of functionality harder. Changes: * Keep only projective texturing code in paint_image_proj.c * Move 2D code to paint_image_2d.c. This needed the introduction of allocation/cleanup functions for the relevant structures. * Common code interface for both modes stays in paint_image.c (which still includes all old code, system should work as it did with the exception of non-projective 3D paint mode) and is made public. This is not a lot of code, only rectangle invalidation and undo system. * Changed the naming in the new code slightly: imapaint_ prefixed functions refer to common functions used by both systems, paint_2d_ prefixed to 2d painting. There will be an interface for the projection painting as well. Probably there is some leftover naming conversions to do. TODO: * Move operator init/exec/modal to common interface file * Get rid of old BKE_brush_painter_paint, now brush_painter_2d_paint. All code uses stroke system for the stroke management * Write space pressure management for the paint stroke system (for other systems to access as well :) ) * Move texture paint tablet presssure exception code for old bugs to stroke system (makes me wonder...aren't other systems also influenced by these pressure issues?) or up in the function hierarchy inside texture paint. This code is still not there so users with tablets may notice some issues. * possibly change other systems to pre-multiply pressure with the relevant influenced attributes in the stroke function. This could get tricky though and it's possible that it could backfire.
2013-03-07 12:11:38 +00:00
}
}
return true;
Texture paint refactoring commit This is as close as I can get to keeping the old code intact. After this commit, I will have to change existing code paths, making testing of functionality harder. Changes: * Keep only projective texturing code in paint_image_proj.c * Move 2D code to paint_image_2d.c. This needed the introduction of allocation/cleanup functions for the relevant structures. * Common code interface for both modes stays in paint_image.c (which still includes all old code, system should work as it did with the exception of non-projective 3D paint mode) and is made public. This is not a lot of code, only rectangle invalidation and undo system. * Changed the naming in the new code slightly: imapaint_ prefixed functions refer to common functions used by both systems, paint_2d_ prefixed to 2d painting. There will be an interface for the projection painting as well. Probably there is some leftover naming conversions to do. TODO: * Move operator init/exec/modal to common interface file * Get rid of old BKE_brush_painter_paint, now brush_painter_2d_paint. All code uses stroke system for the stroke management * Write space pressure management for the paint stroke system (for other systems to access as well :) ) * Move texture paint tablet presssure exception code for old bugs to stroke system (makes me wonder...aren't other systems also influenced by these pressure issues?) or up in the function hierarchy inside texture paint. This code is still not there so users with tablets may notice some issues. * possibly change other systems to pre-multiply pressure with the relevant influenced attributes in the stroke function. This could get tricky though and it's possible that it could backfire.
2013-03-07 12:11:38 +00:00
}
ImBuf *BKE_brush_gen_radial_control_imbuf(Brush *br, bool secondary, bool display_gradient)
{
ImBuf *im = MEM_cnew<ImBuf>("radial control texture");
int side = 512;
int half = side / 2;
BKE_curvemapping_init(br->curve);
float *rect_float = (float *)MEM_callocN(sizeof(float) * side * side, "radial control rect");
IMB_assign_float_buffer(im, rect_float, IB_DO_NOT_TAKE_OWNERSHIP);
im->x = im->y = side;
const bool have_texture = brush_gen_texture(br, side, secondary, im->float_buffer.data);
if (display_gradient || have_texture) {
for (int i = 0; i < side; i++) {
for (int j = 0; j < side; j++) {
const float magn = sqrtf(pow2f(i - half) + pow2f(j - half));
const float strength = BKE_brush_curve_strength_clamped(br, magn, half);
im->float_buffer.data[i * side + j] = (have_texture) ?
im->float_buffer.data[i * side + j] * strength :
strength;
}
}
}
return im;
}
bool BKE_brush_has_cube_tip(const Brush *brush, PaintMode paint_mode)
{
switch (paint_mode) {
case PaintMode::Sculpt: {
if (brush->sculpt_brush_type == SCULPT_BRUSH_TYPE_MULTIPLANE_SCRAPE) {
return true;
}
if (ELEM(brush->sculpt_brush_type, SCULPT_BRUSH_TYPE_CLAY_STRIPS, SCULPT_BRUSH_TYPE_PAINT) &&
(brush->tip_roundness < 1.0f || brush->tip_scale_x != 1.0f))
{
return true;
}
break;
2023-04-13 13:13:56 +10:00
}
default: {
break;
}
}
return false;
}