Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern/paint.cc')
-rw-r--r--source/blender/blenkernel/intern/paint.cc2290
1 files changed, 2290 insertions, 0 deletions
diff --git a/source/blender/blenkernel/intern/paint.cc b/source/blender/blenkernel/intern/paint.cc
new file mode 100644
index 00000000000..ea1bd3c1cc3
--- /dev/null
+++ b/source/blender/blenkernel/intern/paint.cc
@@ -0,0 +1,2290 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later
+ * Copyright 2009 by Nicholas Bishop. All rights reserved. */
+
+/** \file
+ * \ingroup bke
+ */
+
+#include <cstdlib>
+#include <cstring>
+
+#include "MEM_guardedalloc.h"
+
+#include "DNA_brush_types.h"
+#include "DNA_gpencil_types.h"
+#include "DNA_mesh_types.h"
+#include "DNA_meshdata_types.h"
+#include "DNA_modifier_types.h"
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+#include "DNA_space_types.h"
+#include "DNA_view3d_types.h"
+#include "DNA_workspace_types.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_hash.h"
+#include "BLI_listbase.h"
+#include "BLI_math_vector.h"
+#include "BLI_utildefines.h"
+
+#include "BLT_translation.h"
+
+#include "BKE_attribute.h"
+#include "BKE_attribute.hh"
+#include "BKE_brush.h"
+#include "BKE_ccg.h"
+#include "BKE_colortools.h"
+#include "BKE_context.h"
+#include "BKE_crazyspace.h"
+#include "BKE_deform.h"
+#include "BKE_gpencil.h"
+#include "BKE_idtype.h"
+#include "BKE_image.h"
+#include "BKE_key.h"
+#include "BKE_layer.h"
+#include "BKE_lib_id.h"
+#include "BKE_main.h"
+#include "BKE_material.h"
+#include "BKE_mesh.h"
+#include "BKE_mesh_mapping.h"
+#include "BKE_mesh_runtime.h"
+#include "BKE_modifier.h"
+#include "BKE_multires.h"
+#include "BKE_object.h"
+#include "BKE_paint.h"
+#include "BKE_pbvh.h"
+#include "BKE_subdiv_ccg.h"
+#include "BKE_subsurf.h"
+
+#include "DEG_depsgraph.h"
+#include "DEG_depsgraph_query.h"
+
+#include "RNA_enum_types.h"
+
+#include "BLO_read_write.h"
+
+#include "bmesh.h"
+
+using blender::MutableSpan;
+using blender::Span;
+
+static void palette_init_data(ID *id)
+{
+ Palette *palette = (Palette *)id;
+
+ BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(palette, id));
+
+ /* Enable fake user by default. */
+ id_fake_user_set(&palette->id);
+}
+
+static void palette_copy_data(Main *UNUSED(bmain),
+ ID *id_dst,
+ const ID *id_src,
+ const int UNUSED(flag))
+{
+ Palette *palette_dst = (Palette *)id_dst;
+ const Palette *palette_src = (const Palette *)id_src;
+
+ BLI_duplicatelist(&palette_dst->colors, &palette_src->colors);
+}
+
+static void palette_free_data(ID *id)
+{
+ Palette *palette = (Palette *)id;
+
+ BLI_freelistN(&palette->colors);
+}
+
+static void palette_blend_write(BlendWriter *writer, ID *id, const void *id_address)
+{
+ Palette *palette = (Palette *)id;
+
+ BLO_write_id_struct(writer, Palette, id_address, &palette->id);
+ BKE_id_blend_write(writer, &palette->id);
+
+ BLO_write_struct_list(writer, PaletteColor, &palette->colors);
+}
+
+static void palette_blend_read_data(BlendDataReader *reader, ID *id)
+{
+ Palette *palette = (Palette *)id;
+ BLO_read_list(reader, &palette->colors);
+}
+
+static void palette_undo_preserve(BlendLibReader *UNUSED(reader), ID *id_new, ID *id_old)
+{
+ /* Whole Palette is preserved across undo-steps, and it has no extra pointer, simple. */
+ /* NOTE: We do not care about potential internal references to self here, Palette has none. */
+ /* NOTE: We do not swap IDProperties, as dealing with potential ID pointers in those would be
+ * fairly delicate. */
+ BKE_lib_id_swap(nullptr, id_new, id_old);
+ SWAP(IDProperty *, id_new->properties, id_old->properties);
+}
+
+IDTypeInfo IDType_ID_PAL = {
+ /* id_code */ ID_PAL,
+ /* id_filter */ FILTER_ID_PAL,
+ /* main_listbase_index */ INDEX_ID_PAL,
+ /* struct_size */ sizeof(Palette),
+ /* name */ "Palette",
+ /* name_plural */ "palettes",
+ /* translation_context */ BLT_I18NCONTEXT_ID_PALETTE,
+ /* flags */ IDTYPE_FLAGS_NO_ANIMDATA,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ palette_init_data,
+ /* copy_data */ palette_copy_data,
+ /* free_data */ palette_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ nullptr,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_pointer_get */ nullptr,
+
+ /* blend_write */ palette_blend_write,
+ /* blend_read_data */ palette_blend_read_data,
+ /* blend_read_lib */ nullptr,
+ /* blend_read_expand */ nullptr,
+
+ /* blend_read_undo_preserve */ palette_undo_preserve,
+
+ /* lib_override_apply_post */ nullptr,
+};
+
+static void paint_curve_copy_data(Main *UNUSED(bmain),
+ ID *id_dst,
+ const ID *id_src,
+ const int UNUSED(flag))
+{
+ PaintCurve *paint_curve_dst = (PaintCurve *)id_dst;
+ const PaintCurve *paint_curve_src = (const PaintCurve *)id_src;
+
+ if (paint_curve_src->tot_points != 0) {
+ paint_curve_dst->points = static_cast<PaintCurvePoint *>(
+ MEM_dupallocN(paint_curve_src->points));
+ }
+}
+
+static void paint_curve_free_data(ID *id)
+{
+ PaintCurve *paint_curve = (PaintCurve *)id;
+
+ MEM_SAFE_FREE(paint_curve->points);
+ paint_curve->tot_points = 0;
+}
+
+static void paint_curve_blend_write(BlendWriter *writer, ID *id, const void *id_address)
+{
+ PaintCurve *pc = (PaintCurve *)id;
+
+ BLO_write_id_struct(writer, PaintCurve, id_address, &pc->id);
+ BKE_id_blend_write(writer, &pc->id);
+
+ BLO_write_struct_array(writer, PaintCurvePoint, pc->tot_points, pc->points);
+}
+
+static void paint_curve_blend_read_data(BlendDataReader *reader, ID *id)
+{
+ PaintCurve *pc = (PaintCurve *)id;
+ BLO_read_data_address(reader, &pc->points);
+}
+
+IDTypeInfo IDType_ID_PC = {
+ /* id_code */ ID_PC,
+ /* id_filter */ FILTER_ID_PC,
+ /* main_listbase_index */ INDEX_ID_PC,
+ /* struct_size */ sizeof(PaintCurve),
+ /* name */ "PaintCurve",
+ /* name_plural */ "paint_curves",
+ /* translation_context */ BLT_I18NCONTEXT_ID_PAINTCURVE,
+ /* flags */ IDTYPE_FLAGS_NO_ANIMDATA,
+ /* asset_type_info */ nullptr,
+
+ /* init_data */ nullptr,
+ /* copy_data */ paint_curve_copy_data,
+ /* free_data */ paint_curve_free_data,
+ /* make_local */ nullptr,
+ /* foreach_id */ nullptr,
+ /* foreach_cache */ nullptr,
+ /* foreach_path */ nullptr,
+ /* owner_pointer_get */ nullptr,
+
+ /* blend_write */ paint_curve_blend_write,
+ /* blend_read_data */ paint_curve_blend_read_data,
+ /* blend_read_lib */ nullptr,
+ /* blend_read_expand */ nullptr,
+
+ /* blend_read_undo_preserve */ nullptr,
+
+ /* lib_override_apply_post */ nullptr,
+};
+
+const uchar PAINT_CURSOR_SCULPT[3] = {255, 100, 100};
+const uchar PAINT_CURSOR_VERTEX_PAINT[3] = {255, 255, 255};
+const uchar PAINT_CURSOR_WEIGHT_PAINT[3] = {200, 200, 255};
+const uchar PAINT_CURSOR_TEXTURE_PAINT[3] = {255, 255, 255};
+
+static ePaintOverlayControlFlags overlay_flags = (ePaintOverlayControlFlags)0;
+
+void BKE_paint_invalidate_overlay_tex(Scene *scene, ViewLayer *view_layer, const Tex *tex)
+{
+ Paint *p = BKE_paint_get_active(scene, view_layer);
+ if (!p) {
+ return;
+ }
+
+ Brush *br = p->brush;
+ if (!br) {
+ return;
+ }
+
+ if (br->mtex.tex == tex) {
+ overlay_flags |= PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY;
+ }
+ if (br->mask_mtex.tex == tex) {
+ overlay_flags |= PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY;
+ }
+}
+
+void BKE_paint_invalidate_cursor_overlay(Scene *scene, ViewLayer *view_layer, CurveMapping *curve)
+{
+ Paint *p = BKE_paint_get_active(scene, view_layer);
+ if (p == nullptr) {
+ return;
+ }
+
+ Brush *br = p->brush;
+ if (br && br->curve == curve) {
+ overlay_flags |= PAINT_OVERLAY_INVALID_CURVE;
+ }
+}
+
+void BKE_paint_invalidate_overlay_all(void)
+{
+ overlay_flags |= (PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY |
+ PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY | PAINT_OVERLAY_INVALID_CURVE);
+}
+
+ePaintOverlayControlFlags BKE_paint_get_overlay_flags(void)
+{
+ return overlay_flags;
+}
+
+void BKE_paint_set_overlay_override(eOverlayFlags flags)
+{
+ if (flags & BRUSH_OVERLAY_OVERRIDE_MASK) {
+ if (flags & BRUSH_OVERLAY_CURSOR_OVERRIDE_ON_STROKE) {
+ overlay_flags |= PAINT_OVERLAY_OVERRIDE_CURSOR;
+ }
+ if (flags & BRUSH_OVERLAY_PRIMARY_OVERRIDE_ON_STROKE) {
+ overlay_flags |= PAINT_OVERLAY_OVERRIDE_PRIMARY;
+ }
+ if (flags & BRUSH_OVERLAY_SECONDARY_OVERRIDE_ON_STROKE) {
+ overlay_flags |= PAINT_OVERLAY_OVERRIDE_SECONDARY;
+ }
+ }
+ else {
+ overlay_flags &= ~(PAINT_OVERRIDE_MASK);
+ }
+}
+
+void BKE_paint_reset_overlay_invalid(ePaintOverlayControlFlags flag)
+{
+ overlay_flags &= ~(flag);
+}
+
+bool BKE_paint_ensure_from_paintmode(Scene *sce, ePaintMode mode)
+{
+ ToolSettings *ts = sce->toolsettings;
+ Paint **paint_ptr = nullptr;
+ /* Some paint modes don't store paint settings as pointer, for these this can be set and
+ * referenced by paint_ptr. */
+ Paint *paint_tmp = nullptr;
+
+ switch (mode) {
+ case PAINT_MODE_SCULPT:
+ paint_ptr = (Paint **)&ts->sculpt;
+ break;
+ case PAINT_MODE_VERTEX:
+ paint_ptr = (Paint **)&ts->vpaint;
+ break;
+ case PAINT_MODE_WEIGHT:
+ paint_ptr = (Paint **)&ts->wpaint;
+ break;
+ case PAINT_MODE_TEXTURE_2D:
+ case PAINT_MODE_TEXTURE_3D:
+ paint_tmp = (Paint *)&ts->imapaint;
+ paint_ptr = &paint_tmp;
+ break;
+ case PAINT_MODE_SCULPT_UV:
+ paint_ptr = (Paint **)&ts->uvsculpt;
+ break;
+ case PAINT_MODE_GPENCIL:
+ paint_ptr = (Paint **)&ts->gp_paint;
+ break;
+ case PAINT_MODE_VERTEX_GPENCIL:
+ paint_ptr = (Paint **)&ts->gp_vertexpaint;
+ break;
+ case PAINT_MODE_SCULPT_GPENCIL:
+ paint_ptr = (Paint **)&ts->gp_sculptpaint;
+ break;
+ case PAINT_MODE_WEIGHT_GPENCIL:
+ paint_ptr = (Paint **)&ts->gp_weightpaint;
+ break;
+ case PAINT_MODE_SCULPT_CURVES:
+ paint_ptr = (Paint **)&ts->curves_sculpt;
+ break;
+ case PAINT_MODE_INVALID:
+ break;
+ }
+ if (paint_ptr) {
+ BKE_paint_ensure(ts, paint_ptr);
+ return true;
+ }
+ return false;
+}
+
+Paint *BKE_paint_get_active_from_paintmode(Scene *sce, ePaintMode mode)
+{
+ if (sce) {
+ ToolSettings *ts = sce->toolsettings;
+
+ switch (mode) {
+ case PAINT_MODE_SCULPT:
+ return &ts->sculpt->paint;
+ case PAINT_MODE_VERTEX:
+ return &ts->vpaint->paint;
+ case PAINT_MODE_WEIGHT:
+ return &ts->wpaint->paint;
+ case PAINT_MODE_TEXTURE_2D:
+ case PAINT_MODE_TEXTURE_3D:
+ return &ts->imapaint.paint;
+ case PAINT_MODE_SCULPT_UV:
+ return &ts->uvsculpt->paint;
+ case PAINT_MODE_GPENCIL:
+ return &ts->gp_paint->paint;
+ case PAINT_MODE_VERTEX_GPENCIL:
+ return &ts->gp_vertexpaint->paint;
+ case PAINT_MODE_SCULPT_GPENCIL:
+ return &ts->gp_sculptpaint->paint;
+ case PAINT_MODE_WEIGHT_GPENCIL:
+ return &ts->gp_weightpaint->paint;
+ case PAINT_MODE_SCULPT_CURVES:
+ return &ts->curves_sculpt->paint;
+ case PAINT_MODE_INVALID:
+ return nullptr;
+ default:
+ return &ts->imapaint.paint;
+ }
+ }
+
+ return nullptr;
+}
+
+const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(ePaintMode mode)
+{
+ switch (mode) {
+ case PAINT_MODE_SCULPT:
+ return rna_enum_brush_sculpt_tool_items;
+ case PAINT_MODE_VERTEX:
+ return rna_enum_brush_vertex_tool_items;
+ case PAINT_MODE_WEIGHT:
+ return rna_enum_brush_weight_tool_items;
+ case PAINT_MODE_TEXTURE_2D:
+ case PAINT_MODE_TEXTURE_3D:
+ return rna_enum_brush_image_tool_items;
+ case PAINT_MODE_SCULPT_UV:
+ return rna_enum_brush_uv_sculpt_tool_items;
+ case PAINT_MODE_GPENCIL:
+ return rna_enum_brush_gpencil_types_items;
+ case PAINT_MODE_VERTEX_GPENCIL:
+ return rna_enum_brush_gpencil_vertex_types_items;
+ case PAINT_MODE_SCULPT_GPENCIL:
+ return rna_enum_brush_gpencil_sculpt_types_items;
+ case PAINT_MODE_WEIGHT_GPENCIL:
+ return rna_enum_brush_gpencil_weight_types_items;
+ case PAINT_MODE_SCULPT_CURVES:
+ return rna_enum_brush_curves_sculpt_tool_items;
+ case PAINT_MODE_INVALID:
+ break;
+ }
+ return nullptr;
+}
+
+const char *BKE_paint_get_tool_prop_id_from_paintmode(ePaintMode mode)
+{
+ switch (mode) {
+ case PAINT_MODE_SCULPT:
+ return "sculpt_tool";
+ case PAINT_MODE_VERTEX:
+ return "vertex_tool";
+ case PAINT_MODE_WEIGHT:
+ return "weight_tool";
+ case PAINT_MODE_TEXTURE_2D:
+ case PAINT_MODE_TEXTURE_3D:
+ return "image_tool";
+ case PAINT_MODE_SCULPT_UV:
+ return "uv_sculpt_tool";
+ case PAINT_MODE_GPENCIL:
+ return "gpencil_tool";
+ case PAINT_MODE_VERTEX_GPENCIL:
+ return "gpencil_vertex_tool";
+ case PAINT_MODE_SCULPT_GPENCIL:
+ return "gpencil_sculpt_tool";
+ case PAINT_MODE_WEIGHT_GPENCIL:
+ return "gpencil_weight_tool";
+ case PAINT_MODE_SCULPT_CURVES:
+ return "curves_sculpt_tool";
+ case PAINT_MODE_INVALID:
+ break;
+ }
+
+ /* Invalid paint mode. */
+ return nullptr;
+}
+
+Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer)
+{
+ if (sce && view_layer) {
+ ToolSettings *ts = sce->toolsettings;
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ Object *actob = BKE_view_layer_active_object_get(view_layer);
+
+ if (actob) {
+ switch (actob->mode) {
+ case OB_MODE_SCULPT:
+ return &ts->sculpt->paint;
+ case OB_MODE_VERTEX_PAINT:
+ return &ts->vpaint->paint;
+ case OB_MODE_WEIGHT_PAINT:
+ return &ts->wpaint->paint;
+ case OB_MODE_TEXTURE_PAINT:
+ return &ts->imapaint.paint;
+ case OB_MODE_PAINT_GPENCIL:
+ return &ts->gp_paint->paint;
+ case OB_MODE_VERTEX_GPENCIL:
+ return &ts->gp_vertexpaint->paint;
+ case OB_MODE_SCULPT_GPENCIL:
+ return &ts->gp_sculptpaint->paint;
+ case OB_MODE_WEIGHT_GPENCIL:
+ return &ts->gp_weightpaint->paint;
+ case OB_MODE_SCULPT_CURVES:
+ return &ts->curves_sculpt->paint;
+ case OB_MODE_EDIT:
+ return ts->uvsculpt ? &ts->uvsculpt->paint : nullptr;
+ default:
+ break;
+ }
+ }
+
+ /* default to image paint */
+ return &ts->imapaint.paint;
+ }
+
+ return nullptr;
+}
+
+Paint *BKE_paint_get_active_from_context(const bContext *C)
+{
+ Scene *sce = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ SpaceImage *sima;
+
+ if (sce && view_layer) {
+ ToolSettings *ts = sce->toolsettings;
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
+
+ if ((sima = CTX_wm_space_image(C)) != nullptr) {
+ if (obact && obact->mode == OB_MODE_EDIT) {
+ if (sima->mode == SI_MODE_PAINT) {
+ return &ts->imapaint.paint;
+ }
+ if (sima->mode == SI_MODE_UV) {
+ return &ts->uvsculpt->paint;
+ }
+ }
+ else {
+ return &ts->imapaint.paint;
+ }
+ }
+ else {
+ return BKE_paint_get_active(sce, view_layer);
+ }
+ }
+
+ return nullptr;
+}
+
+ePaintMode BKE_paintmode_get_active_from_context(const bContext *C)
+{
+ Scene *sce = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ SpaceImage *sima;
+
+ if (sce && view_layer) {
+ BKE_view_layer_synced_ensure(sce, view_layer);
+ Object *obact = BKE_view_layer_active_object_get(view_layer);
+
+ if ((sima = CTX_wm_space_image(C)) != nullptr) {
+ if (obact && obact->mode == OB_MODE_EDIT) {
+ if (sima->mode == SI_MODE_PAINT) {
+ return PAINT_MODE_TEXTURE_2D;
+ }
+ if (sima->mode == SI_MODE_UV) {
+ return PAINT_MODE_SCULPT_UV;
+ }
+ }
+ else {
+ return PAINT_MODE_TEXTURE_2D;
+ }
+ }
+ else if (obact) {
+ switch (obact->mode) {
+ case OB_MODE_SCULPT:
+ return PAINT_MODE_SCULPT;
+ case OB_MODE_VERTEX_PAINT:
+ return PAINT_MODE_VERTEX;
+ case OB_MODE_WEIGHT_PAINT:
+ return PAINT_MODE_WEIGHT;
+ case OB_MODE_TEXTURE_PAINT:
+ return PAINT_MODE_TEXTURE_3D;
+ case OB_MODE_EDIT:
+ return PAINT_MODE_SCULPT_UV;
+ case OB_MODE_SCULPT_CURVES:
+ return PAINT_MODE_SCULPT_CURVES;
+ default:
+ return PAINT_MODE_TEXTURE_2D;
+ }
+ }
+ else {
+ /* default to image paint */
+ return PAINT_MODE_TEXTURE_2D;
+ }
+ }
+
+ return PAINT_MODE_INVALID;
+}
+
+ePaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
+{
+ if (tref->space_type == SPACE_VIEW3D) {
+ switch (tref->mode) {
+ case CTX_MODE_SCULPT:
+ return PAINT_MODE_SCULPT;
+ case CTX_MODE_PAINT_VERTEX:
+ return PAINT_MODE_VERTEX;
+ case CTX_MODE_PAINT_WEIGHT:
+ return PAINT_MODE_WEIGHT;
+ case CTX_MODE_PAINT_GPENCIL:
+ return PAINT_MODE_GPENCIL;
+ case CTX_MODE_PAINT_TEXTURE:
+ return PAINT_MODE_TEXTURE_3D;
+ case CTX_MODE_VERTEX_GPENCIL:
+ return PAINT_MODE_VERTEX_GPENCIL;
+ case CTX_MODE_SCULPT_GPENCIL:
+ return PAINT_MODE_SCULPT_GPENCIL;
+ case CTX_MODE_WEIGHT_GPENCIL:
+ return PAINT_MODE_WEIGHT_GPENCIL;
+ case CTX_MODE_SCULPT_CURVES:
+ return PAINT_MODE_SCULPT_CURVES;
+ }
+ }
+ else if (tref->space_type == SPACE_IMAGE) {
+ switch (tref->mode) {
+ case SI_MODE_PAINT:
+ return PAINT_MODE_TEXTURE_2D;
+ case SI_MODE_UV:
+ return PAINT_MODE_SCULPT_UV;
+ }
+ }
+
+ return PAINT_MODE_INVALID;
+}
+
+Brush *BKE_paint_brush(Paint *p)
+{
+ return (Brush *)BKE_paint_brush_for_read((const Paint *)p);
+}
+
+const Brush *BKE_paint_brush_for_read(const Paint *p)
+{
+ return p ? p->brush : nullptr;
+}
+
+void BKE_paint_brush_set(Paint *p, Brush *br)
+{
+ if (p) {
+ id_us_min((ID *)p->brush);
+ id_us_plus((ID *)br);
+ p->brush = br;
+
+ BKE_paint_toolslots_brush_update(p);
+ }
+}
+
+void BKE_paint_runtime_init(const ToolSettings *ts, Paint *paint)
+{
+ if (paint == &ts->imapaint.paint) {
+ paint->runtime.tool_offset = offsetof(Brush, imagepaint_tool);
+ paint->runtime.ob_mode = OB_MODE_TEXTURE_PAINT;
+ }
+ else if (ts->sculpt && paint == &ts->sculpt->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, sculpt_tool);
+ paint->runtime.ob_mode = OB_MODE_SCULPT;
+ }
+ else if (ts->vpaint && paint == &ts->vpaint->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, vertexpaint_tool);
+ paint->runtime.ob_mode = OB_MODE_VERTEX_PAINT;
+ }
+ else if (ts->wpaint && paint == &ts->wpaint->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, weightpaint_tool);
+ paint->runtime.ob_mode = OB_MODE_WEIGHT_PAINT;
+ }
+ else if (ts->uvsculpt && paint == &ts->uvsculpt->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, uv_sculpt_tool);
+ paint->runtime.ob_mode = OB_MODE_EDIT;
+ }
+ else if (ts->gp_paint && paint == &ts->gp_paint->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, gpencil_tool);
+ paint->runtime.ob_mode = OB_MODE_PAINT_GPENCIL;
+ }
+ else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, gpencil_vertex_tool);
+ paint->runtime.ob_mode = OB_MODE_VERTEX_GPENCIL;
+ }
+ else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, gpencil_sculpt_tool);
+ paint->runtime.ob_mode = OB_MODE_SCULPT_GPENCIL;
+ }
+ else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, gpencil_weight_tool);
+ paint->runtime.ob_mode = OB_MODE_WEIGHT_GPENCIL;
+ }
+ else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) {
+ paint->runtime.tool_offset = offsetof(Brush, curves_sculpt_tool);
+ paint->runtime.ob_mode = OB_MODE_SCULPT_CURVES;
+ }
+ else {
+ BLI_assert_unreachable();
+ }
+}
+
+uint BKE_paint_get_brush_tool_offset_from_paintmode(const ePaintMode mode)
+{
+ switch (mode) {
+ case PAINT_MODE_TEXTURE_2D:
+ case PAINT_MODE_TEXTURE_3D:
+ return offsetof(Brush, imagepaint_tool);
+ case PAINT_MODE_SCULPT:
+ return offsetof(Brush, sculpt_tool);
+ case PAINT_MODE_VERTEX:
+ return offsetof(Brush, vertexpaint_tool);
+ case PAINT_MODE_WEIGHT:
+ return offsetof(Brush, weightpaint_tool);
+ case PAINT_MODE_SCULPT_UV:
+ return offsetof(Brush, uv_sculpt_tool);
+ case PAINT_MODE_GPENCIL:
+ return offsetof(Brush, gpencil_tool);
+ case PAINT_MODE_VERTEX_GPENCIL:
+ return offsetof(Brush, gpencil_vertex_tool);
+ case PAINT_MODE_SCULPT_GPENCIL:
+ return offsetof(Brush, gpencil_sculpt_tool);
+ case PAINT_MODE_WEIGHT_GPENCIL:
+ return offsetof(Brush, gpencil_weight_tool);
+ case PAINT_MODE_SCULPT_CURVES:
+ return offsetof(Brush, curves_sculpt_tool);
+ case PAINT_MODE_INVALID:
+ break; /* We don't use these yet. */
+ }
+ return 0;
+}
+
+PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name)
+{
+ PaintCurve *pc = static_cast<PaintCurve *>(BKE_id_new(bmain, ID_PC, name));
+ return pc;
+}
+
+Palette *BKE_paint_palette(Paint *p)
+{
+ return p ? p->palette : nullptr;
+}
+
+void BKE_paint_palette_set(Paint *p, Palette *palette)
+{
+ if (p) {
+ id_us_min((ID *)p->palette);
+ p->palette = palette;
+ id_us_plus((ID *)p->palette);
+ }
+}
+
+void BKE_paint_curve_set(Brush *br, PaintCurve *pc)
+{
+ if (br) {
+ id_us_min((ID *)br->paint_curve);
+ br->paint_curve = pc;
+ id_us_plus((ID *)br->paint_curve);
+ }
+}
+
+void BKE_paint_curve_clamp_endpoint_add_index(PaintCurve *pc, const int add_index)
+{
+ pc->add_index = (add_index || pc->tot_points == 1) ? (add_index + 1) : 0;
+}
+
+void BKE_palette_color_remove(Palette *palette, PaletteColor *color)
+{
+ if (BLI_listbase_count_at_most(&palette->colors, palette->active_color) ==
+ palette->active_color) {
+ palette->active_color--;
+ }
+
+ BLI_remlink(&palette->colors, color);
+
+ if (palette->active_color < 0 && !BLI_listbase_is_empty(&palette->colors)) {
+ palette->active_color = 0;
+ }
+
+ MEM_freeN(color);
+}
+
+void BKE_palette_clear(Palette *palette)
+{
+ BLI_freelistN(&palette->colors);
+ palette->active_color = 0;
+}
+
+Palette *BKE_palette_add(Main *bmain, const char *name)
+{
+ Palette *palette = static_cast<Palette *>(BKE_id_new(bmain, ID_PAL, name));
+ return palette;
+}
+
+PaletteColor *BKE_palette_color_add(Palette *palette)
+{
+ PaletteColor *color = MEM_cnew<PaletteColor>(__func__);
+ BLI_addtail(&palette->colors, color);
+ return color;
+}
+
+bool BKE_palette_is_empty(const Palette *palette)
+{
+ return BLI_listbase_is_empty(&palette->colors);
+}
+
+/* helper function to sort using qsort */
+static int palettecolor_compare_hsv(const void *a1, const void *a2)
+{
+ const tPaletteColorHSV *ps1 = static_cast<const tPaletteColorHSV *>(a1);
+ const tPaletteColorHSV *ps2 = static_cast<const tPaletteColorHSV *>(a2);
+
+ /* Hue */
+ if (ps1->h > ps2->h) {
+ return 1;
+ }
+ if (ps1->h < ps2->h) {
+ return -1;
+ }
+
+ /* Saturation. */
+ if (ps1->s > ps2->s) {
+ return 1;
+ }
+ if (ps1->s < ps2->s) {
+ return -1;
+ }
+
+ /* Value. */
+ if (1.0f - ps1->v > 1.0f - ps2->v) {
+ return 1;
+ }
+ if (1.0f - ps1->v < 1.0f - ps2->v) {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* helper function to sort using qsort */
+static int palettecolor_compare_svh(const void *a1, const void *a2)
+{
+ const tPaletteColorHSV *ps1 = static_cast<const tPaletteColorHSV *>(a1);
+ const tPaletteColorHSV *ps2 = static_cast<const tPaletteColorHSV *>(a2);
+
+ /* Saturation. */
+ if (ps1->s > ps2->s) {
+ return 1;
+ }
+ if (ps1->s < ps2->s) {
+ return -1;
+ }
+
+ /* Value. */
+ if (1.0f - ps1->v > 1.0f - ps2->v) {
+ return 1;
+ }
+ if (1.0f - ps1->v < 1.0f - ps2->v) {
+ return -1;
+ }
+
+ /* Hue */
+ if (ps1->h > ps2->h) {
+ return 1;
+ }
+ if (ps1->h < ps2->h) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int palettecolor_compare_vhs(const void *a1, const void *a2)
+{
+ const tPaletteColorHSV *ps1 = static_cast<const tPaletteColorHSV *>(a1);
+ const tPaletteColorHSV *ps2 = static_cast<const tPaletteColorHSV *>(a2);
+
+ /* Value. */
+ if (1.0f - ps1->v > 1.0f - ps2->v) {
+ return 1;
+ }
+ if (1.0f - ps1->v < 1.0f - ps2->v) {
+ return -1;
+ }
+
+ /* Hue */
+ if (ps1->h > ps2->h) {
+ return 1;
+ }
+ if (ps1->h < ps2->h) {
+ return -1;
+ }
+
+ /* Saturation. */
+ if (ps1->s > ps2->s) {
+ return 1;
+ }
+ if (ps1->s < ps2->s) {
+ return -1;
+ }
+
+ return 0;
+}
+
+static int palettecolor_compare_luminance(const void *a1, const void *a2)
+{
+ const tPaletteColorHSV *ps1 = static_cast<const tPaletteColorHSV *>(a1);
+ const tPaletteColorHSV *ps2 = static_cast<const tPaletteColorHSV *>(a2);
+
+ float lumi1 = (ps1->rgb[0] + ps1->rgb[1] + ps1->rgb[2]) / 3.0f;
+ float lumi2 = (ps2->rgb[0] + ps2->rgb[1] + ps2->rgb[2]) / 3.0f;
+
+ if (lumi1 > lumi2) {
+ return -1;
+ }
+ if (lumi1 < lumi2) {
+ return 1;
+ }
+
+ return 0;
+}
+
+void BKE_palette_sort_hsv(tPaletteColorHSV *color_array, const int totcol)
+{
+ /* Sort by Hue, Saturation and Value. */
+ qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_hsv);
+}
+
+void BKE_palette_sort_svh(tPaletteColorHSV *color_array, const int totcol)
+{
+ /* Sort by Saturation, Value and Hue. */
+ qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_svh);
+}
+
+void BKE_palette_sort_vhs(tPaletteColorHSV *color_array, const int totcol)
+{
+ /* Sort by Saturation, Value and Hue. */
+ qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_vhs);
+}
+
+void BKE_palette_sort_luminance(tPaletteColorHSV *color_array, const int totcol)
+{
+ /* Sort by Luminance (calculated with the average, enough for sorting). */
+ qsort(color_array, totcol, sizeof(tPaletteColorHSV), palettecolor_compare_luminance);
+}
+
+bool BKE_palette_from_hash(Main *bmain, GHash *color_table, const char *name, const bool linear)
+{
+ tPaletteColorHSV *color_array = nullptr;
+ tPaletteColorHSV *col_elm = nullptr;
+ bool done = false;
+
+ const int totpal = BLI_ghash_len(color_table);
+
+ if (totpal > 0) {
+ color_array = static_cast<tPaletteColorHSV *>(
+ MEM_calloc_arrayN(totpal, sizeof(tPaletteColorHSV), __func__));
+ /* Put all colors in an array. */
+ GHashIterator gh_iter;
+ int t = 0;
+ GHASH_ITER (gh_iter, color_table) {
+ const uint col = POINTER_AS_INT(BLI_ghashIterator_getValue(&gh_iter));
+ float r, g, b;
+ float h, s, v;
+ cpack_to_rgb(col, &r, &g, &b);
+ rgb_to_hsv(r, g, b, &h, &s, &v);
+
+ col_elm = &color_array[t];
+ col_elm->rgb[0] = r;
+ col_elm->rgb[1] = g;
+ col_elm->rgb[2] = b;
+ col_elm->h = h;
+ col_elm->s = s;
+ col_elm->v = v;
+ t++;
+ }
+ }
+
+ /* Create the Palette. */
+ if (totpal > 0) {
+ /* Sort by Hue and saturation. */
+ BKE_palette_sort_hsv(color_array, totpal);
+
+ Palette *palette = BKE_palette_add(bmain, name);
+ if (palette) {
+ for (int i = 0; i < totpal; i++) {
+ col_elm = &color_array[i];
+ PaletteColor *palcol = BKE_palette_color_add(palette);
+ if (palcol) {
+ copy_v3_v3(palcol->rgb, col_elm->rgb);
+ if (linear) {
+ linearrgb_to_srgb_v3_v3(palcol->rgb, palcol->rgb);
+ }
+ }
+ }
+ done = true;
+ }
+ }
+ else {
+ done = false;
+ }
+
+ if (totpal > 0) {
+ MEM_SAFE_FREE(color_array);
+ }
+
+ return done;
+}
+
+bool BKE_paint_select_face_test(Object *ob)
+{
+ return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) &&
+ (((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_FACE_SEL) &&
+ (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)));
+}
+
+bool BKE_paint_select_vert_test(Object *ob)
+{
+ return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) &&
+ (((Mesh *)ob->data)->editflag & ME_EDIT_PAINT_VERT_SEL) &&
+ (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT));
+}
+
+bool BKE_paint_select_elem_test(Object *ob)
+{
+ return (BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob));
+}
+
+bool BKE_paint_always_hide_test(Object *ob)
+{
+ return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) &&
+ (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT));
+}
+
+void BKE_paint_cavity_curve_preset(Paint *p, int preset)
+{
+ CurveMapping *cumap = nullptr;
+ CurveMap *cuma = nullptr;
+
+ if (!p->cavity_curve) {
+ p->cavity_curve = BKE_curvemapping_add(1, 0, 0, 1, 1);
+ }
+ cumap = p->cavity_curve;
+ cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE;
+ cumap->preset = preset;
+
+ cuma = cumap->cm;
+ BKE_curvemap_reset(cuma, &cumap->clipr, cumap->preset, CURVEMAP_SLOPE_POSITIVE);
+ BKE_curvemapping_changed(cumap, false);
+}
+
+eObjectMode BKE_paint_object_mode_from_paintmode(ePaintMode mode)
+{
+ switch (mode) {
+ case PAINT_MODE_SCULPT:
+ return OB_MODE_SCULPT;
+ case PAINT_MODE_VERTEX:
+ return OB_MODE_VERTEX_PAINT;
+ case PAINT_MODE_WEIGHT:
+ return OB_MODE_WEIGHT_PAINT;
+ case PAINT_MODE_TEXTURE_2D:
+ case PAINT_MODE_TEXTURE_3D:
+ return OB_MODE_TEXTURE_PAINT;
+ case PAINT_MODE_SCULPT_UV:
+ return OB_MODE_EDIT;
+ case PAINT_MODE_INVALID:
+ default:
+ return OB_MODE_OBJECT;
+ }
+}
+
+bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint)
+{
+ Paint *paint = nullptr;
+ if (*r_paint) {
+ /* Tool offset should never be 0 for initialized paint settings, so it's a reliable way to
+ * check if already initialized. */
+ if ((*r_paint)->runtime.tool_offset == 0) {
+ /* Currently only image painting is initialized this way, others have to be allocated. */
+ BLI_assert(ELEM(*r_paint, (Paint *)&ts->imapaint));
+
+ BKE_paint_runtime_init(ts, *r_paint);
+ }
+ else {
+ BLI_assert(ELEM(*r_paint,
+ /* Cast is annoying, but prevent nullptr-pointer access. */
+ (Paint *)ts->gp_paint,
+ (Paint *)ts->gp_vertexpaint,
+ (Paint *)ts->gp_sculptpaint,
+ (Paint *)ts->gp_weightpaint,
+ (Paint *)ts->sculpt,
+ (Paint *)ts->vpaint,
+ (Paint *)ts->wpaint,
+ (Paint *)ts->uvsculpt,
+ (Paint *)ts->curves_sculpt,
+ (Paint *)&ts->imapaint));
+#ifdef DEBUG
+ Paint paint_test = **r_paint;
+ BKE_paint_runtime_init(ts, *r_paint);
+ /* Swap so debug doesn't hide errors when release fails. */
+ SWAP(Paint, **r_paint, paint_test);
+ BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode);
+ BLI_assert(paint_test.runtime.tool_offset == (*r_paint)->runtime.tool_offset);
+#endif
+ }
+ return true;
+ }
+
+ if (((VPaint **)r_paint == &ts->vpaint) || ((VPaint **)r_paint == &ts->wpaint)) {
+ VPaint *data = MEM_cnew<VPaint>(__func__);
+ paint = &data->paint;
+ }
+ else if ((Sculpt **)r_paint == &ts->sculpt) {
+ Sculpt *data = MEM_cnew<Sculpt>(__func__);
+ paint = &data->paint;
+
+ /* Turn on X plane mirror symmetry by default. */
+ paint->symmetry_flags |= PAINT_SYMM_X;
+
+ /* Make sure at least dyntopo subdivision is enabled. */
+ data->flags |= SCULPT_DYNTOPO_SUBDIVIDE | SCULPT_DYNTOPO_COLLAPSE;
+ }
+ else if ((GpPaint **)r_paint == &ts->gp_paint) {
+ GpPaint *data = MEM_cnew<GpPaint>(__func__);
+ paint = &data->paint;
+ }
+ else if ((GpVertexPaint **)r_paint == &ts->gp_vertexpaint) {
+ GpVertexPaint *data = MEM_cnew<GpVertexPaint>(__func__);
+ paint = &data->paint;
+ }
+ else if ((GpSculptPaint **)r_paint == &ts->gp_sculptpaint) {
+ GpSculptPaint *data = MEM_cnew<GpSculptPaint>(__func__);
+ paint = &data->paint;
+ }
+ else if ((GpWeightPaint **)r_paint == &ts->gp_weightpaint) {
+ GpWeightPaint *data = MEM_cnew<GpWeightPaint>(__func__);
+ paint = &data->paint;
+ }
+ else if ((UvSculpt **)r_paint == &ts->uvsculpt) {
+ UvSculpt *data = MEM_cnew<UvSculpt>(__func__);
+ paint = &data->paint;
+ }
+ else if ((CurvesSculpt **)r_paint == &ts->curves_sculpt) {
+ CurvesSculpt *data = MEM_cnew<CurvesSculpt>(__func__);
+ paint = &data->paint;
+ }
+ else if (*r_paint == &ts->imapaint.paint) {
+ paint = &ts->imapaint.paint;
+ }
+
+ paint->flags |= PAINT_SHOW_BRUSH;
+
+ *r_paint = paint;
+
+ BKE_paint_runtime_init(ts, paint);
+
+ return false;
+}
+
+void BKE_paint_init(Main *bmain, Scene *sce, ePaintMode mode, const uchar col[3])
+{
+ UnifiedPaintSettings *ups = &sce->toolsettings->unified_paint_settings;
+ Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode);
+
+ BKE_paint_ensure_from_paintmode(sce, mode);
+
+ /* If there's no brush, create one */
+ if (PAINT_MODE_HAS_BRUSH(mode)) {
+ Brush *brush = BKE_paint_brush(paint);
+ if (brush == nullptr) {
+ eObjectMode ob_mode = BKE_paint_object_mode_from_paintmode(mode);
+ brush = BKE_brush_first_search(bmain, ob_mode);
+ if (!brush) {
+ brush = BKE_brush_add(bmain, "Brush", ob_mode);
+ id_us_min(&brush->id); /* Fake user only. */
+ }
+ BKE_paint_brush_set(paint, brush);
+ }
+ }
+
+ copy_v3_v3_uchar(paint->paint_cursor_col, col);
+ paint->paint_cursor_col[3] = 128;
+ ups->last_stroke_valid = false;
+ zero_v3(ups->average_stroke_accum);
+ ups->average_stroke_counter = 0;
+ if (!paint->cavity_curve) {
+ BKE_paint_cavity_curve_preset(paint, CURVE_PRESET_LINE);
+ }
+}
+
+void BKE_paint_free(Paint *paint)
+{
+ BKE_curvemapping_free(paint->cavity_curve);
+ MEM_SAFE_FREE(paint->tool_slots);
+}
+
+void BKE_paint_copy(Paint *src, Paint *tar, const int flag)
+{
+ tar->brush = src->brush;
+ tar->cavity_curve = BKE_curvemapping_copy(src->cavity_curve);
+ tar->tool_slots = static_cast<PaintToolSlot *>(MEM_dupallocN(src->tool_slots));
+
+ if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
+ id_us_plus((ID *)tar->brush);
+ id_us_plus((ID *)tar->palette);
+ if (src->tool_slots != nullptr) {
+ for (int i = 0; i < tar->tool_slots_len; i++) {
+ id_us_plus((ID *)tar->tool_slots[i].brush);
+ }
+ }
+ }
+}
+
+void BKE_paint_stroke_get_average(Scene *scene, Object *ob, float stroke[3])
+{
+ UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
+ if (ups->last_stroke_valid && ups->average_stroke_counter > 0) {
+ float fac = 1.0f / ups->average_stroke_counter;
+ mul_v3_v3fl(stroke, ups->average_stroke_accum, fac);
+ }
+ else {
+ copy_v3_v3(stroke, ob->obmat[3]);
+ }
+}
+
+void BKE_paint_blend_write(BlendWriter *writer, Paint *p)
+{
+ if (p->cavity_curve) {
+ BKE_curvemapping_blend_write(writer, p->cavity_curve);
+ }
+ BLO_write_struct_array(writer, PaintToolSlot, p->tool_slots_len, p->tool_slots);
+}
+
+void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *p)
+{
+ if (p->num_input_samples < 1) {
+ p->num_input_samples = 1;
+ }
+
+ BLO_read_data_address(reader, &p->cavity_curve);
+ if (p->cavity_curve) {
+ BKE_curvemapping_blend_read(reader, p->cavity_curve);
+ }
+ else {
+ BKE_paint_cavity_curve_preset(p, CURVE_PRESET_LINE);
+ }
+
+ BLO_read_data_address(reader, &p->tool_slots);
+
+ /* Workaround for invalid data written in older versions. */
+ const size_t expected_size = sizeof(PaintToolSlot) * p->tool_slots_len;
+ if (p->tool_slots && MEM_allocN_len(p->tool_slots) < expected_size) {
+ MEM_freeN(p->tool_slots);
+ p->tool_slots = static_cast<PaintToolSlot *>(MEM_callocN(expected_size, "PaintToolSlot"));
+ }
+
+ BKE_paint_runtime_init(scene->toolsettings, p);
+}
+
+void BKE_paint_blend_read_lib(BlendLibReader *reader, Scene *sce, Paint *p)
+{
+ if (p) {
+ BLO_read_id_address(reader, sce->id.lib, &p->brush);
+ for (int i = 0; i < p->tool_slots_len; i++) {
+ if (p->tool_slots[i].brush != nullptr) {
+ BLO_read_id_address(reader, sce->id.lib, &p->tool_slots[i].brush);
+ }
+ }
+ BLO_read_id_address(reader, sce->id.lib, &p->palette);
+ p->paint_cursor = nullptr;
+
+ BKE_paint_runtime_init(sce->toolsettings, p);
+ }
+}
+
+bool paint_is_face_hidden(const MLoopTri *lt, const bool *hide_poly)
+{
+ if (!hide_poly) {
+ return false;
+ }
+ return hide_poly[lt->poly];
+}
+
+bool paint_is_grid_face_hidden(const uint *grid_hidden, int gridsize, int x, int y)
+{
+ /* Skip face if any of its corners are hidden. */
+ return (BLI_BITMAP_TEST(grid_hidden, y * gridsize + x) ||
+ BLI_BITMAP_TEST(grid_hidden, y * gridsize + x + 1) ||
+ BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x + 1) ||
+ BLI_BITMAP_TEST(grid_hidden, (y + 1) * gridsize + x));
+}
+
+bool paint_is_bmesh_face_hidden(BMFace *f)
+{
+ BMLoop *l_iter;
+ BMLoop *l_first;
+
+ l_iter = l_first = BM_FACE_FIRST_LOOP(f);
+ do {
+ if (BM_elem_flag_test(l_iter->v, BM_ELEM_HIDDEN)) {
+ return true;
+ }
+ } while ((l_iter = l_iter->next) != l_first);
+
+ return false;
+}
+
+float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y)
+{
+ int factor = BKE_ccg_factor(level, gpm->level);
+ int gridsize = BKE_ccg_gridsize(gpm->level);
+
+ return gpm->data[(y * factor) * gridsize + (x * factor)];
+}
+
+/* Threshold to move before updating the brush rotation. */
+#define RAKE_THRESHHOLD 20
+
+void paint_update_brush_rake_rotation(UnifiedPaintSettings *ups, Brush *brush, float rotation)
+{
+ if (brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) {
+ ups->brush_rotation = rotation;
+ }
+ else {
+ ups->brush_rotation = 0.0f;
+ }
+
+ if (brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE) {
+ ups->brush_rotation_sec = rotation;
+ }
+ else {
+ ups->brush_rotation_sec = 0.0f;
+ }
+}
+
+bool paint_calculate_rake_rotation(UnifiedPaintSettings *ups,
+ Brush *brush,
+ const float mouse_pos[2])
+{
+ bool ok = false;
+ if ((brush->mtex.brush_angle_mode & MTEX_ANGLE_RAKE) ||
+ (brush->mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE)) {
+ const float r = RAKE_THRESHHOLD;
+ float rotation;
+
+ float dpos[2];
+ sub_v2_v2v2(dpos, ups->last_rake, mouse_pos);
+
+ if (len_squared_v2(dpos) >= r * r) {
+ rotation = atan2f(dpos[0], dpos[1]);
+
+ copy_v2_v2(ups->last_rake, mouse_pos);
+
+ ups->last_rake_angle = rotation;
+
+ paint_update_brush_rake_rotation(ups, brush, rotation);
+ ok = true;
+ }
+ /* Make sure we reset here to the last rotation to avoid accumulating
+ * values in case a random rotation is also added. */
+ else {
+ paint_update_brush_rake_rotation(ups, brush, ups->last_rake_angle);
+ ok = false;
+ }
+ }
+ else {
+ ups->brush_rotation = ups->brush_rotation_sec = 0.0f;
+ ok = true;
+ }
+ return ok;
+}
+
+void BKE_sculptsession_free_deformMats(SculptSession *ss)
+{
+ MEM_SAFE_FREE(ss->orig_cos);
+ MEM_SAFE_FREE(ss->deform_cos);
+ MEM_SAFE_FREE(ss->deform_imats);
+}
+
+void BKE_sculptsession_free_vwpaint_data(SculptSession *ss)
+{
+ SculptVertexPaintGeomMap *gmap = nullptr;
+ if (ss->mode_type == OB_MODE_VERTEX_PAINT) {
+ gmap = &ss->mode.vpaint.gmap;
+ }
+ else if (ss->mode_type == OB_MODE_WEIGHT_PAINT) {
+ gmap = &ss->mode.wpaint.gmap;
+
+ MEM_SAFE_FREE(ss->mode.wpaint.alpha_weight);
+ if (ss->mode.wpaint.dvert_prev) {
+ BKE_defvert_array_free_elems(ss->mode.wpaint.dvert_prev, ss->totvert);
+ MEM_freeN(ss->mode.wpaint.dvert_prev);
+ ss->mode.wpaint.dvert_prev = nullptr;
+ }
+ }
+ else {
+ return;
+ }
+ MEM_SAFE_FREE(gmap->vert_to_loop);
+ MEM_SAFE_FREE(gmap->vert_map_mem);
+ MEM_SAFE_FREE(gmap->vert_to_poly);
+ MEM_SAFE_FREE(gmap->poly_map_mem);
+}
+
+/**
+ * Write out the sculpt dynamic-topology #BMesh to the #Mesh.
+ */
+static void sculptsession_bm_to_me_update_data_only(Object *ob, bool reorder)
+{
+ SculptSession *ss = ob->sculpt;
+
+ if (ss->bm) {
+ if (ob->data) {
+ BMIter iter;
+ BMFace *efa;
+ BM_ITER_MESH (efa, &iter, ss->bm, BM_FACES_OF_MESH) {
+ BM_elem_flag_set(efa, BM_ELEM_SMOOTH, ss->bm_smooth_shading);
+ }
+ if (reorder) {
+ BM_log_mesh_elems_reorder(ss->bm, ss->bm_log);
+ }
+ BMeshToMeshParams params{};
+ params.calc_object_remap = false;
+ BM_mesh_bm_to_me(nullptr, ss->bm, static_cast<Mesh *>(ob->data), &params);
+ }
+ }
+}
+
+void BKE_sculptsession_bm_to_me(Object *ob, bool reorder)
+{
+ if (ob && ob->sculpt) {
+ sculptsession_bm_to_me_update_data_only(ob, reorder);
+
+ /* Ensure the objects evaluated mesh doesn't hold onto arrays
+ * now realloc'd in the mesh T34473. */
+ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+ }
+}
+
+static void sculptsession_free_pbvh(Object *object)
+{
+ SculptSession *ss = object->sculpt;
+
+ if (!ss) {
+ return;
+ }
+
+ if (ss->pbvh) {
+ BKE_pbvh_free(ss->pbvh);
+ ss->pbvh = nullptr;
+ }
+
+ MEM_SAFE_FREE(ss->pmap);
+ MEM_SAFE_FREE(ss->pmap_mem);
+
+ MEM_SAFE_FREE(ss->epmap);
+ MEM_SAFE_FREE(ss->epmap_mem);
+
+ MEM_SAFE_FREE(ss->vemap);
+ MEM_SAFE_FREE(ss->vemap_mem);
+
+ MEM_SAFE_FREE(ss->persistent_base);
+
+ MEM_SAFE_FREE(ss->preview_vert_list);
+ ss->preview_vert_count = 0;
+
+ MEM_SAFE_FREE(ss->preview_vert_list);
+
+ MEM_SAFE_FREE(ss->vertex_info.connected_component);
+ MEM_SAFE_FREE(ss->vertex_info.boundary);
+
+ MEM_SAFE_FREE(ss->fake_neighbors.fake_neighbor_index);
+}
+
+void BKE_sculptsession_bm_to_me_for_render(Object *object)
+{
+ if (object && object->sculpt) {
+ if (object->sculpt->bm) {
+ /* Ensure no points to old arrays are stored in DM
+ *
+ * Apparently, we could not use DEG_id_tag_update
+ * here because this will lead to the while object
+ * surface to disappear, so we'll release DM in place.
+ */
+ BKE_object_free_derived_caches(object);
+
+ sculptsession_bm_to_me_update_data_only(object, false);
+
+ /* In contrast with sculptsession_bm_to_me no need in
+ * DAG tag update here - derived mesh was freed and
+ * old pointers are nowhere stored.
+ */
+ }
+ }
+}
+
+void BKE_sculptsession_free(Object *ob)
+{
+ if (ob && ob->sculpt) {
+ SculptSession *ss = ob->sculpt;
+
+ if (ss->bm) {
+ BKE_sculptsession_bm_to_me(ob, true);
+ BM_mesh_free(ss->bm);
+ }
+
+ sculptsession_free_pbvh(ob);
+
+ MEM_SAFE_FREE(ss->pmap);
+ MEM_SAFE_FREE(ss->pmap_mem);
+
+ MEM_SAFE_FREE(ss->epmap);
+ MEM_SAFE_FREE(ss->epmap_mem);
+
+ MEM_SAFE_FREE(ss->vemap);
+ MEM_SAFE_FREE(ss->vemap_mem);
+
+ if (ss->bm_log) {
+ BM_log_free(ss->bm_log);
+ }
+
+ if (ss->tex_pool) {
+ BKE_image_pool_free(ss->tex_pool);
+ }
+
+ MEM_SAFE_FREE(ss->orig_cos);
+ MEM_SAFE_FREE(ss->deform_cos);
+ MEM_SAFE_FREE(ss->deform_imats);
+
+ if (ss->pose_ik_chain_preview) {
+ for (int i = 0; i < ss->pose_ik_chain_preview->tot_segments; i++) {
+ MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments[i].weights);
+ }
+ MEM_SAFE_FREE(ss->pose_ik_chain_preview->segments);
+ MEM_SAFE_FREE(ss->pose_ik_chain_preview);
+ }
+
+ if (ss->boundary_preview) {
+ MEM_SAFE_FREE(ss->boundary_preview->verts);
+ MEM_SAFE_FREE(ss->boundary_preview->edges);
+ MEM_SAFE_FREE(ss->boundary_preview->distance);
+ MEM_SAFE_FREE(ss->boundary_preview->edit_info);
+ MEM_SAFE_FREE(ss->boundary_preview);
+ }
+
+ BKE_sculptsession_free_vwpaint_data(ob->sculpt);
+
+ MEM_SAFE_FREE(ss->last_paint_canvas_key);
+
+ MEM_freeN(ss);
+
+ ob->sculpt = nullptr;
+ }
+}
+
+static MultiresModifierData *sculpt_multires_modifier_get(const Scene *scene,
+ Object *ob,
+ const bool auto_create_mdisps)
+{
+ Mesh *me = (Mesh *)ob->data;
+ ModifierData *md;
+ VirtualModifierData virtualModifierData;
+
+ if (ob->sculpt && ob->sculpt->bm) {
+ /* Can't combine multires and dynamic topology. */
+ return nullptr;
+ }
+
+ bool need_mdisps = false;
+
+ if (!CustomData_get_layer(&me->ldata, CD_MDISPS)) {
+ if (!auto_create_mdisps) {
+ /* Multires can't work without displacement layer. */
+ return nullptr;
+ }
+ else {
+ need_mdisps = true;
+ }
+ }
+
+ /* Weight paint operates on original vertices, and needs to treat multires as regular modifier
+ * to make it so that PBVH vertices are at the multires surface. */
+ if ((ob->mode & OB_MODE_SCULPT) == 0) {
+ return nullptr;
+ }
+
+ for (md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData); md; md = md->next) {
+ if (md->type == eModifierType_Multires) {
+ MultiresModifierData *mmd = (MultiresModifierData *)md;
+
+ if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) {
+ continue;
+ }
+
+ if (mmd->sculptlvl > 0 && !(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) {
+ if (need_mdisps) {
+ CustomData_add_layer(&me->ldata, CD_MDISPS, CD_SET_DEFAULT, nullptr, me->totloop);
+ }
+
+ return mmd;
+ }
+
+ return nullptr;
+ }
+ }
+
+ return nullptr;
+}
+
+MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob)
+{
+ return sculpt_multires_modifier_get(scene, ob, false);
+}
+
+/* Checks if there are any supported deformation modifiers active */
+static bool sculpt_modifiers_active(Scene *scene, Sculpt *sd, Object *ob)
+{
+ ModifierData *md;
+ Mesh *me = (Mesh *)ob->data;
+ VirtualModifierData virtualModifierData;
+
+ if (ob->sculpt->bm || BKE_sculpt_multires_active(scene, ob)) {
+ return false;
+ }
+
+ /* Non-locked shape keys could be handled in the same way as deformed mesh. */
+ if ((ob->shapeflag & OB_SHAPE_LOCK) == 0 && me->key && ob->shapenr) {
+ return true;
+ }
+
+ md = BKE_modifiers_get_virtual_modifierlist(ob, &virtualModifierData);
+
+ /* Exception for shape keys because we can edit those. */
+ for (; md; md = md->next) {
+ const ModifierTypeInfo *mti = BKE_modifier_get_info(static_cast<ModifierType>(md->type));
+ if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) {
+ continue;
+ }
+ if (md->type == eModifierType_Multires && (ob->mode & OB_MODE_SCULPT)) {
+ MultiresModifierData *mmd = (MultiresModifierData *)md;
+ if (!(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) {
+ continue;
+ }
+ }
+ if (md->type == eModifierType_ShapeKey) {
+ continue;
+ }
+
+ if (mti->type == eModifierTypeType_OnlyDeform) {
+ return true;
+ }
+ if ((sd->flags & SCULPT_ONLY_DEFORM) == 0) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static void sculpt_update_object(
+ Depsgraph *depsgraph, Object *ob, Object *ob_eval, bool need_pmap, bool is_paint_tool)
+{
+ Scene *scene = DEG_get_input_scene(depsgraph);
+ Sculpt *sd = scene->toolsettings->sculpt;
+ SculptSession *ss = ob->sculpt;
+ Mesh *me = BKE_object_get_original_mesh(ob);
+ Mesh *me_eval = BKE_object_get_evaluated_mesh(ob_eval);
+ MultiresModifierData *mmd = sculpt_multires_modifier_get(scene, ob, true);
+ const bool use_face_sets = (ob->mode & OB_MODE_SCULPT) != 0;
+
+ BLI_assert(me_eval != nullptr);
+
+ ss->depsgraph = depsgraph;
+
+ ss->deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob);
+ ss->show_mask = (sd->flags & SCULPT_HIDE_MASK) == 0;
+ ss->show_face_sets = (sd->flags & SCULPT_HIDE_FACE_SETS) == 0;
+
+ ss->building_vp_handle = false;
+
+ ss->scene = scene;
+
+ ss->shapekey_active = (mmd == nullptr) ? BKE_keyblock_from_object(ob) : nullptr;
+
+ /* NOTE: Weight pPaint require mesh info for loop lookup, but it never uses multires code path,
+ * so no extra checks is needed here. */
+ if (mmd) {
+ ss->multires.active = true;
+ ss->multires.modifier = mmd;
+ ss->multires.level = mmd->sculptlvl;
+ ss->totvert = me_eval->totvert;
+ ss->totpoly = me_eval->totpoly;
+ ss->totfaces = me->totpoly;
+
+ /* These are assigned to the base mesh in Multires. This is needed because Face Sets operators
+ * and tools use the Face Sets data from the base mesh when Multires is active. */
+ ss->mvert = BKE_mesh_verts_for_write(me);
+ ss->mpoly = BKE_mesh_polys(me);
+ ss->mloop = BKE_mesh_loops(me);
+ }
+ else {
+ ss->totvert = me->totvert;
+ ss->totpoly = me->totpoly;
+ ss->totfaces = me->totpoly;
+ ss->mvert = BKE_mesh_verts_for_write(me);
+ ss->mpoly = BKE_mesh_polys(me);
+ ss->mloop = BKE_mesh_loops(me);
+ ss->multires.active = false;
+ ss->multires.modifier = nullptr;
+ ss->multires.level = 0;
+ ss->vmask = static_cast<float *>(CustomData_get_layer(&me->vdata, CD_PAINT_MASK));
+
+ CustomDataLayer *layer;
+ eAttrDomain domain;
+
+ if (BKE_pbvh_get_color_layer(me, &layer, &domain)) {
+ if (layer->type == CD_PROP_COLOR) {
+ ss->vcol = static_cast<MPropCol *>(layer->data);
+ }
+ else {
+ ss->mcol = static_cast<MLoopCol *>(layer->data);
+ }
+
+ ss->vcol_domain = domain;
+ ss->vcol_type = static_cast<eCustomDataType>(layer->type);
+ }
+ else {
+ ss->vcol = nullptr;
+ ss->mcol = nullptr;
+
+ ss->vcol_type = (eCustomDataType)-1;
+ ss->vcol_domain = ATTR_DOMAIN_POINT;
+ }
+ }
+
+ /* Sculpt Face Sets. */
+ if (use_face_sets) {
+ ss->face_sets = static_cast<int *>(CustomData_get_layer(&me->pdata, CD_SCULPT_FACE_SETS));
+ }
+ else {
+ ss->face_sets = nullptr;
+ }
+
+ ss->hide_poly = (bool *)CustomData_get_layer_named(&me->pdata, CD_PROP_BOOL, ".hide_poly");
+
+ ss->subdiv_ccg = me_eval->runtime.subdiv_ccg;
+
+ PBVH *pbvh = BKE_sculpt_object_pbvh_ensure(depsgraph, ob);
+ BLI_assert(pbvh == ss->pbvh);
+ UNUSED_VARS_NDEBUG(pbvh);
+
+ BKE_pbvh_subdiv_cgg_set(ss->pbvh, ss->subdiv_ccg);
+ BKE_pbvh_face_sets_set(ss->pbvh, ss->face_sets);
+ BKE_pbvh_update_hide_attributes_from_mesh(ss->pbvh);
+
+ BKE_pbvh_face_sets_color_set(ss->pbvh, me->face_sets_color_seed, me->face_sets_color_default);
+
+ if (need_pmap && ob->type == OB_MESH && !ss->pmap) {
+ BKE_mesh_vert_poly_map_create(&ss->pmap,
+ &ss->pmap_mem,
+ BKE_mesh_polys(me),
+ BKE_mesh_loops(me),
+ me->totvert,
+ me->totpoly,
+ me->totloop);
+
+ if (ss->pbvh) {
+ BKE_pbvh_pmap_set(ss->pbvh, ss->pmap);
+ }
+ }
+
+ pbvh_show_mask_set(ss->pbvh, ss->show_mask);
+ pbvh_show_face_sets_set(ss->pbvh, ss->show_face_sets);
+
+ if (ss->deform_modifiers_active) {
+ /* Painting doesn't need crazyspace, use already evaluated mesh coordinates if possible. */
+ bool used_me_eval = false;
+
+ if (ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT)) {
+ Mesh *me_eval_deform = ob_eval->runtime.mesh_deform_eval;
+
+ /* If the fully evaluated mesh has the same topology as the deform-only version, use it.
+ * This matters because crazyspace evaluation is very restrictive and excludes even modifiers
+ * that simply recompute vertex weights (which can even include Geometry Nodes). */
+ if (me_eval_deform->polys().data() == me_eval->polys().data() &&
+ me_eval_deform->loops().data() == me_eval->loops().data() &&
+ me_eval_deform->totvert == me_eval->totvert) {
+ BKE_sculptsession_free_deformMats(ss);
+
+ BLI_assert(me_eval_deform->totvert == me->totvert);
+
+ ss->deform_cos = BKE_mesh_vert_coords_alloc(me_eval, NULL);
+ BKE_pbvh_vert_coords_apply(ss->pbvh, ss->deform_cos, me->totvert);
+
+ used_me_eval = true;
+ }
+ }
+
+ if (!ss->orig_cos && !used_me_eval) {
+ int a;
+
+ BKE_sculptsession_free_deformMats(ss);
+
+ ss->orig_cos = (ss->shapekey_active) ?
+ BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active) :
+ BKE_mesh_vert_coords_alloc(me, nullptr);
+
+ BKE_crazyspace_build_sculpt(depsgraph, scene, ob, &ss->deform_imats, &ss->deform_cos);
+ BKE_pbvh_vert_coords_apply(ss->pbvh, ss->deform_cos, me->totvert);
+
+ for (a = 0; a < me->totvert; a++) {
+ invert_m3(ss->deform_imats[a]);
+ }
+ }
+ }
+ else {
+ BKE_sculptsession_free_deformMats(ss);
+ }
+
+ if (ss->shapekey_active != nullptr && ss->deform_cos == nullptr) {
+ ss->deform_cos = BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active);
+ }
+
+ /* if pbvh is deformed, key block is already applied to it */
+ if (ss->shapekey_active) {
+ bool pbvh_deformed = BKE_pbvh_is_deformed(ss->pbvh);
+ if (!pbvh_deformed || ss->deform_cos == nullptr) {
+ float(*vertCos)[3] = BKE_keyblock_convert_to_vertcos(ob, ss->shapekey_active);
+
+ if (vertCos) {
+ if (!pbvh_deformed) {
+ /* apply shape keys coordinates to PBVH */
+ BKE_pbvh_vert_coords_apply(ss->pbvh, vertCos, me->totvert);
+ }
+ if (ss->deform_cos == nullptr) {
+ ss->deform_cos = vertCos;
+ }
+ if (vertCos != ss->deform_cos) {
+ MEM_freeN(vertCos);
+ }
+ }
+ }
+ }
+
+ if (is_paint_tool) {
+ /*
+ * We should rebuild the PBVH_pixels when painting canvas changes.
+ *
+ * The relevant changes are stored/encoded in the paint canvas key.
+ * These include the active uv map, and resolutions.
+ */
+ if (U.experimental.use_sculpt_texture_paint && ss->pbvh) {
+ char *paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob);
+ if (ss->last_paint_canvas_key == nullptr ||
+ !STREQ(paint_canvas_key, ss->last_paint_canvas_key)) {
+ MEM_SAFE_FREE(ss->last_paint_canvas_key);
+ ss->last_paint_canvas_key = paint_canvas_key;
+ BKE_pbvh_mark_rebuild_pixels(ss->pbvh);
+ }
+ else {
+ MEM_freeN(paint_canvas_key);
+ }
+ }
+
+ /* We could be more precise when we have access to the active tool. */
+ const bool use_paint_slots = (ob->mode & OB_MODE_SCULPT) != 0;
+ if (use_paint_slots) {
+ BKE_texpaint_slots_refresh_object(scene, ob);
+ }
+ }
+}
+
+void BKE_sculpt_update_object_before_eval(Object *ob_eval)
+{
+ /* Update before mesh evaluation in the dependency graph. */
+ SculptSession *ss = ob_eval->sculpt;
+
+ if (ss && ss->building_vp_handle == false) {
+ if (!ss->cache && !ss->filter_cache && !ss->expand_cache) {
+ /* We free pbvh on changes, except in the middle of drawing a stroke
+ * since it can't deal with changing PVBH node organization, we hope
+ * topology does not change in the meantime .. weak. */
+ sculptsession_free_pbvh(ob_eval);
+
+ BKE_sculptsession_free_deformMats(ob_eval->sculpt);
+
+ /* In vertex/weight paint, force maps to be rebuilt. */
+ BKE_sculptsession_free_vwpaint_data(ob_eval->sculpt);
+ }
+ else {
+ PBVHNode **nodes;
+ int n, totnode;
+
+ BKE_pbvh_search_gather(ss->pbvh, nullptr, nullptr, &nodes, &totnode);
+
+ for (n = 0; n < totnode; n++) {
+ BKE_pbvh_node_mark_update(nodes[n]);
+ }
+
+ MEM_freeN(nodes);
+ }
+ }
+}
+
+void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval)
+{
+ /* Update after mesh evaluation in the dependency graph, to rebuild PBVH or
+ * other data when modifiers change the mesh. */
+ Object *ob_orig = DEG_get_original_object(ob_eval);
+
+ sculpt_update_object(depsgraph, ob_orig, ob_eval, false, false);
+}
+
+void BKE_sculpt_color_layer_create_if_needed(Object *object)
+{
+ Mesh *orig_me = BKE_object_get_original_mesh(object);
+
+ int types[] = {CD_PROP_COLOR, CD_PROP_BYTE_COLOR};
+ bool has_color = false;
+
+ for (int i = 0; i < ARRAY_SIZE(types); i++) {
+ has_color = CustomData_has_layer(&orig_me->vdata, types[i]) ||
+ CustomData_has_layer(&orig_me->ldata, types[i]);
+
+ if (has_color) {
+ break;
+ }
+ }
+
+ if (has_color) {
+ return;
+ }
+
+ CustomData_add_layer(&orig_me->vdata, CD_PROP_COLOR, CD_SET_DEFAULT, nullptr, orig_me->totvert);
+ CustomDataLayer *layer = orig_me->vdata.layers +
+ CustomData_get_layer_index(&orig_me->vdata, CD_PROP_COLOR);
+
+ BKE_mesh_tessface_clear(orig_me);
+
+ BKE_id_attributes_active_color_set(&orig_me->id, layer);
+ DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES);
+
+ if (object->sculpt && object->sculpt->pbvh) {
+ BKE_pbvh_update_active_vcol(object->sculpt->pbvh, orig_me);
+ }
+}
+
+void BKE_sculpt_update_object_for_edit(
+ Depsgraph *depsgraph, Object *ob_orig, bool need_pmap, bool /*need_mask*/, bool is_paint_tool)
+{
+ BLI_assert(ob_orig == DEG_get_original_object(ob_orig));
+
+ Object *ob_eval = DEG_get_evaluated_object(depsgraph, ob_orig);
+
+ sculpt_update_object(depsgraph, ob_orig, ob_eval, need_pmap, is_paint_tool);
+}
+
+int *BKE_sculpt_face_sets_ensure(Mesh *mesh)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ if (CustomData_has_layer(&mesh->pdata, CD_SCULPT_FACE_SETS)) {
+ return static_cast<int *>(CustomData_get_layer(&mesh->pdata, CD_SCULPT_FACE_SETS));
+ }
+
+ const AttributeAccessor attributes = mesh->attributes_for_write();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+
+ MutableSpan<int> face_sets = {
+ static_cast<int *>(CustomData_add_layer(
+ &mesh->pdata, CD_SCULPT_FACE_SETS, CD_CONSTRUCT, nullptr, mesh->totpoly)),
+ mesh->totpoly};
+
+ face_sets.fill(1);
+ mesh->face_sets_color_default = 1;
+ return face_sets.data();
+}
+
+bool *BKE_sculpt_hide_poly_ensure(Mesh *mesh)
+{
+ if (bool *hide_poly = static_cast<bool *>(
+ CustomData_get_layer_named(&mesh->pdata, CD_PROP_BOOL, ".hide_poly"))) {
+ return hide_poly;
+ }
+ return static_cast<bool *>(CustomData_add_layer_named(
+ &mesh->pdata, CD_PROP_BOOL, CD_SET_DEFAULT, nullptr, mesh->totpoly, ".hide_poly"));
+}
+
+int BKE_sculpt_mask_layers_ensure(Object *ob, MultiresModifierData *mmd)
+{
+ Mesh *me = static_cast<Mesh *>(ob->data);
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
+ int ret = 0;
+
+ const float *paint_mask = static_cast<const float *>(
+ CustomData_get_layer(&me->vdata, CD_PAINT_MASK));
+
+ /* if multires is active, create a grid paint mask layer if there
+ * isn't one already */
+ if (mmd && !CustomData_has_layer(&me->ldata, CD_GRID_PAINT_MASK)) {
+ GridPaintMask *gmask;
+ int level = max_ii(1, mmd->sculptlvl);
+ int gridsize = BKE_ccg_gridsize(level);
+ int gridarea = gridsize * gridsize;
+ int i, j;
+
+ gmask = static_cast<GridPaintMask *>(CustomData_add_layer(
+ &me->ldata, CD_GRID_PAINT_MASK, CD_SET_DEFAULT, nullptr, me->totloop));
+
+ for (i = 0; i < me->totloop; i++) {
+ GridPaintMask *gpm = &gmask[i];
+
+ gpm->level = level;
+ gpm->data = static_cast<float *>(
+ MEM_callocN(sizeof(float) * gridarea, "GridPaintMask.data"));
+ }
+
+ /* if vertices already have mask, copy into multires data */
+ if (paint_mask) {
+ for (i = 0; i < me->totpoly; i++) {
+ const MPoly *p = &polys[i];
+ float avg = 0;
+
+ /* mask center */
+ for (j = 0; j < p->totloop; j++) {
+ const MLoop *l = &loops[p->loopstart + j];
+ avg += paint_mask[l->v];
+ }
+ avg /= (float)p->totloop;
+
+ /* fill in multires mask corner */
+ for (j = 0; j < p->totloop; j++) {
+ GridPaintMask *gpm = &gmask[p->loopstart + j];
+ const MLoop *l = &loops[p->loopstart + j];
+ const MLoop *prev = ME_POLY_LOOP_PREV(loops, p, j);
+ const MLoop *next = ME_POLY_LOOP_NEXT(loops, p, j);
+
+ gpm->data[0] = avg;
+ gpm->data[1] = (paint_mask[l->v] + paint_mask[next->v]) * 0.5f;
+ gpm->data[2] = (paint_mask[l->v] + paint_mask[prev->v]) * 0.5f;
+ gpm->data[3] = paint_mask[l->v];
+ }
+ }
+ }
+
+ ret |= SCULPT_MASK_LAYER_CALC_LOOP;
+ }
+
+ /* Create vertex paint mask layer if there isn't one already. */
+ if (!paint_mask) {
+ CustomData_add_layer(&me->vdata, CD_PAINT_MASK, CD_SET_DEFAULT, nullptr, me->totvert);
+ ret |= SCULPT_MASK_LAYER_CALC_VERT;
+ }
+
+ return ret;
+}
+
+void BKE_sculpt_toolsettings_data_ensure(Scene *scene)
+{
+ BKE_paint_ensure(scene->toolsettings, (Paint **)&scene->toolsettings->sculpt);
+
+ Sculpt *sd = scene->toolsettings->sculpt;
+ if (!sd->detail_size) {
+ sd->detail_size = 12;
+ }
+ if (!sd->detail_percent) {
+ sd->detail_percent = 25;
+ }
+ if (sd->constant_detail == 0.0f) {
+ sd->constant_detail = 3.0f;
+ }
+
+ /* Set sane default tiling offsets. */
+ if (!sd->paint.tile_offset[0]) {
+ sd->paint.tile_offset[0] = 1.0f;
+ }
+ if (!sd->paint.tile_offset[1]) {
+ sd->paint.tile_offset[1] = 1.0f;
+ }
+ if (!sd->paint.tile_offset[2]) {
+ sd->paint.tile_offset[2] = 1.0f;
+ }
+}
+
+static bool check_sculpt_object_deformed(Object *object, const bool for_construction)
+{
+ bool deformed = false;
+
+ /* Active modifiers means extra deformation, which can't be handled correct
+ * on birth of PBVH and sculpt "layer" levels, so use PBVH only for internal brush
+ * stuff and show final evaluated mesh so user would see actual object shape. */
+ deformed |= object->sculpt->deform_modifiers_active;
+
+ if (for_construction) {
+ deformed |= object->sculpt->shapekey_active != nullptr;
+ }
+ else {
+ /* As in case with modifiers, we can't synchronize deformation made against
+ * PBVH and non-locked keyblock, so also use PBVH only for brushes and
+ * final DM to give final result to user. */
+ deformed |= object->sculpt->shapekey_active && (object->shapeflag & OB_SHAPE_LOCK) == 0;
+ }
+
+ return deformed;
+}
+
+void BKE_sculpt_sync_face_visibility_to_grids(Mesh *mesh, SubdivCCG *subdiv_ccg)
+{
+ using namespace blender;
+ using namespace blender::bke;
+ if (!subdiv_ccg) {
+ return;
+ }
+
+ const AttributeAccessor attributes = mesh->attributes();
+ const VArray<bool> hide_poly = attributes.lookup_or_default<bool>(
+ ".hide_poly", ATTR_DOMAIN_FACE, false);
+ if (hide_poly.is_single() && !hide_poly.get_internal_single()) {
+ /* Nothing is hidden, so we can just remove all visibility bitmaps. */
+ for (const int i : hide_poly.index_range()) {
+ BKE_subdiv_ccg_grid_hidden_free(subdiv_ccg, i);
+ }
+ return;
+ }
+
+ const VArraySpan<bool> hide_poly_span(hide_poly);
+ CCGKey key;
+ BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
+ for (int i = 0; i < mesh->totloop; i++) {
+ const int face_index = BKE_subdiv_ccg_grid_to_face_index(subdiv_ccg, i);
+ const bool is_hidden = hide_poly_span[face_index];
+
+ /* Avoid creating and modifying the grid_hidden bitmap if the base mesh face is visible and
+ * there is not bitmap for the grid. This is because missing grid_hidden implies grid is fully
+ * visible. */
+ if (is_hidden) {
+ BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg, i);
+ }
+
+ BLI_bitmap *gh = subdiv_ccg->grid_hidden[i];
+ if (gh) {
+ BLI_bitmap_set_all(gh, is_hidden, key.grid_area);
+ }
+ }
+}
+
+static PBVH *build_pbvh_for_dynamic_topology(Object *ob)
+{
+ PBVH *pbvh = BKE_pbvh_new();
+ BKE_pbvh_build_bmesh(pbvh,
+ ob->sculpt->bm,
+ ob->sculpt->bm_smooth_shading,
+ ob->sculpt->bm_log,
+ ob->sculpt->cd_vert_node_offset,
+ ob->sculpt->cd_face_node_offset);
+ pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
+ pbvh_show_face_sets_set(pbvh, false);
+ return pbvh;
+}
+
+static PBVH *build_pbvh_from_regular_mesh(Object *ob, Mesh *me_eval_deform, bool respect_hide)
+{
+ Mesh *me = BKE_object_get_original_mesh(ob);
+ const int looptris_num = poly_to_tri_count(me->totpoly, me->totloop);
+ PBVH *pbvh = BKE_pbvh_new();
+ BKE_pbvh_respect_hide_set(pbvh, respect_hide);
+
+ MutableSpan<MVert> verts = me->verts_for_write();
+ const Span<MPoly> polys = me->polys();
+ const Span<MLoop> loops = me->loops();
+
+ MLoopTri *looptri = static_cast<MLoopTri *>(
+ MEM_malloc_arrayN(looptris_num, sizeof(*looptri), __func__));
+
+ BKE_mesh_recalc_looptri(
+ loops.data(), polys.data(), verts.data(), me->totloop, me->totpoly, looptri);
+
+ BKE_pbvh_build_mesh(pbvh,
+ me,
+ polys.data(),
+ loops.data(),
+ verts.data(),
+ me->totvert,
+ &me->vdata,
+ &me->ldata,
+ &me->pdata,
+ looptri,
+ looptris_num);
+
+ pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
+ pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets);
+
+ const bool is_deformed = check_sculpt_object_deformed(ob, true);
+ if (is_deformed && me_eval_deform != nullptr) {
+ int totvert;
+ float(*v_cos)[3] = BKE_mesh_vert_coords_alloc(me_eval_deform, &totvert);
+ BKE_pbvh_vert_coords_apply(pbvh, v_cos, totvert);
+ MEM_freeN(v_cos);
+ }
+
+ return pbvh;
+}
+
+static PBVH *build_pbvh_from_ccg(Object *ob, SubdivCCG *subdiv_ccg, bool respect_hide)
+{
+ CCGKey key;
+ BKE_subdiv_ccg_key_top_level(&key, subdiv_ccg);
+ PBVH *pbvh = BKE_pbvh_new();
+ BKE_pbvh_respect_hide_set(pbvh, respect_hide);
+
+ Mesh *base_mesh = BKE_mesh_from_object(ob);
+ BKE_sculpt_sync_face_visibility_to_grids(base_mesh, subdiv_ccg);
+
+ BKE_pbvh_build_grids(pbvh,
+ subdiv_ccg->grids,
+ subdiv_ccg->num_grids,
+ &key,
+ (void **)subdiv_ccg->grid_faces,
+ subdiv_ccg->grid_flag_mats,
+ subdiv_ccg->grid_hidden);
+ pbvh_show_mask_set(pbvh, ob->sculpt->show_mask);
+ pbvh_show_face_sets_set(pbvh, ob->sculpt->show_face_sets);
+ return pbvh;
+}
+
+PBVH *BKE_sculpt_object_pbvh_ensure(Depsgraph *depsgraph, Object *ob)
+{
+ if (ob == nullptr || ob->sculpt == nullptr) {
+ return nullptr;
+ }
+
+ const bool respect_hide = true;
+
+ PBVH *pbvh = ob->sculpt->pbvh;
+ if (pbvh != nullptr) {
+ /* NOTE: It is possible that grids were re-allocated due to modifier
+ * stack. Need to update those pointers. */
+ if (BKE_pbvh_type(pbvh) == PBVH_GRIDS) {
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
+ Mesh *mesh_eval = static_cast<Mesh *>(object_eval->data);
+ SubdivCCG *subdiv_ccg = mesh_eval->runtime.subdiv_ccg;
+ if (subdiv_ccg != nullptr) {
+ BKE_sculpt_bvh_update_from_ccg(pbvh, subdiv_ccg);
+ }
+ }
+
+ BKE_pbvh_update_active_vcol(pbvh, BKE_object_get_original_mesh(ob));
+ BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap);
+
+ return pbvh;
+ }
+
+ if (ob->sculpt->bm != nullptr) {
+ /* Sculpting on a BMesh (dynamic-topology) gets a special PBVH. */
+ pbvh = build_pbvh_for_dynamic_topology(ob);
+ }
+ else {
+ Object *object_eval = DEG_get_evaluated_object(depsgraph, ob);
+ Mesh *mesh_eval = static_cast<Mesh *>(object_eval->data);
+ if (mesh_eval->runtime.subdiv_ccg != nullptr) {
+ pbvh = build_pbvh_from_ccg(ob, mesh_eval->runtime.subdiv_ccg, respect_hide);
+ }
+ else if (ob->type == OB_MESH) {
+ Mesh *me_eval_deform = object_eval->runtime.mesh_deform_eval;
+ pbvh = build_pbvh_from_regular_mesh(ob, me_eval_deform, respect_hide);
+ }
+ }
+
+ BKE_pbvh_pmap_set(pbvh, ob->sculpt->pmap);
+
+ ob->sculpt->pbvh = pbvh;
+ return pbvh;
+}
+
+void BKE_sculpt_bvh_update_from_ccg(PBVH *pbvh, SubdivCCG *subdiv_ccg)
+{
+ BKE_pbvh_grids_update(pbvh,
+ subdiv_ccg->grids,
+ (void **)subdiv_ccg->grid_faces,
+ subdiv_ccg->grid_flag_mats,
+ subdiv_ccg->grid_hidden);
+}
+
+bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const View3D *UNUSED(v3d))
+{
+ SculptSession *ss = ob->sculpt;
+ if (ss == nullptr || ss->pbvh == nullptr || ss->mode_type != OB_MODE_SCULPT) {
+ return false;
+ }
+
+ if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) {
+ /* Regular mesh only draws from PBVH without modifiers and shape keys. */
+
+ return !(ss->shapekey_active || ss->deform_modifiers_active);
+ }
+
+ /* Multires and dyntopo always draw directly from the PBVH. */
+ return true;
+}
+
+/* Returns the Face Set random color for rendering in the overlay given its ID and a color seed. */
+#define GOLDEN_RATIO_CONJUGATE 0.618033988749895f
+void BKE_paint_face_set_overlay_color_get(const int face_set, const int seed, uchar r_color[4])
+{
+ float rgba[4];
+ float random_mod_hue = GOLDEN_RATIO_CONJUGATE * (face_set + (seed % 10));
+ random_mod_hue = random_mod_hue - floorf(random_mod_hue);
+ const float random_mod_sat = BLI_hash_int_01(face_set + seed + 1);
+ const float random_mod_val = BLI_hash_int_01(face_set + seed + 2);
+ hsv_to_rgb(random_mod_hue,
+ 0.6f + (random_mod_sat * 0.25f),
+ 1.0f - (random_mod_val * 0.35f),
+ &rgba[0],
+ &rgba[1],
+ &rgba[2]);
+ rgba_float_to_uchar(r_color, rgba);
+}