/* SPDX-FileCopyrightText: 2009 by Nicholas Bishop. All rights reserved. * * SPDX-License-Identifier: GPL-2.0-or-later */ /** \file * \ingroup bke */ /* ALlow using deprecated color for sync legacy. */ #define DNA_DEPRECATED_ALLOW #include #include #include #include "MEM_guardedalloc.h" #include "DNA_asset_types.h" #include "DNA_brush_types.h" #include "DNA_key_types.h" #include "DNA_mesh_types.h" #include "DNA_meshdata_types.h" #include "DNA_modifier_types.h" #include "DNA_object_enums.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_space_types.h" #include "DNA_userdef_types.h" #include "DNA_view3d_types.h" #include "DNA_workspace_types.h" #include "BLI_hash.h" #include "BLI_listbase.h" #include "BLI_math_color.h" #include "BLI_math_matrix.hh" #include "BLI_math_vector.h" #include "BLI_noise.hh" #include "BLI_string.h" #include "BLI_utildefines.h" #include "BLI_vector.hh" #include "BLT_translation.hh" #include "BKE_asset.hh" #include "BKE_asset_edit.hh" #include "BKE_attribute.h" #include "BKE_attribute.hh" #include "BKE_brush.hh" #include "BKE_ccg.hh" #include "BKE_colortools.hh" #include "BKE_context.hh" #include "BKE_crazyspace.hh" #include "BKE_deform.hh" #include "BKE_idtype.hh" #include "BKE_image.hh" #include "BKE_key.hh" #include "BKE_layer.hh" #include "BKE_lib_id.hh" #include "BKE_main.hh" #include "BKE_material.hh" #include "BKE_mesh.hh" #include "BKE_mesh_runtime.hh" #include "BKE_modifier.hh" #include "BKE_multires.hh" #include "BKE_object.hh" #include "BKE_paint.hh" #include "BKE_paint_bvh.hh" #include "BKE_paint_types.hh" #include "BKE_scene.hh" #include "BKE_subdiv_ccg.hh" #include "DEG_depsgraph.hh" #include "DEG_depsgraph_query.hh" #include "PRF_profile.hh" #include "RNA_enum_types.hh" #include "BLO_read_write.hh" #include "IMB_colormanagement.hh" #include "bmesh.hh" namespace blender { using bke::AttrDomain; static void palette_init_data(ID *id) { Palette *palette = id_cast(id); INIT_DEFAULT_STRUCT_AFTER(palette, id); /* Enable fake user by default. */ id_fake_user_set(&palette->id); } static void palette_copy_data(Main * /*bmain*/, std::optional /*owner_library*/, ID *id_dst, const ID *id_src, const int /*flag*/) { Palette *palette_dst = id_cast(id_dst); const Palette *palette_src = id_cast(id_src); BLI_duplicatelist(&palette_dst->colors, &palette_src->colors); } static void palette_free_data(ID *id) { Palette *palette = id_cast(id); palette->colors.free_no_destruct(); } static void palette_foreach_working_space_color(ID *id, const IDTypeForeachColorFunctionCallback &fn) { Palette *palette = id_cast(id); for (PaletteColor &color : palette->colors) { fn.single(color.color); BKE_palette_color_sync_legacy(&color); } } static void palette_blend_write(BlendWriter *writer, ID *id, const void *id_address) { Palette *palette = id_cast(id); writer->write_id_struct(id_address, palette); BKE_id_blend_write(writer, &palette->id); writer->write_struct_list(&palette->colors); } static void palette_blend_read_data(BlendDataReader *reader, ID *id) { Palette *palette = id_cast(id); BLO_read_struct_list(reader, PaletteColor, &palette->colors); } static void palette_undo_preserve(BlendLibReader * /*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, false, 0); std::swap(id_new->properties, id_old->properties); std::swap(id_new->system_properties, id_old->system_properties); } IDTypeInfo IDType_ID_PAL = { .id_code = Palette::id_type, .id_filter = FILTER_ID_PAL, .dependencies_id_types = 0, .main_listbase_index = INDEX_ID_PAL, .struct_size = sizeof(Palette), .name = "Palette", .name_plural = N_("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, .foreach_working_space_color = palette_foreach_working_space_color, .owner_pointer_get = nullptr, .blend_write = palette_blend_write, .blend_read_data = palette_blend_read_data, .blend_read_after_liblink = nullptr, .blend_read_undo_preserve = palette_undo_preserve, .lib_override_apply_post = nullptr, }; static void paint_curve_copy_data(Main * /*bmain*/, std::optional /*owner_library*/, ID *id_dst, const ID *id_src, const int /*flag*/) { PaintCurve *paint_curve_dst = id_cast(id_dst); const PaintCurve *paint_curve_src = id_cast(id_src); if (paint_curve_src->tot_points != 0) { paint_curve_dst->points = static_cast( MEM_dupalloc(paint_curve_src->points)); } } static void paint_curve_free_data(ID *id) { PaintCurve *paint_curve = id_cast(id); MEM_SAFE_DELETE(paint_curve->points); paint_curve->tot_points = 0; } static void paint_curve_blend_write(BlendWriter *writer, ID *id, const void *id_address) { PaintCurve *pc = id_cast(id); writer->write_id_struct(id_address, pc); BKE_id_blend_write(writer, &pc->id); writer->write_struct_array(pc->tot_points, pc->points); } static void paint_curve_blend_read_data(BlendDataReader *reader, ID *id) { PaintCurve *pc = id_cast(id); BLO_read_array_and_validate_size(reader, &pc->points, &pc->tot_points); } IDTypeInfo IDType_ID_PC = { .id_code = PaintCurve::id_type, .id_filter = FILTER_ID_PC, .dependencies_id_types = 0, .main_listbase_index = INDEX_ID_PC, .struct_size = sizeof(PaintCurve), .name = "PaintCurve", .name_plural = N_("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, .foreach_working_space_color = nullptr, .owner_pointer_get = nullptr, .blend_write = paint_curve_blend_write, .blend_read_data = paint_curve_blend_read_data, .blend_read_after_liblink = nullptr, .blend_read_undo_preserve = nullptr, .lib_override_apply_post = nullptr, }; static ePaintOverlayControlFlags overlay_flags = ePaintOverlayControlFlags{}; void BKE_paint_invalidate_overlay_tex(const Main &bmain, Scene *scene, ViewLayer *view_layer, const Tex *tex) { Paint *paint = BKE_paint_get_active(bmain, scene, view_layer); if (!paint) { return; } Brush *br = BKE_paint_brush(paint); 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(const Main &bmain, Scene *scene, ViewLayer *view_layer, CurveMapping *curve) { Paint *paint = BKE_paint_get_active(bmain, scene, view_layer); if (paint == nullptr) { return; } Brush *br = BKE_paint_brush(paint); if (br && br->curve_distance_falloff == curve) { overlay_flags |= PAINT_OVERLAY_INVALID_CURVE; } } void BKE_paint_invalidate_overlay_all() { overlay_flags |= (PAINT_OVERLAY_INVALID_TEXTURE_SECONDARY | PAINT_OVERLAY_INVALID_TEXTURE_PRIMARY | PAINT_OVERLAY_INVALID_CURVE); } ePaintOverlayControlFlags BKE_paint_get_overlay_flags() { 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, PaintMode 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 PaintMode::Sculpt: paint_ptr = reinterpret_cast(&ts->sculpt); break; case PaintMode::Vertex: paint_ptr = reinterpret_cast(&ts->vpaint); break; case PaintMode::Weight: paint_ptr = reinterpret_cast(&ts->wpaint); break; case PaintMode::Texture2D: case PaintMode::Texture3D: paint_tmp = reinterpret_cast(&ts->imapaint); paint_ptr = &paint_tmp; break; case PaintMode::GPencil: paint_ptr = reinterpret_cast(&ts->gp_paint); break; case PaintMode::VertexGPencil: paint_ptr = reinterpret_cast(&ts->gp_vertexpaint); break; case PaintMode::SculptGPencil: paint_ptr = reinterpret_cast(&ts->gp_sculptpaint); break; case PaintMode::WeightGPencil: paint_ptr = reinterpret_cast(&ts->gp_weightpaint); break; case PaintMode::SculptCurves: paint_ptr = reinterpret_cast(&ts->curves_sculpt); break; case PaintMode::Invalid: break; } if (paint_ptr) { BKE_paint_ensure(ts, paint_ptr); return true; } return false; } Paint *BKE_paint_get_active_from_paintmode(Scene *sce, PaintMode mode) { if (sce) { ToolSettings *ts = sce->toolsettings; switch (mode) { case PaintMode::Sculpt: return &ts->sculpt->paint; case PaintMode::Vertex: return &ts->vpaint->paint; case PaintMode::Weight: return &ts->wpaint->paint; case PaintMode::Texture2D: case PaintMode::Texture3D: return &ts->imapaint.paint; case PaintMode::GPencil: return &ts->gp_paint->paint; case PaintMode::VertexGPencil: return &ts->gp_vertexpaint->paint; case PaintMode::SculptGPencil: return &ts->gp_sculptpaint->paint; case PaintMode::WeightGPencil: return &ts->gp_weightpaint->paint; case PaintMode::SculptCurves: return &ts->curves_sculpt->paint; case PaintMode::Invalid: return nullptr; default: return &ts->imapaint.paint; } } return nullptr; } const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(const PaintMode mode) { switch (mode) { case PaintMode::Sculpt: return rna_enum_brush_sculpt_brush_type_items; case PaintMode::Vertex: return rna_enum_brush_vertex_brush_type_items; case PaintMode::Weight: return rna_enum_brush_weight_brush_type_items; case PaintMode::Texture2D: case PaintMode::Texture3D: return rna_enum_brush_image_brush_type_items; case PaintMode::GPencil: return rna_enum_brush_gpencil_types_items; case PaintMode::VertexGPencil: return rna_enum_brush_gpencil_vertex_types_items; case PaintMode::SculptGPencil: return rna_enum_brush_gpencil_sculpt_types_items; case PaintMode::WeightGPencil: return rna_enum_brush_gpencil_weight_types_items; case PaintMode::SculptCurves: return rna_enum_brush_curves_sculpt_brush_type_items; case PaintMode::Invalid: break; } return nullptr; } Paint *BKE_paint_get_active(const Main &bmain, Scene *sce, ViewLayer *view_layer) { if (sce && view_layer) { ToolSettings *ts = sce->toolsettings; BKE_view_layer_synced_ensure(bmain, 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_GREASE_PENCIL: return &ts->gp_paint->paint; case OB_MODE_VERTEX_GREASE_PENCIL: return &ts->gp_vertexpaint->paint; case OB_MODE_SCULPT_GREASE_PENCIL: return &ts->gp_sculptpaint->paint; case OB_MODE_WEIGHT_GREASE_PENCIL: return &ts->gp_weightpaint->paint; case OB_MODE_SCULPT_CURVES: return &ts->curves_sculpt->paint; default: break; } } /* default to image paint */ return &ts->imapaint.paint; } return nullptr; } Paint *BKE_paint_get_active_from_context(const bContext *C) { const Main *bmain = CTX_data_main(C); Scene *sce = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); if (sce && view_layer) { ToolSettings *ts = sce->toolsettings; BKE_view_layer_synced_ensure(*bmain, sce, view_layer); Object *obact = BKE_view_layer_active_object_get(view_layer); SpaceImage *sima = CTX_wm_space_image(C); if (sima != nullptr) { if (obact && obact->mode == OB_MODE_EDIT) { if (sima->mode == SI_MODE_PAINT) { return &ts->imapaint.paint; } } else { return &ts->imapaint.paint; } } else { return BKE_paint_get_active(*bmain, sce, view_layer); } } return nullptr; } PaintMode BKE_paintmode_get_active_from_context(const bContext *C) { const Main *bmain = CTX_data_main(C); Scene *sce = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); if (sce && view_layer) { BKE_view_layer_synced_ensure(*bmain, sce, view_layer); Object *obact = BKE_view_layer_active_object_get(view_layer); SpaceImage *sima = CTX_wm_space_image(C); if (sima != nullptr) { if (obact && obact->mode == OB_MODE_EDIT) { if (sima->mode == SI_MODE_PAINT) { return PaintMode::Texture2D; } } else { return PaintMode::Texture2D; } } else if (obact) { switch (obact->mode) { case OB_MODE_SCULPT: return PaintMode::Sculpt; case OB_MODE_SCULPT_GREASE_PENCIL: if (obact->type == OB_GREASE_PENCIL) { return PaintMode::SculptGPencil; } return PaintMode::Invalid; case OB_MODE_PAINT_GREASE_PENCIL: return PaintMode::GPencil; case OB_MODE_WEIGHT_GREASE_PENCIL: return PaintMode::WeightGPencil; case OB_MODE_VERTEX_GREASE_PENCIL: return PaintMode::VertexGPencil; case OB_MODE_VERTEX_PAINT: return PaintMode::Vertex; case OB_MODE_WEIGHT_PAINT: return PaintMode::Weight; case OB_MODE_TEXTURE_PAINT: return PaintMode::Texture3D; case OB_MODE_SCULPT_CURVES: return PaintMode::SculptCurves; default: return PaintMode::Texture2D; } } else { /* default to image paint */ return PaintMode::Texture2D; } } return PaintMode::Invalid; } PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref) { if (tref->space_type == SPACE_VIEW3D) { switch (tref->mode) { case CTX_MODE_SCULPT: return PaintMode::Sculpt; case CTX_MODE_PAINT_VERTEX: return PaintMode::Vertex; case CTX_MODE_PAINT_WEIGHT: return PaintMode::Weight; case CTX_MODE_PAINT_GPENCIL_LEGACY: return PaintMode::GPencil; case CTX_MODE_PAINT_TEXTURE: return PaintMode::Texture3D; case CTX_MODE_VERTEX_GREASE_PENCIL: case CTX_MODE_VERTEX_GPENCIL_LEGACY: return PaintMode::VertexGPencil; case CTX_MODE_SCULPT_GPENCIL_LEGACY: return PaintMode::SculptGPencil; case CTX_MODE_WEIGHT_GREASE_PENCIL: case CTX_MODE_WEIGHT_GPENCIL_LEGACY: return PaintMode::WeightGPencil; case CTX_MODE_SCULPT_CURVES: return PaintMode::SculptCurves; case CTX_MODE_PAINT_GREASE_PENCIL: return PaintMode::GPencil; case CTX_MODE_SCULPT_GREASE_PENCIL: return PaintMode::SculptGPencil; } } else if (tref->space_type == SPACE_IMAGE) { switch (tref->mode) { case SI_MODE_PAINT: return PaintMode::Texture2D; } } return PaintMode::Invalid; } bool BKE_paint_use_unified_size(const Paint *paint) { /* For now, Grease Pencil Draw mode doesn't use the unified paint settings. */ if (paint->runtime->ob_mode == OB_MODE_PAINT_GREASE_PENCIL) { return false; } return paint->unified_paint_settings.flag & UNIFIED_PAINT_SIZE; } bool BKE_paint_use_unified_strength(const Paint *paint) { /* For now, Grease Pencil Draw mode doesn't use the unified paint settings. */ if (paint->runtime->ob_mode == OB_MODE_PAINT_GREASE_PENCIL) { return false; } return paint->unified_paint_settings.flag & UNIFIED_PAINT_ALPHA; } bool BKE_paint_use_unified_color(const Paint *paint) { /* For now, Grease Pencil Draw mode doesn't use the unified paint settings. */ if (paint->runtime->ob_mode == OB_MODE_PAINT_GREASE_PENCIL) { return false; } return paint->unified_paint_settings.flag & UNIFIED_PAINT_COLOR; } /** * After changing #Paint.brush_asset_reference, call this to activate the matching brush, importing * it if necessary. Has no effect if #Paint.brush is set already. */ static bool paint_brush_update_from_asset_reference(Main *bmain, Paint *paint) { /* Don't resolve this during file read, it will be done after. */ if (bmain->is_locked_for_linking) { return false; } /* Attempt to restore a valid active brush from brush asset information. */ if (paint->brush != nullptr) { return false; } if (paint->brush_asset_reference == nullptr) { return false; } Brush *brush = reinterpret_cast( bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, *paint->brush_asset_reference)); BLI_assert(brush == nullptr || bke::asset_edit_id_is_editable(brush->id)); /* Ensure we have a brush with appropriate mode to assign. * Could happen if contents of asset blend was manually changed. */ if (brush == nullptr || (paint->runtime->ob_mode & brush->ob_mode) == 0) { MEM_delete(paint->brush_asset_reference); paint->brush_asset_reference = nullptr; return false; } paint->brush = brush; return true; } Brush *BKE_paint_brush(Paint *paint) { return paint ? paint->brush : nullptr; } const Brush *BKE_paint_brush_for_read(const Paint *paint) { return paint ? paint->brush : nullptr; } bool BKE_paint_can_use_brush(const Paint *paint, const Brush *brush) { if (paint == nullptr) { return false; } return !brush || (paint->runtime->ob_mode & brush->ob_mode) != 0; } static AssetWeakReference *asset_reference_create_from_brush(Brush *brush) { if (std::optional weak_ref = bke::asset_edit_weak_reference_from_id( brush->id)) { return MEM_new(__func__, *weak_ref); } return nullptr; } bool BKE_paint_brush_set(Main *bmain, Paint *paint, const AssetWeakReference &brush_asset_reference) { /* Don't resolve this during file read, it will be done after. */ if (bmain->is_locked_for_linking) { return false; } Brush *brush = reinterpret_cast( bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference)); BLI_assert(brush == nullptr || !ID_IS_LINKED(brush) || bke::asset_edit_id_is_editable(brush->id)); /* Ensure we have a brush with appropriate mode to assign. * Could happen if contents of asset blend were manually changed. */ if (brush == nullptr || !BKE_paint_can_use_brush(paint, brush)) { return false; } /* Update the brush itself. */ paint->brush = brush; /* Update the brush asset reference. */ { MEM_delete(paint->brush_asset_reference); paint->brush_asset_reference = nullptr; if (brush != nullptr) { BLI_assert(bke::asset_edit_weak_reference_from_id(brush->id) == brush_asset_reference); paint->brush_asset_reference = MEM_new(__func__, brush_asset_reference); } } return true; } bool BKE_paint_brush_set(Paint *paint, Brush *brush) { if (!BKE_paint_can_use_brush(paint, brush)) { return false; } paint->brush = brush; MEM_delete(paint->brush_asset_reference); paint->brush_asset_reference = nullptr; if (brush != nullptr) { paint->brush_asset_reference = asset_reference_create_from_brush(brush); } BKE_paint_invalidate_overlay_all(); return true; } static const char *paint_brush_essentials_asset_file_name_from_paint_mode( const PaintMode paint_mode) { switch (paint_mode) { case PaintMode::Sculpt: return "essentials_brushes-mesh_sculpt.blend"; case PaintMode::Vertex: return "essentials_brushes-mesh_vertex.blend"; case PaintMode::Weight: return "essentials_brushes-mesh_weight.blend"; case PaintMode::Texture2D: case PaintMode::Texture3D: return "essentials_brushes-mesh_texture.blend"; case PaintMode::GPencil: return "essentials_brushes-gp_draw.blend"; case PaintMode::SculptGPencil: return "essentials_brushes-gp_sculpt.blend"; case PaintMode::WeightGPencil: return "essentials_brushes-gp_weight.blend"; case PaintMode::VertexGPencil: return "essentials_brushes-gp_vertex.blend"; case PaintMode::SculptCurves: return "essentials_brushes-curve_sculpt.blend"; default: return nullptr; } } static AssetWeakReference *paint_brush_asset_reference_ptr_from_essentials( const char *name, const PaintMode paint_mode) { const char *essentials_file_name = paint_brush_essentials_asset_file_name_from_paint_mode( paint_mode); if (!essentials_file_name) { return nullptr; } AssetWeakReference *weak_ref = MEM_new(__func__); weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS; weak_ref->asset_library_identifier = nullptr; weak_ref->relative_asset_identifier = BLI_sprintfN( "brushes/%s/Brush/%s", essentials_file_name, name); return weak_ref; } static std::optional paint_brush_asset_reference_from_essentials( const char *name, const PaintMode paint_mode) { const char *essentials_file_name = paint_brush_essentials_asset_file_name_from_paint_mode( paint_mode); if (!essentials_file_name) { return {}; } AssetWeakReference weak_ref; weak_ref.asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS; weak_ref.asset_library_identifier = nullptr; weak_ref.relative_asset_identifier = BLI_sprintfN( "brushes/%s/Brush/%s", essentials_file_name, name); return weak_ref; } Brush *BKE_paint_brush_from_essentials(Main *bmain, const PaintMode paint_mode, const char *name) { std::optional weak_ref = paint_brush_asset_reference_from_essentials( name, paint_mode); if (!weak_ref) { return nullptr; } return reinterpret_cast( bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, *weak_ref)); } static void paint_brush_set_essentials_reference(Paint *paint, const char *name) { /* Set brush asset reference to a named brush in the essentials asset library. */ MEM_delete(paint->brush_asset_reference); BLI_assert(paint->runtime->initialized); paint->brush_asset_reference = paint_brush_asset_reference_ptr_from_essentials( name, paint->runtime->paint_mode); paint->brush = nullptr; } static void paint_brush_default_essentials_name_get(const PaintMode paint_mode, std::optional brush_type, StringRefNull *r_name) { const char *name = ""; switch (paint_mode) { case PaintMode::Sculpt: name = "Draw"; if (brush_type) { switch (eBrushSculptType(*brush_type)) { case SCULPT_BRUSH_TYPE_MASK: name = "Mask"; break; case SCULPT_BRUSH_TYPE_DRAW_FACE_SETS: name = "Face Set Paint"; break; case SCULPT_BRUSH_TYPE_PAINT: name = "Paint Hard"; break; case SCULPT_BRUSH_TYPE_SIMPLIFY: name = "Density"; break; case SCULPT_BRUSH_TYPE_DISPLACEMENT_ERASER: name = "Erase Multires Displacement"; break; case SCULPT_BRUSH_TYPE_DISPLACEMENT_SMEAR: name = "Smear Multires Displacement"; break; default: break; } } break; case PaintMode::Vertex: name = "Paint Hard"; if (brush_type) { switch (eBrushVertexPaintType(*brush_type)) { case VPAINT_BRUSH_TYPE_BLUR: name = "Blur"; break; case VPAINT_BRUSH_TYPE_AVERAGE: name = "Average"; break; case VPAINT_BRUSH_TYPE_SMEAR: name = "Smear"; break; case VPAINT_BRUSH_TYPE_DRAW: /* Use default, don't override. */ break; } } break; case PaintMode::Weight: name = "Paint"; if (brush_type) { switch (eBrushWeightPaintType(*brush_type)) { case WPAINT_BRUSH_TYPE_BLUR: name = "Blur"; break; case WPAINT_BRUSH_TYPE_AVERAGE: name = "Average"; break; case WPAINT_BRUSH_TYPE_SMEAR: name = "Smear"; break; case WPAINT_BRUSH_TYPE_DRAW: /* Use default, don't override. */ break; } } break; case PaintMode::Texture2D: case PaintMode::Texture3D: name = "Paint Hard"; if (brush_type) { switch (eBrushImagePaintType(*brush_type)) { case IMAGE_PAINT_BRUSH_TYPE_SOFTEN: name = "Blur"; break; case IMAGE_PAINT_BRUSH_TYPE_SMEAR: name = "Smear"; break; case IMAGE_PAINT_BRUSH_TYPE_FILL: name = "Fill"; break; case IMAGE_PAINT_BRUSH_TYPE_MASK: name = "Mask"; break; case IMAGE_PAINT_BRUSH_TYPE_CLONE: name = "Clone"; break; case IMAGE_PAINT_BRUSH_TYPE_DRAW: break; } } break; case PaintMode::SculptCurves: name = "Comb"; if (brush_type) { switch (eBrushCurvesSculptType(*brush_type)) { case CURVES_SCULPT_BRUSH_TYPE_ADD: name = "Add"; break; case CURVES_SCULPT_BRUSH_TYPE_DELETE: name = "Delete"; break; case CURVES_SCULPT_BRUSH_TYPE_DENSITY: name = "Density"; break; case CURVES_SCULPT_BRUSH_TYPE_SELECTION_PAINT: name = "Select"; break; default: break; } } break; case PaintMode::GPencil: name = "Pencil"; /* Different default brush for some brush types. */ if (brush_type) { switch (eBrushGPaintType(*brush_type)) { case GPAINT_BRUSH_TYPE_ERASE: name = "Eraser Hard"; break; case GPAINT_BRUSH_TYPE_FILL: name = "Fill"; break; case GPAINT_BRUSH_TYPE_DRAW: case GPAINT_BRUSH_TYPE_TINT: /* Use default, don't override. */ break; } } break; case PaintMode::VertexGPencil: name = "Paint"; if (brush_type) { switch (eBrushGPVertexType(*brush_type)) { case GPVERTEX_BRUSH_TYPE_BLUR: name = "Blur"; break; case GPVERTEX_BRUSH_TYPE_AVERAGE: name = "Average"; break; case GPVERTEX_BRUSH_TYPE_SMEAR: name = "Smear"; break; case GPVERTEX_BRUSH_TYPE_REPLACE: name = "Replace"; break; case GPVERTEX_BRUSH_TYPE_DRAW: /* Use default, don't override. */ break; case GPVERTEX_BRUSH_TYPE_TINT: /* Unused brush type. */ BLI_assert_unreachable(); break; } } break; case PaintMode::SculptGPencil: name = "Smooth"; if (brush_type) { switch (eBrushGPSculptType(*brush_type)) { case GPSCULPT_BRUSH_TYPE_CLONE: name = "Clone"; break; default: break; } } break; case PaintMode::WeightGPencil: name = "Paint"; if (brush_type) { switch (eBrushGPWeightType(*brush_type)) { case GPWEIGHT_BRUSH_TYPE_BLUR: name = "Blur"; break; case GPWEIGHT_BRUSH_TYPE_AVERAGE: name = "Average"; break; case GPWEIGHT_BRUSH_TYPE_SMEAR: name = "Smear"; break; case GPWEIGHT_BRUSH_TYPE_DRAW: /* Use default, don't override. */ break; } } break; default: BLI_assert_unreachable(); break; } *r_name = name; } std::optional BKE_paint_brush_type_default_reference( const PaintMode paint_mode, std::optional brush_type) { StringRefNull name; paint_brush_default_essentials_name_get(paint_mode, brush_type, &name); if (name.is_empty()) { return {}; } return paint_brush_asset_reference_from_essentials(name.c_str(), paint_mode); } static void paint_brush_set_default_reference(Paint *paint, const bool do_regular = true) { if (!paint->runtime || !paint->runtime->initialized) { /* Can happen when loading old file where toolsettings are created in versioning, without * calling #paint_runtime_init(). Will be done later when necessary. */ return; } StringRefNull name; paint_brush_default_essentials_name_get(paint->runtime->paint_mode, std::nullopt, &name); if (do_regular && !name.is_empty()) { paint_brush_set_essentials_reference(paint, name.c_str()); } } void BKE_paint_brushes_set_default_references(ToolSettings *ts) { if (ts->sculpt) { paint_brush_set_default_reference(&ts->sculpt->paint); } if (ts->curves_sculpt) { paint_brush_set_default_reference(&ts->curves_sculpt->paint); } if (ts->wpaint) { paint_brush_set_default_reference(&ts->wpaint->paint); } if (ts->vpaint) { paint_brush_set_default_reference(&ts->vpaint->paint); } if (ts->gp_paint) { paint_brush_set_default_reference(&ts->gp_paint->paint); } if (ts->gp_vertexpaint) { paint_brush_set_default_reference(&ts->gp_vertexpaint->paint); } if (ts->gp_sculptpaint) { paint_brush_set_default_reference(&ts->gp_sculptpaint->paint); } if (ts->gp_weightpaint) { paint_brush_set_default_reference(&ts->gp_weightpaint->paint); } paint_brush_set_default_reference(&ts->imapaint.paint); } bool BKE_paint_brush_set_default(Main *bmain, Paint *paint) { paint_brush_set_default_reference(paint, true); return paint_brush_update_from_asset_reference(bmain, paint); } bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name) { paint_brush_set_essentials_reference(paint, name); return paint_brush_update_from_asset_reference(bmain, paint); } void BKE_paint_previous_asset_reference_set(Paint *paint, AssetWeakReference &&asset_weak_reference) { if (!paint->runtime->previous_active_brush_reference) { paint->runtime->previous_active_brush_reference = MEM_new(__func__); } *paint->runtime->previous_active_brush_reference = asset_weak_reference; } void BKE_paint_previous_asset_reference_clear(Paint *paint) { MEM_SAFE_DELETE(paint->runtime->previous_active_brush_reference); } void BKE_paint_brushes_validate(Main *bmain, Paint *paint) { /* Clear brush with invalid mode. Unclear if this can still happen, * but kept from old paint tool-slots code. */ Brush *brush = BKE_paint_brush(paint); if (brush && (paint->runtime->ob_mode & brush->ob_mode) == 0) { BKE_paint_brush_set(paint, nullptr); BKE_paint_brush_set_default(bmain, paint); } } static void paint_runtime_init(const ToolSettings *ts, Paint *paint) { if (!paint->runtime) { paint->runtime = MEM_new(__func__); } if (paint == &ts->imapaint.paint) { paint->runtime->ob_mode = OB_MODE_TEXTURE_PAINT; /* Note: This is an odd case where 3D Texture paint and Image Paint share the same struct. * It would be equally valid to assign PaintMode::Texture2D to this. */ paint->runtime->paint_mode = PaintMode::Texture3D; } else if (ts->sculpt && paint == &ts->sculpt->paint) { paint->runtime->ob_mode = OB_MODE_SCULPT; paint->runtime->paint_mode = PaintMode::Sculpt; } else if (ts->vpaint && paint == &ts->vpaint->paint) { paint->runtime->ob_mode = OB_MODE_VERTEX_PAINT; paint->runtime->paint_mode = PaintMode::Vertex; } else if (ts->wpaint && paint == &ts->wpaint->paint) { paint->runtime->ob_mode = OB_MODE_WEIGHT_PAINT; paint->runtime->paint_mode = PaintMode::Weight; } else if (ts->gp_paint && paint == &ts->gp_paint->paint) { paint->runtime->ob_mode = OB_MODE_PAINT_GREASE_PENCIL; paint->runtime->paint_mode = PaintMode::GPencil; } else if (ts->gp_vertexpaint && paint == &ts->gp_vertexpaint->paint) { paint->runtime->ob_mode = OB_MODE_VERTEX_GREASE_PENCIL; paint->runtime->paint_mode = PaintMode::VertexGPencil; } else if (ts->gp_sculptpaint && paint == &ts->gp_sculptpaint->paint) { paint->runtime->ob_mode = OB_MODE_SCULPT_GREASE_PENCIL; paint->runtime->paint_mode = PaintMode::SculptGPencil; } else if (ts->gp_weightpaint && paint == &ts->gp_weightpaint->paint) { paint->runtime->ob_mode = OB_MODE_WEIGHT_GREASE_PENCIL; paint->runtime->paint_mode = PaintMode::WeightGPencil; } else if (ts->curves_sculpt && paint == &ts->curves_sculpt->paint) { paint->runtime->ob_mode = OB_MODE_SCULPT_CURVES; paint->runtime->paint_mode = PaintMode::SculptCurves; } else { BLI_assert_unreachable(); } paint->runtime->initialized = true; } uint BKE_paint_get_brush_type_offset_from_paintmode(const PaintMode mode) { switch (mode) { case PaintMode::Texture2D: case PaintMode::Texture3D: return offsetof(Brush, image_brush_type); case PaintMode::Sculpt: return offsetof(Brush, sculpt_brush_type); case PaintMode::Vertex: return offsetof(Brush, vertex_brush_type); case PaintMode::Weight: return offsetof(Brush, weight_brush_type); case PaintMode::GPencil: return offsetof(Brush, gpencil_brush_type); case PaintMode::VertexGPencil: return offsetof(Brush, gpencil_vertex_brush_type); case PaintMode::SculptGPencil: return offsetof(Brush, gpencil_sculpt_brush_type); case PaintMode::WeightGPencil: return offsetof(Brush, gpencil_weight_brush_type); case PaintMode::SculptCurves: return offsetof(Brush, curves_sculpt_brush_type); case PaintMode::Invalid: break; /* We don't use these yet. */ } return 0; } std::optional BKE_paint_get_brush_type_from_obmode(const Brush *brush, const eObjectMode ob_mode) { switch (ob_mode) { case OB_MODE_TEXTURE_PAINT: case OB_MODE_EDIT: return brush->image_brush_type; case OB_MODE_SCULPT: return brush->sculpt_brush_type; case OB_MODE_VERTEX_PAINT: return brush->vertex_brush_type; case OB_MODE_WEIGHT_PAINT: return brush->weight_brush_type; case OB_MODE_PAINT_GREASE_PENCIL: return brush->gpencil_brush_type; case OB_MODE_VERTEX_GREASE_PENCIL: return brush->gpencil_vertex_brush_type; case OB_MODE_SCULPT_GREASE_PENCIL: return brush->gpencil_sculpt_brush_type; case OB_MODE_WEIGHT_GREASE_PENCIL: return brush->gpencil_weight_brush_type; case OB_MODE_SCULPT_CURVES: return brush->curves_sculpt_brush_type; default: return {}; } } std::optional BKE_paint_get_brush_type_from_paintmode(const Brush *brush, const PaintMode mode) { switch (mode) { case PaintMode::Texture2D: case PaintMode::Texture3D: return brush->image_brush_type; case PaintMode::Sculpt: return brush->sculpt_brush_type; case PaintMode::Vertex: return brush->vertex_brush_type; case PaintMode::Weight: return brush->weight_brush_type; case PaintMode::GPencil: return brush->gpencil_brush_type; case PaintMode::VertexGPencil: return brush->gpencil_vertex_brush_type; case PaintMode::SculptGPencil: return brush->gpencil_sculpt_brush_type; case PaintMode::WeightGPencil: return brush->gpencil_weight_brush_type; case PaintMode::SculptCurves: return brush->curves_sculpt_brush_type; case PaintMode::Invalid: default: return {}; } } PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name) { PaintCurve *pc = BKE_id_new(bmain, name); return pc; } Palette *BKE_paint_palette(Paint *paint) { return paint ? paint->palette : nullptr; } void BKE_paint_palette_set(Paint *paint, Palette *palette) { if (paint) { id_us_min(id_cast(paint->palette)); paint->palette = palette; id_us_plus(id_cast(paint->palette)); } } 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 && !palette->colors.is_empty()) { palette->active_color = 0; } MEM_delete(color); } void BKE_palette_clear(Palette *palette) { palette->colors.free_no_destruct(); palette->active_color = 0; } void BKE_palette_color_set(PaletteColor *color, const float rgb[3]) { copy_v3_v3(color->color, rgb); BKE_palette_color_sync_legacy(color); } void BKE_palette_color_sync_legacy(PaletteColor *color) { linearrgb_to_srgb_v3_v3(color->rgb, color->color); } Palette *BKE_palette_add(Main *bmain, const char *name) { Palette *palette = BKE_id_new(bmain, name); return palette; } PaletteColor *BKE_palette_color_add(Palette *palette) { PaletteColor *color = MEM_new(__func__); BLI_addtail(&palette->colors, color); return color; } bool BKE_palette_is_empty(const Palette *palette) { return palette->colors.is_empty(); } bool BKE_paint_select_face_test(const Object *ob) { return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) && ((id_cast(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(const Object *ob) { return ((ob != nullptr) && (ob->type == OB_MESH) && (ob->data != nullptr) && ((id_cast(ob->data))->editflag & ME_EDIT_PAINT_VERT_SEL) && (ob->mode & OB_MODE_WEIGHT_PAINT || ob->mode & OB_MODE_VERTEX_PAINT)); } bool BKE_paint_select_grease_pencil_test(const Object *ob) { if (ob == nullptr || ob->data == nullptr) { return false; } if (ob->type == OB_GREASE_PENCIL) { return (ob->mode & (OB_MODE_SCULPT_GREASE_PENCIL | OB_MODE_VERTEX_GREASE_PENCIL)); } return false; } bool BKE_paint_select_elem_test(const Object *ob) { return (BKE_paint_select_vert_test(ob) || BKE_paint_select_face_test(ob) || BKE_paint_select_grease_pencil_test(ob)); } bool BKE_paint_always_hide_test(const 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 *paint, int preset) { CurveMapping *cumap = nullptr; CurveMap *cuma = nullptr; if (!paint->cavity_curve) { paint->cavity_curve = BKE_curvemapping_add(1, 0, 0, 1, 1); } cumap = paint->cavity_curve; cumap->flag &= ~CUMA_EXTEND_EXTRAPOLATE; cumap->preset = eCurveMappingPreset(preset); cuma = cumap->cm; BKE_curvemap_reset(cuma, &cumap->clipr, cumap->preset, CurveMapSlopeType::Positive); BKE_curvemapping_changed(cumap, false); } eObjectMode BKE_paint_object_mode_from_paintmode(const PaintMode mode) { switch (mode) { case PaintMode::Sculpt: return OB_MODE_SCULPT; case PaintMode::Vertex: return OB_MODE_VERTEX_PAINT; case PaintMode::Weight: return OB_MODE_WEIGHT_PAINT; case PaintMode::Texture2D: case PaintMode::Texture3D: return OB_MODE_TEXTURE_PAINT; case PaintMode::SculptCurves: return OB_MODE_SCULPT_CURVES; case PaintMode::GPencil: return OB_MODE_PAINT_GREASE_PENCIL; case PaintMode::Invalid: default: return OB_MODE_OBJECT; } } static void paint_init_data(Paint &paint) { const UnifiedPaintSettings &default_ups = UnifiedPaintSettings(); paint.unified_paint_settings.size = default_ups.size; paint.unified_paint_settings.input_samples = default_ups.input_samples; paint.unified_paint_settings.unprojected_size = default_ups.unprojected_size; paint.unified_paint_settings.alpha = default_ups.alpha; paint.unified_paint_settings.weight = default_ups.weight; paint.unified_paint_settings.flag = default_ups.flag; if (!paint.unified_paint_settings.curve_rand_hue) { paint.unified_paint_settings.curve_rand_hue = BKE_paint_default_curve(); } if (!paint.unified_paint_settings.curve_rand_saturation) { paint.unified_paint_settings.curve_rand_saturation = BKE_paint_default_curve(); } if (!paint.unified_paint_settings.curve_rand_value) { paint.unified_paint_settings.curve_rand_value = BKE_paint_default_curve(); } copy_v3_v3(paint.unified_paint_settings.color, default_ups.color); copy_v3_v3(paint.unified_paint_settings.secondary_color, default_ups.secondary_color); } bool BKE_paint_ensure(ToolSettings *ts, Paint **r_paint) { Paint *paint = nullptr; if (*r_paint) { if (!(*r_paint)->runtime) { (*r_paint)->runtime = MEM_new(__func__); } if (!(*r_paint)->runtime->initialized && *r_paint == reinterpret_cast(&ts->imapaint)) { 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->curves_sculpt, (Paint *)&ts->imapaint)); #ifndef NDEBUG Paint paint_test = dna::shallow_copy(**r_paint); paint_runtime_init(ts, *r_paint); /* Swap so debug doesn't hide errors when release fails. */ dna::shallow_swap(**r_paint, paint_test); BLI_assert(paint_test.runtime->ob_mode == (*r_paint)->runtime->ob_mode); #endif } return true; } if ((reinterpret_cast(r_paint) == &ts->vpaint) || (reinterpret_cast(r_paint) == &ts->wpaint)) { VPaint *data = MEM_new(__func__); paint = &data->paint; paint_init_data(*paint); } else if (reinterpret_cast(r_paint) == &ts->sculpt) { Sculpt *data = MEM_new(__func__); paint = &data->paint; paint_init_data(*paint); BKE_paint_mesh_automasking_settings_ensure(*paint); } else if (reinterpret_cast(r_paint) == &ts->gp_paint) { GpPaint *data = MEM_new(__func__); paint = &data->paint; paint_init_data(*paint); } else if (reinterpret_cast(r_paint) == &ts->gp_vertexpaint) { GpVertexPaint *data = MEM_new(__func__); paint = &data->paint; paint_init_data(*paint); } else if (reinterpret_cast(r_paint) == &ts->gp_sculptpaint) { GpSculptPaint *data = MEM_new(__func__); paint = &data->paint; paint_init_data(*paint); } else if (reinterpret_cast(r_paint) == &ts->gp_weightpaint) { GpWeightPaint *data = MEM_new(__func__); paint = &data->paint; paint_init_data(*paint); } else if (reinterpret_cast(r_paint) == &ts->curves_sculpt) { CurvesSculpt *data = MEM_new(__func__); paint = &data->paint; paint_init_data(*paint); } else if (*r_paint == &ts->imapaint.paint) { paint = &ts->imapaint.paint; paint_init_data(*paint); } paint->flags |= PAINT_SHOW_BRUSH; *r_paint = paint; paint_runtime_init(ts, paint); return false; } void BKE_paint_brushes_ensure(Main *bmain, Paint *paint) { if (paint->brush_asset_reference) { paint_brush_update_from_asset_reference(bmain, paint); } if (!paint->brush) { BKE_paint_brush_set_default(bmain, paint); } } void BKE_paint_mesh_automasking_settings_ensure(Paint &paint) { if (!paint.mesh_automasking_settings) { paint.mesh_automasking_settings = MEM_new(__func__); paint.mesh_automasking_settings->cavity_curve = BKE_sculpt_default_cavity_curve(); paint.mesh_automasking_settings->cavity_curve_op = BKE_sculpt_default_cavity_curve(); } } void BKE_paint_init(Main *bmain, Scene *sce, PaintMode mode, const bool ensure_brushes) { BKE_paint_ensure_from_paintmode(sce, mode); Paint *paint = BKE_paint_get_active_from_paintmode(sce, mode); if (ensure_brushes) { BKE_paint_brushes_ensure(bmain, paint); } if (!paint->cavity_curve) { BKE_paint_cavity_curve_preset(paint, CURVE_PRESET_LINE); } if (mode == PaintMode::Sculpt) { BKE_paint_mesh_automasking_settings_ensure(*paint); } } void BKE_paint_free(Paint *paint) { BKE_curvemapping_free(paint->cavity_curve); MEM_delete(paint->brush_asset_reference); MEM_delete(paint->tool_brush_bindings.main_brush_asset_reference); for (NamedBrushAssetReference &brush_ref : paint->tool_brush_bindings.active_brush_per_brush_type.items_mutable()) { MEM_delete(brush_ref.name); MEM_delete(brush_ref.brush_asset_reference); MEM_delete(&brush_ref); } BKE_curvemapping_free(paint->unified_paint_settings.curve_rand_hue); BKE_curvemapping_free(paint->unified_paint_settings.curve_rand_saturation); BKE_curvemapping_free(paint->unified_paint_settings.curve_rand_value); if (paint->mesh_automasking_settings) { BKE_curvemapping_free(paint->mesh_automasking_settings->cavity_curve); BKE_curvemapping_free(paint->mesh_automasking_settings->cavity_curve_op); MEM_delete(paint->mesh_automasking_settings); } MEM_SAFE_DELETE(paint->runtime); } void BKE_paint_copy(const Paint *src, Paint *dst, const int flag) { dst->brush = src->brush; dst->cavity_curve = BKE_curvemapping_copy(src->cavity_curve); if (src->brush_asset_reference) { dst->brush_asset_reference = MEM_new(__func__, *src->brush_asset_reference); } if (src->tool_brush_bindings.main_brush_asset_reference) { dst->tool_brush_bindings.main_brush_asset_reference = MEM_new( __func__, *src->tool_brush_bindings.main_brush_asset_reference); } BLI_duplicatelist(&dst->tool_brush_bindings.active_brush_per_brush_type, &src->tool_brush_bindings.active_brush_per_brush_type); for (NamedBrushAssetReference &brush_ref : dst->tool_brush_bindings.active_brush_per_brush_type) { brush_ref.name = BLI_strdup(brush_ref.name); brush_ref.brush_asset_reference = MEM_new( __func__, *brush_ref.brush_asset_reference); } dst->unified_paint_settings.curve_rand_hue = BKE_curvemapping_copy( src->unified_paint_settings.curve_rand_hue); dst->unified_paint_settings.curve_rand_saturation = BKE_curvemapping_copy( src->unified_paint_settings.curve_rand_saturation); dst->unified_paint_settings.curve_rand_value = BKE_curvemapping_copy( src->unified_paint_settings.curve_rand_value); if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) { id_us_plus(id_cast(dst->palette)); } if (src->mesh_automasking_settings) { dst->mesh_automasking_settings = MEM_new( __func__, dna::shallow_copy(*src->mesh_automasking_settings)); dst->mesh_automasking_settings->cavity_curve = BKE_curvemapping_copy( src->mesh_automasking_settings->cavity_curve); dst->mesh_automasking_settings->cavity_curve_op = BKE_curvemapping_copy( src->mesh_automasking_settings->cavity_curve_op); } dst->runtime = MEM_new(__func__); if (src->runtime) { dst->runtime->paint_mode = src->runtime->paint_mode; dst->runtime->ob_mode = src->runtime->ob_mode; dst->runtime->initialized = true; } } void BKE_paint_settings_foreach_mode(ToolSettings *ts, FunctionRef fn) { if (ts->vpaint) { fn(reinterpret_cast(ts->vpaint)); } if (ts->wpaint) { fn(reinterpret_cast(ts->wpaint)); } if (ts->sculpt) { fn(reinterpret_cast(ts->sculpt)); } if (ts->gp_paint) { fn(reinterpret_cast(ts->gp_paint)); } if (ts->gp_vertexpaint) { fn(reinterpret_cast(ts->gp_vertexpaint)); } if (ts->gp_sculptpaint) { fn(reinterpret_cast(ts->gp_sculptpaint)); } if (ts->gp_weightpaint) { fn(reinterpret_cast(ts->gp_weightpaint)); } if (ts->curves_sculpt) { fn(reinterpret_cast(ts->curves_sculpt)); } fn(reinterpret_cast(&ts->imapaint)); } void BKE_paint_stroke_get_average(const Paint *paint, const Object *ob, float stroke[3]) { const bke::PaintRuntime &paint_runtime = *paint->runtime; if (paint_runtime.last_stroke_valid && paint_runtime.average_stroke_counter > 0) { float fac = 1.0f / paint_runtime.average_stroke_counter; mul_v3_v3fl(stroke, paint_runtime.average_stroke_accum, fac); } else { copy_v3_v3(stroke, ob->object_to_world().location()); } } float3 BKE_paint_randomize_color(const BrushColorJitterSettings &color_jitter, const float3 &initial_hsv_jitter, const float distance, const float pressure, const float3 &color) { constexpr float noise_scale = 1 / 20.0f; const float random_hue = (color_jitter.flag & BRUSH_COLOR_JITTER_USE_HUE_AT_STROKE) ? initial_hsv_jitter[0] : noise::perlin( float2(distance * noise_scale, initial_hsv_jitter[0] * 100)); const float random_sat = (color_jitter.flag & BRUSH_COLOR_JITTER_USE_SAT_AT_STROKE) ? initial_hsv_jitter[1] : noise::perlin( float2(distance * noise_scale, initial_hsv_jitter[1] * 100)); const float random_val = (color_jitter.flag & BRUSH_COLOR_JITTER_USE_VAL_AT_STROKE) ? initial_hsv_jitter[2] : noise::perlin( float2(distance * noise_scale, initial_hsv_jitter[2] * 100)); float hue_jitter_scale = color_jitter.hue; if (color_jitter.flag & BRUSH_COLOR_JITTER_USE_HUE_RAND_PRESS) { hue_jitter_scale *= BKE_curvemapping_evaluateF(color_jitter.curve_hue_jitter, 0, pressure); } float sat_jitter_scale = color_jitter.saturation; if (color_jitter.flag & BRUSH_COLOR_JITTER_USE_SAT_RAND_PRESS) { sat_jitter_scale *= BKE_curvemapping_evaluateF(color_jitter.curve_sat_jitter, 0, pressure); } float val_jitter_scale = color_jitter.value; if (color_jitter.flag & BRUSH_COLOR_JITTER_USE_VAL_RAND_PRESS) { val_jitter_scale *= BKE_curvemapping_evaluateF(color_jitter.curve_val_jitter, 0, pressure); } float3 hsv; rgb_to_hsv_v(color, hsv); hsv[0] += math::interpolate(0.5f, random_hue, hue_jitter_scale) - 0.5f; /* Wrap hue. */ if (hsv[0] > 1.0f) { hsv[0] -= 1.0f; } else if (hsv[0] < 0.0f) { hsv[0] += 1.0f; } hsv[1] *= math::interpolate(1.0f, random_sat * 2.0f, sat_jitter_scale); hsv[2] *= math::interpolate(1.0f, random_val * 2.0f, val_jitter_scale); float3 random_color; hsv_to_rgb_v(hsv, random_color); return random_color; } void BKE_paint_blend_write(BlendWriter *writer, Paint *paint) { if (paint->cavity_curve) { BKE_curvemapping_blend_write(writer, paint->cavity_curve); } if (paint->brush_asset_reference) { BKE_asset_weak_reference_write(writer, paint->brush_asset_reference); } { /* Write tool system bindings. */ ToolSystemBrushBindings &tool_brush_bindings = paint->tool_brush_bindings; if (tool_brush_bindings.main_brush_asset_reference) { BKE_asset_weak_reference_write(writer, tool_brush_bindings.main_brush_asset_reference); } writer->write_struct_list(&tool_brush_bindings.active_brush_per_brush_type); for (NamedBrushAssetReference &brush_ref : tool_brush_bindings.active_brush_per_brush_type) { writer->write_string(brush_ref.name); if (brush_ref.brush_asset_reference) { BKE_asset_weak_reference_write(writer, brush_ref.brush_asset_reference); } } } if (paint->unified_paint_settings.curve_rand_hue) { BKE_curvemapping_blend_write(writer, paint->unified_paint_settings.curve_rand_hue); } if (paint->unified_paint_settings.curve_rand_saturation) { BKE_curvemapping_blend_write(writer, paint->unified_paint_settings.curve_rand_saturation); } if (paint->unified_paint_settings.curve_rand_value) { BKE_curvemapping_blend_write(writer, paint->unified_paint_settings.curve_rand_value); } if (paint->mesh_automasking_settings) { writer->write_struct(paint->mesh_automasking_settings); MeshAutomaskingSettings &automasking_settings = *paint->mesh_automasking_settings; if (automasking_settings.cavity_curve) { BKE_curvemapping_blend_write(writer, automasking_settings.cavity_curve); } if (automasking_settings.cavity_curve_op) { BKE_curvemapping_blend_write(writer, automasking_settings.cavity_curve_op); } } } void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *paint) { BLO_read_struct(reader, CurveMapping, &paint->cavity_curve); if (paint->cavity_curve) { BKE_curvemapping_blend_read(reader, paint->cavity_curve); } else { BKE_paint_cavity_curve_preset(paint, CURVE_PRESET_LINE); } BLO_read_struct(reader, AssetWeakReference, &paint->brush_asset_reference); if (paint->brush_asset_reference) { BKE_asset_weak_reference_read(reader, paint->brush_asset_reference); } { /* Read tool system bindings. */ ToolSystemBrushBindings &tool_brush_bindings = paint->tool_brush_bindings; BLO_read_struct(reader, AssetWeakReference, &tool_brush_bindings.main_brush_asset_reference); if (tool_brush_bindings.main_brush_asset_reference) { BKE_asset_weak_reference_read(reader, tool_brush_bindings.main_brush_asset_reference); } BLO_read_struct_list( reader, NamedBrushAssetReference, &tool_brush_bindings.active_brush_per_brush_type); for (NamedBrushAssetReference &brush_ref : tool_brush_bindings.active_brush_per_brush_type) { BLO_read_string(reader, &brush_ref.name); BLO_read_struct(reader, AssetWeakReference, &brush_ref.brush_asset_reference); if (brush_ref.brush_asset_reference) { BKE_asset_weak_reference_read(reader, brush_ref.brush_asset_reference); } } } UnifiedPaintSettings *ups = &paint->unified_paint_settings; BLO_read_struct(reader, CurveMapping, &ups->curve_rand_hue); if (ups->curve_rand_hue) { BKE_curvemapping_blend_read(reader, ups->curve_rand_hue); BKE_curvemapping_init(ups->curve_rand_hue); } BLO_read_struct(reader, CurveMapping, &ups->curve_rand_saturation); if (ups->curve_rand_saturation) { BKE_curvemapping_blend_read(reader, ups->curve_rand_saturation); BKE_curvemapping_init(ups->curve_rand_saturation); } BLO_read_struct(reader, CurveMapping, &ups->curve_rand_value); if (ups->curve_rand_value) { BKE_curvemapping_blend_read(reader, ups->curve_rand_value); BKE_curvemapping_init(ups->curve_rand_value); } BLO_read_struct(reader, MeshAutomaskingSettings, &paint->mesh_automasking_settings); if (paint->mesh_automasking_settings) { MeshAutomaskingSettings &automasking_settings = *paint->mesh_automasking_settings; BLO_read_struct(reader, CurveMapping, &automasking_settings.cavity_curve); if (automasking_settings.cavity_curve) { BKE_curvemapping_blend_read(reader, automasking_settings.cavity_curve); BKE_curvemapping_init(automasking_settings.cavity_curve); } BLO_read_struct(reader, CurveMapping, &automasking_settings.cavity_curve_op); if (automasking_settings.cavity_curve_op) { BKE_curvemapping_blend_read(reader, automasking_settings.cavity_curve_op); BKE_curvemapping_init(automasking_settings.cavity_curve_op); } } paint->runtime = MEM_new(__func__); paint_runtime_init(scene->toolsettings, paint); } bool paint_is_grid_face_hidden(const BoundedBitSpan grid_hidden, const int gridsize, const int x, const int y) { return grid_hidden[CCG_grid_xy_to_index(gridsize, x, y)] || grid_hidden[CCG_grid_xy_to_index(gridsize, x + 1, y)] || grid_hidden[CCG_grid_xy_to_index(gridsize, x + 1, y + 1)] || grid_hidden[CCG_grid_xy_to_index(gridsize, x, y + 1)]; } bool paint_is_bmesh_face_hidden(const 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; } namespace bke::paint { bool supports_scene_size(const PaintMode paint_mode) { switch (paint_mode) { case PaintMode::Sculpt: return true; case PaintMode::Vertex: case PaintMode::Weight: case PaintMode::Texture3D: return false; case PaintMode::GPencil: case PaintMode::VertexGPencil: case PaintMode::SculptGPencil: case PaintMode::WeightGPencil: return true; case PaintMode::SculptCurves: return false; case PaintMode::Texture2D: return false; case PaintMode::Invalid: BLI_assert_unreachable(); return false; } BLI_assert_unreachable(); return false; } bool supports_symmetry_tiling(const PaintMode paint_mode) { switch (paint_mode) { case PaintMode::Sculpt: return true; case PaintMode::Vertex: case PaintMode::Weight: case PaintMode::Texture3D: return false; case PaintMode::GPencil: case PaintMode::VertexGPencil: case PaintMode::SculptGPencil: case PaintMode::WeightGPencil: return false; case PaintMode::SculptCurves: return false; case PaintMode::Texture2D: return false; case PaintMode::Invalid: BLI_assert_unreachable(); return false; } BLI_assert_unreachable(); return false; } } // namespace bke::paint float paint_grid_paint_mask(const GridPaintMask *gpm, uint level, uint x, uint y) { int factor = CCG_grid_factor(level, gpm->level); int gridsize = CCG_grid_size(gpm->level); return gpm->data[(y * factor) * gridsize + (x * factor)]; } /* Threshold to move before updating the brush rotation, reduces jitter. */ static float paint_rake_rotation_spacing(const Paint & /*ups*/, const Brush &brush) { return brush.sculpt_brush_type == SCULPT_BRUSH_TYPE_CLAY_STRIPS ? 1.0f : 20.0f; } void paint_update_brush_rake_rotation(Paint &paint, const Brush &brush, float rotation) { bke::PaintRuntime &paint_runtime = *paint.runtime; paint_runtime.brush_rotation = rotation; if (brush.mask_mtex.brush_angle_mode & MTEX_ANGLE_RAKE) { paint_runtime.brush_rotation_sec = rotation; } else { paint_runtime.brush_rotation_sec = 0.0f; } } static bool paint_rake_rotation_active(const MTex &mtex) { return mtex.tex && mtex.brush_angle_mode & MTEX_ANGLE_RAKE; } static bool paint_rake_rotation_active(const Brush &brush, PaintMode paint_mode) { return paint_rake_rotation_active(brush.mtex) || paint_rake_rotation_active(brush.mask_mtex) || BKE_brush_has_cube_tip(&brush, paint_mode); } bool paint_calculate_rake_rotation(Paint &paint, const Brush &brush, const float mouse_pos[2], const PaintMode paint_mode, bool stroke_has_started) { bke::PaintRuntime &paint_runtime = *paint.runtime; bool ok = false; if (paint_rake_rotation_active(brush, paint_mode)) { float r = paint_rake_rotation_spacing(paint, brush); float rotation; /* Use a smaller limit if the stroke hasn't started to prevent excessive pre-roll. */ if (!stroke_has_started) { r = min_ff(r, 4.0f); } float dpos[2]; sub_v2_v2v2(dpos, mouse_pos, paint_runtime.last_rake); /* Limit how often we update the angle to prevent jitter. */ if (len_squared_v2(dpos) >= r * r) { rotation = atan2f(dpos[1], dpos[0]) + float(0.5f * M_PI); copy_v2_v2(paint_runtime.last_rake, mouse_pos); paint_runtime.last_rake_angle = rotation; paint_update_brush_rake_rotation(paint, 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(paint, brush, paint_runtime.last_rake_angle); ok = false; } } else { paint_runtime.brush_rotation = paint_runtime.brush_rotation_sec = 0.0f; ok = true; } return ok; } void BKE_sculptsession_free_deformMats(SculptSession *ss) { ss->deform_cos = {}; ss->deform_imats = {}; ss->vert_normals_deform = {}; ss->face_normals_deform = {}; } /** * Write out the sculpt dynamic-topology #BMesh to the #Mesh. */ static void sculptsession_bm_to_me_update_data_only(Object *ob) { SculptSession &ss = *ob->runtime->sculpt_session; if (ss.bm) { if (ob->data) { BMeshToMeshParams params{}; params.calc_object_remap = false; BM_mesh_bm_to_me(nullptr, ss.bm, id_cast(ob->data), ¶ms); } } } void BKE_sculptsession_bm_to_me(Object *ob) { if (ob && ob->runtime->sculpt_session) { sculptsession_bm_to_me_update_data_only(ob); /* Ensure the objects evaluated mesh doesn't hold onto arrays * now realloc'd in the mesh #34473. */ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); } } void BKE_sculptsession_free_pbvh(Object &object) { SculptSession *ss = object.runtime->sculpt_session; if (!ss) { return; } ss->pbvh.reset(); ss->edge_to_face_offsets = {}; ss->edge_to_face_indices = {}; ss->edge_to_face_map = {}; ss->vert_to_edge_offsets = {}; ss->vert_to_edge_indices = {}; ss->vert_to_edge_map = {}; ss->preview_verts = {}; ss->boundary_info_cache.reset(); ss->fake_neighbors.fake_neighbor_index = {}; ss->topology_island_cache.reset(); ss->clear_active_elements(false); } void BKE_sculptsession_bm_to_me_for_render(Object *object) { if (object && object->runtime->sculpt_session) { if (object->runtime->sculpt_session->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); /* 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->runtime->sculpt_session) { SculptSession *ss = ob->runtime->sculpt_session; if (ss->bm) { BKE_sculptsession_bm_to_me(ob); BM_mesh_free(ss->bm); } BKE_sculptsession_free_pbvh(*ob); MEM_delete(ss); ob->runtime->sculpt_session = nullptr; } } SculptSession::SculptSession() = default; SculptSession::~SculptSession() { if (this->bm_log) { BM_log_free(this->bm_log); } if (this->tex_pool) { BKE_image_pool_free(this->tex_pool); } } ActiveVert SculptSession::active_vert() const { return active_vert_; } ActiveVert SculptSession::last_active_vert() const { return last_active_vert_; } int SculptSession::active_vert_index() const { if (std::holds_alternative(active_vert_)) { return std::get(active_vert_); } if (std::holds_alternative(active_vert_)) { BMVert *bm_vert = std::get(active_vert_); return BM_elem_index_get(bm_vert); } return -1; } int SculptSession::last_active_vert_index() const { if (std::holds_alternative(last_active_vert_)) { return std::get(last_active_vert_); } if (std::holds_alternative(last_active_vert_)) { BMVert *bm_vert = std::get(last_active_vert_); return BM_elem_index_get(bm_vert); } return -1; } float3 SculptSession::active_vert_position(const Depsgraph &depsgraph, const Object &object) const { if (std::holds_alternative(active_vert_)) { if (this->subdiv_ccg) { return this->subdiv_ccg->positions[std::get(active_vert_)]; } const Span positions = bke::pbvh::vert_positions_eval(depsgraph, object); return positions[std::get(active_vert_)]; } if (std::holds_alternative(active_vert_)) { BMVert *bm_vert = std::get(active_vert_); return bm_vert->co; } BLI_assert_unreachable(); return float3(std::numeric_limits::infinity()); } void SculptSession::clear_active_elements(bool persist_last_active) { if (persist_last_active) { if (!std::holds_alternative(active_vert_)) { last_active_vert_ = active_vert_; } } else { last_active_vert_ = {}; } active_vert_ = {}; active_grid_index.reset(); active_face_index.reset(); } void SculptSession::set_active_vert(const ActiveVert vert) { active_vert_ = vert; } std::optional SculptSession::persistent_multires_data() { BLI_assert(subdiv_ccg); if (persistent.grids_num == -1 || persistent.grid_size == -1) { return std::nullopt; } if (this->subdiv_ccg->grids_num != persistent.grids_num || this->subdiv_ccg->grid_size != persistent.grid_size) { return std::nullopt; } return PersistentMultiresData{persistent.sculpt_persistent_co, persistent.sculpt_persistent_no, persistent.sculpt_persistent_disp}; } static MultiresModifierData *sculpt_multires_modifier_get(const Scene *scene, Object *ob, const bool auto_create_mdisps) { Mesh &mesh = *id_cast(ob->data); if (ob->runtime->sculpt_session && ob->runtime->sculpt_session->bm) { /* Can't combine multires and dynamic topology. */ return nullptr; } bool need_mdisps = false; if (!CustomData_get_layer(&mesh.corner_data, CD_MDISPS)) { if (!auto_create_mdisps) { /* Multires can't work without displacement layer. */ return nullptr; } need_mdisps = true; } /* Weight paint operates on original vertices, and needs to treat multires as regular modifier * to make it so that pbvh::Tree vertices are at the multires surface. */ if ((ob->mode & OB_MODE_SCULPT) == 0) { return nullptr; } VirtualModifierData virtual_modifier_data; for (ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtual_modifier_data); md; md = md->next) { if (md->type == eModifierType_Multires) { MultiresModifierData *mmd = reinterpret_cast(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(&mesh.corner_data, CD_MDISPS, CD_SET_DEFAULT, mesh.corners_num); } return mmd; } return nullptr; } } return nullptr; } MultiresModifierData *BKE_sculpt_multires_active(const Scene *scene, Object *ob) { return sculpt_multires_modifier_get(scene, ob, false); } int BKE_sculpt_get_grid_num_verts(const Object &object) { const SculptSession &ss = *object.runtime->sculpt_session; BLI_assert(bke::object::pbvh_get(object)->type() == bke::pbvh::Type::Grids); const CCGKey key = BKE_subdiv_ccg_key_top_level(*ss.subdiv_ccg); return ss.subdiv_ccg->grids_num * key.grid_area; } int BKE_sculpt_get_grid_num_faces(const Object &object) { const SculptSession &ss = *object.runtime->sculpt_session; BLI_assert(bke::object::pbvh_get(object)->type() == bke::pbvh::Type::Grids); const CCGKey key = BKE_subdiv_ccg_key_top_level(*ss.subdiv_ccg); return ss.subdiv_ccg->grids_num * square_i(key.grid_size - 1); } /* Checks if there are any supported deformation modifiers active */ static bool sculpt_modifiers_active(const Scene *scene, const Sculpt *sd, Object *ob) { const Mesh &mesh = *id_cast(ob->data); if (ob->runtime->sculpt_session->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 && mesh.key && ob->shapenr) { return true; } VirtualModifierData virtual_modifier_data; for (ModifierData *md = BKE_modifiers_get_virtual_modifierlist(ob, &virtual_modifier_data); md; md = md->next) { const ModifierTypeInfo *mti = BKE_modifier_get_info(md->type); if (!BKE_modifier_is_enabled(scene, md, eModifierMode_Realtime)) { continue; } if (md->type == eModifierType_Multires && (ob->mode & OB_MODE_SCULPT)) { MultiresModifierData *mmd = reinterpret_cast(md); if (!(mmd->flags & eMultiresModifierFlag_UseSculptBaseMesh)) { continue; } } /* Exception for shape keys because we can edit those. */ if (md->type == eModifierType_ShapeKey) { continue; } if (mti->type == ModifierTypeType::OnlyDeform) { return true; } if (sd == nullptr || (sd->flags & SCULPT_ONLY_DEFORM) == 0) { return true; } } return false; } static void sculpt_update_object(Depsgraph *depsgraph, Object *ob, Object *ob_eval, bool is_paint_tool) { using namespace blender::bke; Scene *scene = DEG_get_input_scene(depsgraph); Sculpt *sd = scene->toolsettings->sculpt; SculptSession &ss = *ob->runtime->sculpt_session; Mesh *mesh_orig = BKE_object_get_original_mesh(ob); /* Use the "unchecked" function, because this code also runs as part of the depsgraph node that * evaluates the object's geometry. So from perspective of the depsgraph, the mesh is not fully * evaluated yet. */ Mesh *mesh_eval = BKE_object_get_evaluated_mesh_unchecked(ob_eval); MultiresModifierData *mmd = sculpt_multires_modifier_get(scene, ob, true); BLI_assert(mesh_eval != nullptr); /* This is for handling a newly opened file with no object visible, * causing `mesh_eval == nullptr`. */ if (mesh_eval == nullptr) { return; } ss.deform_modifiers_active = sculpt_modifiers_active(scene, sd, ob); ss.shapekey_active = (mmd == nullptr && ss.bm == nullptr) ? BKE_keyblock_from_object(ob) : nullptr; ss.multires_modifier = mmd; ss.subdiv_ccg = mesh_eval->runtime->subdiv_ccg.get(); pbvh::Tree &pbvh = object::pbvh_ensure(*depsgraph, *ob); 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)) { const Mesh *me_eval_deform = BKE_object_get_mesh_deform_eval(ob_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->faces_num == mesh_eval->faces_num && me_eval_deform->corners_num == mesh_eval->corners_num && me_eval_deform->verts_num == mesh_eval->verts_num) { BKE_sculptsession_free_deformMats(&ss); BLI_assert(me_eval_deform->verts_num == mesh_orig->verts_num); ss.deform_cos = mesh_eval->vert_positions(); BKE_pbvh_vert_coords_apply(pbvh, ss.deform_cos); used_me_eval = true; } } /* We depend on the deform coordinates not being updated in the middle of a stroke. This array * eventually gets cleared inside BKE_sculpt_update_object_before_eval. * See #126713 for more information. */ if (ss.deform_cos.is_empty() && !used_me_eval) { BKE_sculptsession_free_deformMats(&ss); BKE_crazyspace_build_sculpt(depsgraph, scene, ob, ss.deform_imats, ss.deform_cos); BKE_pbvh_vert_coords_apply(pbvh, ss.deform_cos); for (float3x3 &matrix : ss.deform_imats) { matrix = math::invert(matrix); } } } else { BKE_sculptsession_free_deformMats(&ss); } if (ss.shapekey_active != nullptr && ss.deform_cos.is_empty()) { ss.deform_cos = Span(static_cast(ss.shapekey_active->data), mesh_orig->verts_num); if (!ss.deform_cos.is_empty()) { BKE_pbvh_vert_coords_apply(pbvh, ss.deform_cos); } } 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 (USER_EXPERIMENTAL_TEST(&U, use_sculpt_texture_paint)) { std::string paint_canvas_key = BKE_paint_canvas_key_get(&scene->toolsettings->paint_mode, ob); if (!ss.last_paint_canvas_key || paint_canvas_key != ss.last_paint_canvas_key) { ss.last_paint_canvas_key = paint_canvas_key; BKE_pbvh_mark_rebuild_pixels(pbvh); } } /* 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); } } /* This solves a crash when running a sculpt brush in background mode, because there is no redraw * after entering sculpt mode to make sure normals are allocated. Recalculating normals with * every brush step is too expensive currently. */ bke::pbvh::update_normals(*depsgraph, *ob, pbvh); } void BKE_sculpt_update_object_before_eval(Object *ob_eval) { /* Update before mesh evaluation in the dependency graph. */ Object *ob_orig = DEG_get_original(ob_eval); SculptSession *ss = ob_orig->runtime->sculpt_session; if (!ss) { return; } bke::pbvh::Tree *pbvh = bke::object::pbvh_get(*ob_orig); if (!ss->cache && !ss->filter_cache && !ss->expand_cache) { /* Avoid performing the following normal update for Multires, as it causes race conditions * and other intermittent crashes with shared meshes. * See !125268 and #125157 for more information. */ if (pbvh && pbvh->type() != bke::pbvh::Type::Grids) { /* pbvh::Tree nodes may contain dirty normal tags. To avoid losing that information when * the pbvh::Tree is deleted, make sure all tagged geometry normals are up to date. * See #122947 for more information. */ bke::pbvh::update_normals_from_eval(*ob_eval, *pbvh); } /* 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. */ BKE_sculptsession_free_pbvh(*ob_orig); BKE_sculptsession_free_deformMats(ss); } else if (pbvh) { IndexMaskMemory memory; const IndexMask node_mask = bke::pbvh::all_leaf_nodes(*pbvh, memory); pbvh->tag_positions_changed(node_mask); BKE_pbvh_mark_rebuild_pixels(*pbvh); } } void BKE_sculpt_update_object_after_eval(Depsgraph *depsgraph, Object *ob_eval) { /* Update after mesh evaluation in the dependency graph, to rebuild pbvh::Tree or * other data when modifiers change the mesh. */ Object *ob_orig = DEG_get_original(ob_eval); sculpt_update_object(depsgraph, ob_orig, ob_eval, false); } void BKE_sculpt_color_layer_create_if_needed(Object *object) { using namespace blender::bke; Mesh *orig_me = BKE_object_get_original_mesh(object); if (BKE_id_attributes_color_find(&orig_me->id, orig_me->active_color_attribute)) { return; } AttributeOwner owner = AttributeOwner::from_id(&orig_me->id); const std::string unique_name = BKE_attribute_calc_unique_name(owner, "Color"); if (!orig_me->attributes_for_write().add( unique_name, AttrDomain::Point, AttrType::ColorFloat, AttributeInitDefaultValue())) { return; } BKE_id_attributes_active_color_set(&orig_me->id, unique_name); BKE_id_attributes_default_color_set(&orig_me->id, unique_name); DEG_id_tag_update(&orig_me->id, ID_RECALC_GEOMETRY_ALL_MODES); BKE_mesh_tessface_clear(orig_me); } void BKE_sculpt_update_object_for_edit(Depsgraph *depsgraph, Object *ob_orig, bool is_paint_tool) { PRF_scope(ProfileCategory::Editor); BLI_assert(ob_orig == DEG_get_original(ob_orig)); Object *ob_eval = DEG_get_evaluated(depsgraph, ob_orig); sculpt_update_object(depsgraph, ob_orig, ob_eval, is_paint_tool); } void BKE_sculpt_mask_layers_ensure(Depsgraph *depsgraph, Main *bmain, Object *ob, MultiresModifierData *mmd) { using namespace blender::bke; Mesh *mesh = id_cast(ob->data); const OffsetIndices faces = mesh->faces(); const Span corner_verts = mesh->corner_verts(); MutableAttributeAccessor attributes = mesh->attributes_for_write(); /* if multires is active, create a grid paint mask layer if there * isn't one already */ if (mmd && !CustomData_has_layer(&mesh->corner_data, CD_GRID_PAINT_MASK)) { int level = max_ii(1, mmd->sculptlvl); int gridsize = CCG_grid_size(level); int gridarea = gridsize * gridsize; GridPaintMask *gmask = static_cast(CustomData_add_layer( &mesh->corner_data, CD_GRID_PAINT_MASK, CD_SET_DEFAULT, mesh->corners_num)); for (int i = 0; i < mesh->corners_num; i++) { GridPaintMask *gpm = &gmask[i]; gpm->level = level; gpm->data = MEM_new_array_zeroed(gridarea, "GridPaintMask.data"); } /* If vertices already have mask, copy into multires data. */ if (const VArray mask = *attributes.lookup(".sculpt_mask", AttrDomain::Point)) { const VArraySpan mask_span(mask); for (const int i : faces.index_range()) { const IndexRange face = faces[i]; /* Mask center. */ float avg = 0.0f; for (const int vert : corner_verts.slice(face)) { avg += mask_span[vert]; } avg /= float(face.size()); /* Fill in multires mask corner. */ for (const int corner : face) { GridPaintMask *gpm = &gmask[corner]; const int vert = corner_verts[corner]; const int prev = corner_verts[mesh::face_corner_prev(face, corner)]; const int next = corner_verts[mesh::face_corner_next(face, corner)]; gpm->data[0] = avg; gpm->data[1] = (mask_span[vert] + mask_span[next]) * 0.5f; gpm->data[2] = (mask_span[vert] + mask_span[prev]) * 0.5f; gpm->data[3] = mask_span[vert]; } } } /* The evaluated multires CCG must be updated to contain the new data. */ DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY); if (depsgraph) { BKE_scene_graph_evaluated_ensure(depsgraph, bmain); } } else { attributes.add(".sculpt_mask", AttrDomain::Point, AttributeInitDefaultValue()); } } void BKE_sculpt_toolsettings_data_ensure(Main *bmain, Scene *scene) { BKE_paint_init(bmain, scene, PaintMode::Sculpt, true); Sculpt *sd = scene->toolsettings->sculpt; const Sculpt defaults = {}; /* We have file versioning code here for historical * reasons. Don't add more checks here, do it properly * in blenloader. */ if (sd->detail_percent == 0.0f) { sd->detail_percent = defaults.detail_percent; } if (sd->constant_detail == 0.0f) { sd->constant_detail = defaults.constant_detail; } if (sd->detail_size == 0.0f) { sd->detail_size = defaults.detail_size; } /* 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::Tree and sculpt "layer" levels, so use pbvh::Tree only for internal brush * stuff and show final evaluated mesh so user would see actual object shape. */ deformed |= object->runtime->sculpt_session->deform_modifiers_active; if (for_construction) { deformed |= object->runtime->sculpt_session->shapekey_active != nullptr; } else { /* As in case with modifiers, we can't synchronize deformation made against * pbvh::Tree and non-locked keyblock, so also use pbvh::Tree only for brushes and * final DM to give final result to user. */ deformed |= object->runtime->sculpt_session->shapekey_active && (object->shapeflag & OB_SHAPE_LOCK) == 0; } return deformed; } void BKE_sculpt_sync_face_visibility_to_grids(const Mesh &mesh, SubdivCCG &subdiv_ccg) { using namespace blender::bke; const AttributeAccessor attributes = mesh.attributes(); const VArray hide_poly = *attributes.lookup_or_default( ".hide_poly", AttrDomain::Face, false); if (hide_poly.is_single() && !hide_poly.get_internal_single()) { BKE_subdiv_ccg_grid_hidden_free(subdiv_ccg); return; } const OffsetIndices faces = mesh.faces(); const VArraySpan hide_poly_span(hide_poly); BitGroupVector<> &grid_hidden = BKE_subdiv_ccg_grid_hidden_ensure(subdiv_ccg); threading::parallel_for(faces.index_range(), 1024, [&](const IndexRange range) { for (const int i : range) { const bool face_hidden = hide_poly_span[i]; for (const int corner : faces[i]) { grid_hidden[corner].set_all(face_hidden); } } }); } namespace bke { static std::unique_ptr build_pbvh_for_dynamic_topology(Object *ob) { BMesh &bm = *ob->runtime->sculpt_session->bm; BM_data_layer_ensure_named(&bm, &bm.vdata, CD_PROP_INT32, ".sculpt_dyntopo_node_id_vertex"); BM_data_layer_ensure_named(&bm, &bm.pdata, CD_PROP_INT32, ".sculpt_dyntopo_node_id_face"); return std::make_unique(pbvh::Tree::from_bmesh(bm)); } static std::unique_ptr build_pbvh_from_regular_mesh(Object *ob, const Mesh *me_eval_deform) { const Mesh &mesh = *BKE_object_get_original_mesh(ob); std::unique_ptr pbvh = std::make_unique(pbvh::Tree::from_mesh(mesh)); const bool is_deformed = check_sculpt_object_deformed(ob, true); if (is_deformed && me_eval_deform != nullptr) { BKE_pbvh_vert_coords_apply(*pbvh, me_eval_deform->vert_positions()); } return pbvh; } static std::unique_ptr build_pbvh_from_ccg(Object *ob, SubdivCCG &subdiv_ccg) { const Mesh &base_mesh = *BKE_mesh_from_object(ob); BKE_sculpt_sync_face_visibility_to_grids(base_mesh, subdiv_ccg); return std::make_unique(pbvh::Tree::from_grids(base_mesh, subdiv_ccg)); } } // namespace bke namespace bke::object { pbvh::Tree &pbvh_ensure(Depsgraph &depsgraph, Object &object) { if (pbvh::Tree *pbvh = pbvh_get(object)) { return *pbvh; } BLI_assert(object.runtime->sculpt_session != nullptr); SculptSession &ss = *object.runtime->sculpt_session; if (ss.bm != nullptr) { /* Sculpting on a BMesh (dynamic-topology) gets a special pbvh::Tree. */ ss.pbvh = build_pbvh_for_dynamic_topology(&object); } else { Object *object_eval = DEG_get_evaluated(&depsgraph, &object); Mesh *mesh_eval = id_cast(object_eval->data); if (mesh_eval->runtime->subdiv_ccg != nullptr) { ss.pbvh = build_pbvh_from_ccg(&object, *mesh_eval->runtime->subdiv_ccg); } else { const Mesh *me_eval_deform = BKE_object_get_mesh_deform_eval(object_eval); ss.pbvh = build_pbvh_from_regular_mesh(&object, me_eval_deform); } } return *object::pbvh_get(object); } const pbvh::Tree *pbvh_get(const Object &object) { if (!object.runtime->sculpt_session) { return nullptr; } return object.runtime->sculpt_session->pbvh.get(); } pbvh::Tree *pbvh_get(Object &object) { BLI_assert(object.type == OB_MESH); if (!object.runtime->sculpt_session) { return nullptr; } return object.runtime->sculpt_session->pbvh.get(); } } // namespace bke::object bool BKE_object_sculpt_use_dyntopo(const Object *object) { return object->runtime->sculpt_session && object->runtime->sculpt_session->bm; } bool BKE_sculptsession_use_pbvh_draw(const Object *ob, const RegionView3D *rv3d) { SculptSession *ss = ob->runtime->sculpt_session; if (ss == nullptr || ss->mode_type != OB_MODE_SCULPT) { return false; } const bke::pbvh::Tree *pbvh = bke::object::pbvh_get(*ob); if (!pbvh) { return false; } /* External render engines like Cycles do not have access to the pbvh::Tree * like Eevee does, and need evaluated mesh geometry to render from. */ const bool external_engine = rv3d && rv3d->view_render != nullptr; if (pbvh->type() == bke::pbvh::Type::Mesh) { /* Regular mesh only draws from pbvh::Tree without modifiers and shape keys, * and without external render engine. */ return !(ss->shapekey_active || ss->deform_modifiers_active || external_engine); } if (pbvh->type() == bke::pbvh::Type::BMesh) { /* Dyntopo draws from pbvh::Tree, except for external render engines. */ return !external_engine; } /* Multires always draws directly from the pbvh::Tree. */ 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); } } // namespace blender