Sculpt/Paint: Bring back support for multiple brush based tools
Design: https://projects.blender.org/blender/blender/issues/126032 The brush assets project merged all brush based tools into a single, "Brush" tool. After feedback, we want to bring back some of the previous brush based tools. For example in grease pencil draw mode, not having an eraser or fill tool available, and having to go through all the brush assets instead made the workflow more cumbersome, and features less discoverable (an eraser tool in the toolbar is quite easy to find, a brush deep down in the asset library may not be). With this commit we can add back some tools for specific brush types in sculpt & paint modes. The follow up commit will start with the eraser and fill tools for grease pencil draw mode, but more tools in other modes are expected to follow. For every brush type that has a tool in the toolbar, the last used brush is remembered. This is the biggest part of the changes here. Brush asset popups will only show the brushes supported by the active tool for now. The permanent asset shelf region displays all brushes. Activating a brush that isn't compatible with the current tool will also activate the general "Brush" tool, but while the brush tool is active we never switch to another one (e.g. activating an eraser brush will keep the "Brush" tool active). All this might change after further feedback. Pull Request: https://projects.blender.org/blender/blender/pulls/125449
This commit is contained in:
@@ -10,17 +10,48 @@ class BrushAssetShelf:
|
||||
bl_options = {'DEFAULT_VISIBLE', 'NO_ASSET_DRAG', 'STORE_ENABLED_CATALOGS_IN_PREFERENCES'}
|
||||
bl_activate_operator = "BRUSH_OT_asset_activate"
|
||||
bl_default_preview_size = 48
|
||||
brush_type_prop = None
|
||||
tool_prop = None
|
||||
mode_prop = None
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
return hasattr(context, "object") and context.object and context.object.mode == cls.mode
|
||||
|
||||
@classmethod
|
||||
def brush_type_poll(cls, context, asset):
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
tool = ToolSelectPanelHelper.tool_active_from_context(context)
|
||||
|
||||
if not tool or tool.brush_type == 'ANY':
|
||||
return True
|
||||
if not cls.brush_type_prop or not cls.tool_prop:
|
||||
return True
|
||||
|
||||
asset_brush_type = asset.metadata.get(cls.brush_type_prop)
|
||||
# Asset metadata doesn't store a brush type. Only show it when the tool doesn't require a
|
||||
# certain brush type.
|
||||
if asset_brush_type is None:
|
||||
return False
|
||||
brush_type_items = bpy.types.Brush.bl_rna.properties[cls.tool_prop].enum_items
|
||||
|
||||
return brush_type_items[asset_brush_type].identifier == tool.brush_type
|
||||
|
||||
@classmethod
|
||||
def asset_poll(cls, asset):
|
||||
if asset.id_type != 'BRUSH':
|
||||
return False
|
||||
if hasattr(cls, "mode_prop"):
|
||||
return asset.metadata.get(cls.mode_prop, False)
|
||||
if cls.mode_prop and not asset.metadata.get(cls.mode_prop, False):
|
||||
return False
|
||||
|
||||
context = bpy.context
|
||||
|
||||
is_asset_shelf_region = context.region and context.region.type == 'ASSET_SHELF'
|
||||
# Show all brushes in the permanent asset shelf region. Otherwise filter out brushes that
|
||||
# are incompatible with the tool.
|
||||
if not is_asset_shelf_region and not cls.brush_type_poll(context, asset):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@classmethod
|
||||
@@ -28,7 +59,7 @@ class BrushAssetShelf:
|
||||
# Only show active highlight when using the brush tool.
|
||||
from bl_ui.space_toolsystem_common import ToolSelectPanelHelper
|
||||
tool = ToolSelectPanelHelper.tool_active_from_context(bpy.context)
|
||||
if not tool or tool.idname != "builtin.brush":
|
||||
if not tool or not tool.use_brushes:
|
||||
return None
|
||||
|
||||
paint_settings = UnifiedPaintPanel.paint_settings(bpy.context)
|
||||
|
||||
@@ -1718,6 +1718,8 @@ class ImageAssetShelf(BrushAssetShelf):
|
||||
|
||||
class IMAGE_AST_brush_paint(ImageAssetShelf, AssetShelf):
|
||||
mode_prop = "use_paint_image"
|
||||
brush_type_prop = "image_brush_type"
|
||||
tool_prop = "image_tool"
|
||||
|
||||
@classmethod
|
||||
def poll(cls, context):
|
||||
|
||||
@@ -1036,7 +1036,7 @@ def _activate_by_item(context, space_type, item, index, *, as_fallback=False):
|
||||
cursor=item.cursor or 'DEFAULT',
|
||||
options=item.options or set(),
|
||||
gizmo_group=gizmo_group,
|
||||
brush_type=item.brush_type or "",
|
||||
brush_type=item.brush_type or 'ANY',
|
||||
data_block=item.data_block or "",
|
||||
operator=item.operator or "",
|
||||
index=index,
|
||||
|
||||
@@ -9548,51 +9548,71 @@ class View3DAssetShelf(BrushAssetShelf):
|
||||
class VIEW3D_AST_brush_sculpt(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT'
|
||||
mode_prop = "use_paint_sculpt"
|
||||
brush_type_prop = "sculpt_brush_type"
|
||||
tool_prop = "sculpt_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_sculpt_curves(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT_CURVES'
|
||||
mode_prop = "use_paint_sculpt_curves"
|
||||
brush_type_prop = "curves_sculpt_brush_type"
|
||||
tool_prop = "curves_sculpt_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_vertex_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'VERTEX_PAINT'
|
||||
mode_prop = "use_paint_vertex"
|
||||
brush_type_prop = "vertex_brush_type"
|
||||
tool_prop = "vertex_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_weight_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'WEIGHT_PAINT'
|
||||
mode_prop = "use_paint_weight"
|
||||
brush_type_prop = "weight_brush_type"
|
||||
tool_prop = "weight_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_texture_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'TEXTURE_PAINT'
|
||||
mode_prop = "use_paint_image"
|
||||
brush_type_prop = "image_brush_type"
|
||||
tool_prop = "image_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'PAINT_GPENCIL'
|
||||
mode_prop = "use_paint_grease_pencil"
|
||||
brush_type_prop = "gpencil_brush_type"
|
||||
tool_prop = "gpencil_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_grease_pencil_paint(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'PAINT_GREASE_PENCIL'
|
||||
mode_prop = "use_paint_grease_pencil"
|
||||
brush_type_prop = "gpencil_brush_type"
|
||||
tool_prop = "gpencil_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_sculpt(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'SCULPT_GPENCIL'
|
||||
mode_prop = "use_sculpt_grease_pencil"
|
||||
brush_type_prop = "gpencil_sculpt_brush_type"
|
||||
tool_prop = "gpencil_sculpt_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_vertex(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'VERTEX_GPENCIL'
|
||||
mode_prop = "use_vertex_grease_pencil"
|
||||
brush_type_prop = "gpencil_vertex_brush_type"
|
||||
tool_prop = "gpencil_vertex_tool"
|
||||
|
||||
|
||||
class VIEW3D_AST_brush_gpencil_weight(View3DAssetShelf, bpy.types.AssetShelf):
|
||||
mode = 'WEIGHT_GPENCIL'
|
||||
mode_prop = "use_weight_grease_pencil"
|
||||
brush_type_prop = "gpencil_weight_brush_type"
|
||||
tool_prop = "gpencil_weight_tool"
|
||||
|
||||
|
||||
classes = (
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "BKE_pbvh.hh"
|
||||
#include "BKE_subdiv_ccg.hh"
|
||||
|
||||
struct AssetWeakReference;
|
||||
struct BMFace;
|
||||
struct BMLog;
|
||||
struct BMVert;
|
||||
@@ -199,6 +200,8 @@ const EnumPropertyItem *BKE_paint_get_tool_enum_from_paintmode(PaintMode mode);
|
||||
uint BKE_paint_get_brush_type_offset_from_paintmode(PaintMode mode);
|
||||
std::optional<int> BKE_paint_get_brush_type_from_obmode(const Brush *brush,
|
||||
const eObjectMode ob_mode);
|
||||
std::optional<int> BKE_paint_get_brush_type_from_paintmode(const Brush *brush,
|
||||
const PaintMode mode);
|
||||
Paint *BKE_paint_get_active(Scene *sce, ViewLayer *view_layer);
|
||||
Paint *BKE_paint_get_active_from_context(const bContext *C);
|
||||
PaintMode BKE_paintmode_get_active_from_context(const bContext *C);
|
||||
@@ -216,11 +219,25 @@ Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name);
|
||||
*
|
||||
* \return True on success. If \a brush is already active, this is considered a success (the brush
|
||||
* asset reference will still be updated).
|
||||
*
|
||||
* \note #WM_toolsystem_activate_brush_and_tool() might be the preferable way to change the active
|
||||
* brush. It also lets the toolsystem decide if the active tool should be changed given the type of
|
||||
* brush, and it updates the "last used brush" for the previous tool. #BKE_paint_brush_set() should
|
||||
* only be called to force a brush to be active, circumventing the tool system.
|
||||
*/
|
||||
bool BKE_paint_brush_set(Paint *paint, Brush *brush);
|
||||
/**
|
||||
* Version of #BKE_paint_brush_set() that takes an asset reference instead of a brush, importing
|
||||
* the brush if necessary.
|
||||
*/
|
||||
bool BKE_paint_brush_set(Main *bmain,
|
||||
Paint *paint,
|
||||
const AssetWeakReference *brush_asset_reference);
|
||||
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint);
|
||||
bool BKE_paint_brush_set_essentials(Main *bmain, Paint *paint, const char *name);
|
||||
|
||||
std::optional<AssetWeakReference> BKE_paint_brush_type_default_reference(
|
||||
eObjectMode ob_mode, std::optional<int> brush_type);
|
||||
void BKE_paint_brushes_set_default_references(ToolSettings *ts);
|
||||
void BKE_paint_brushes_validate(Main *bmain, Paint *paint);
|
||||
|
||||
|
||||
@@ -602,7 +602,11 @@ PaintMode BKE_paintmode_get_from_tool(const bToolRef *tref)
|
||||
return PaintMode::Invalid;
|
||||
}
|
||||
|
||||
static bool paint_brush_set_from_asset_reference(Main *bmain, Paint *paint)
|
||||
/**
|
||||
* 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) {
|
||||
@@ -642,6 +646,52 @@ const Brush *BKE_paint_brush_for_read(const Paint *paint)
|
||||
return paint ? paint->brush : nullptr;
|
||||
}
|
||||
|
||||
static AssetWeakReference *asset_reference_create_from_brush(Brush *brush)
|
||||
{
|
||||
if (std::optional<AssetWeakReference> weak_ref = blender::bke::asset_edit_weak_reference_from_id(
|
||||
brush->id))
|
||||
{
|
||||
return MEM_new<AssetWeakReference>(__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<Brush *>(
|
||||
blender::bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, *brush_asset_reference));
|
||||
BLI_assert(brush == nullptr || blender::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 && (paint->runtime.ob_mode & brush->ob_mode) == 0) {
|
||||
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(blender::bke::asset_edit_weak_reference_from_id(brush->id) ==
|
||||
*brush_asset_reference);
|
||||
paint->brush_asset_reference = MEM_new<AssetWeakReference>(__func__, *brush_asset_reference);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BKE_paint_brush_set(Paint *paint, Brush *brush)
|
||||
{
|
||||
if (paint == nullptr) {
|
||||
@@ -655,18 +705,32 @@ bool BKE_paint_brush_set(Paint *paint, Brush *brush)
|
||||
|
||||
MEM_delete(paint->brush_asset_reference);
|
||||
paint->brush_asset_reference = nullptr;
|
||||
|
||||
if (brush != nullptr) {
|
||||
std::optional<AssetWeakReference> weak_ref = blender::bke::asset_edit_weak_reference_from_id(
|
||||
brush->id);
|
||||
if (weak_ref.has_value()) {
|
||||
paint->brush_asset_reference = MEM_new<AssetWeakReference>(__func__, *weak_ref);
|
||||
}
|
||||
paint->brush_asset_reference = asset_reference_create_from_brush(brush);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static AssetWeakReference *paint_brush_asset_reference_from_essentials(const char *name)
|
||||
{
|
||||
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
|
||||
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref->asset_library_identifier = nullptr;
|
||||
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
return weak_ref;
|
||||
}
|
||||
|
||||
static void paint_brush_asset_reference_from_essentials(const char *name,
|
||||
AssetWeakReference *r_weak_ref)
|
||||
{
|
||||
r_weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
r_weak_ref->asset_library_identifier = nullptr;
|
||||
r_weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
}
|
||||
|
||||
Brush *BKE_paint_brush_from_essentials(Main *bmain, const char *name)
|
||||
{
|
||||
AssetWeakReference weak_ref;
|
||||
@@ -683,11 +747,7 @@ 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);
|
||||
|
||||
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
|
||||
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
paint->brush_asset_reference = weak_ref;
|
||||
paint->brush_asset_reference = paint_brush_asset_reference_from_essentials(name);
|
||||
paint->brush = nullptr;
|
||||
}
|
||||
|
||||
@@ -696,28 +756,20 @@ static void paint_eraser_brush_set_essentials_reference(Paint *paint, const char
|
||||
/* Set brush asset reference to a named brush in the essentials asset library. */
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
|
||||
AssetWeakReference *weak_ref = MEM_new<AssetWeakReference>(__func__);
|
||||
weak_ref->asset_library_type = eAssetLibraryType::ASSET_LIBRARY_ESSENTIALS;
|
||||
weak_ref->relative_asset_identifier = BLI_sprintfN("brushes/essentials_brushes.blend/Brush/%s",
|
||||
name);
|
||||
paint->eraser_brush_asset_reference = weak_ref;
|
||||
paint->eraser_brush_asset_reference = paint_brush_asset_reference_from_essentials(name);
|
||||
paint->eraser_brush = nullptr;
|
||||
}
|
||||
|
||||
static void paint_brush_set_default_reference(Paint *paint,
|
||||
const bool do_regular = true,
|
||||
const bool do_eraser = true)
|
||||
static void paint_brush_default_essentials_name_get(
|
||||
eObjectMode ob_mode,
|
||||
std::optional<int> brush_type,
|
||||
blender::StringRefNull *r_name,
|
||||
blender::StringRefNull *r_eraser_name = nullptr)
|
||||
{
|
||||
if (!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;
|
||||
}
|
||||
const char *name = "";
|
||||
const char *eraser_name = "";
|
||||
|
||||
const char *name = nullptr;
|
||||
const char *eraser_name = nullptr;
|
||||
|
||||
switch (paint->runtime.ob_mode) {
|
||||
switch (ob_mode) {
|
||||
case OB_MODE_SCULPT:
|
||||
name = "Draw";
|
||||
break;
|
||||
@@ -735,6 +787,17 @@ static void paint_brush_set_default_reference(Paint *paint,
|
||||
break;
|
||||
case OB_MODE_PAINT_GPENCIL_LEGACY:
|
||||
name = "Pencil";
|
||||
/* Different default brush for some brush types. */
|
||||
if (brush_type) {
|
||||
switch (*brush_type) {
|
||||
case GPAINT_BRUSH_TYPE_ERASE:
|
||||
name = "Eraser Hard";
|
||||
break;
|
||||
case GPAINT_BRUSH_TYPE_FILL:
|
||||
name = "Fill Area";
|
||||
break;
|
||||
}
|
||||
}
|
||||
eraser_name = "Eraser Soft";
|
||||
break;
|
||||
case OB_MODE_VERTEX_GPENCIL_LEGACY:
|
||||
@@ -748,14 +811,51 @@ static void paint_brush_set_default_reference(Paint *paint,
|
||||
break;
|
||||
default:
|
||||
BLI_assert_unreachable();
|
||||
return;
|
||||
break;
|
||||
}
|
||||
|
||||
if (do_regular && name) {
|
||||
paint_brush_set_essentials_reference(paint, name);
|
||||
*r_name = name;
|
||||
if (r_eraser_name) {
|
||||
*r_eraser_name = eraser_name;
|
||||
}
|
||||
if (do_eraser && eraser_name) {
|
||||
paint_eraser_brush_set_essentials_reference(paint, eraser_name);
|
||||
}
|
||||
|
||||
std::optional<AssetWeakReference> BKE_paint_brush_type_default_reference(
|
||||
eObjectMode ob_mode, std::optional<int> brush_type)
|
||||
{
|
||||
blender::StringRefNull name;
|
||||
|
||||
paint_brush_default_essentials_name_get(ob_mode, brush_type, &name, nullptr);
|
||||
if (name.is_empty()) {
|
||||
return {};
|
||||
}
|
||||
|
||||
AssetWeakReference asset_reference;
|
||||
paint_brush_asset_reference_from_essentials(name.c_str(), &asset_reference);
|
||||
return asset_reference;
|
||||
}
|
||||
|
||||
static void paint_brush_set_default_reference(Paint *paint,
|
||||
const bool do_regular = true,
|
||||
const bool do_eraser = true)
|
||||
{
|
||||
if (!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;
|
||||
}
|
||||
|
||||
blender::StringRefNull name;
|
||||
blender::StringRefNull eraser_name;
|
||||
|
||||
paint_brush_default_essentials_name_get(
|
||||
eObjectMode(paint->runtime.ob_mode), std::nullopt, &name, nullptr);
|
||||
|
||||
if (do_regular && !name.is_empty()) {
|
||||
paint_brush_set_essentials_reference(paint, name.c_str());
|
||||
}
|
||||
if (do_eraser && !eraser_name.is_empty()) {
|
||||
paint_eraser_brush_set_essentials_reference(paint, eraser_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -791,13 +891,13 @@ void BKE_paint_brushes_set_default_references(ToolSettings *ts)
|
||||
bool BKE_paint_brush_set_default(Main *bmain, Paint *paint)
|
||||
{
|
||||
paint_brush_set_default_reference(paint, true, false);
|
||||
return paint_brush_set_from_asset_reference(bmain, paint);
|
||||
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_set_from_asset_reference(bmain, paint);
|
||||
return paint_brush_update_from_asset_reference(bmain, paint);
|
||||
}
|
||||
|
||||
void BKE_paint_brushes_validate(Main *bmain, Paint *paint)
|
||||
@@ -999,6 +1099,37 @@ std::optional<int> BKE_paint_get_brush_type_from_obmode(const Brush *brush,
|
||||
}
|
||||
}
|
||||
|
||||
std::optional<int> 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::SculptGreasePencil:
|
||||
return brush->gpencil_sculpt_brush_type;
|
||||
case PaintMode::Invalid:
|
||||
default:
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
PaintCurve *BKE_paint_curve_add(Main *bmain, const char *name)
|
||||
{
|
||||
PaintCurve *pc = static_cast<PaintCurve *>(BKE_id_new(bmain, ID_PC, name));
|
||||
@@ -1365,7 +1496,7 @@ bool BKE_paint_ensure(Main *bmain, ToolSettings *ts, Paint **r_paint)
|
||||
BLI_assert(paint_test.runtime.ob_mode == (*r_paint)->runtime.ob_mode);
|
||||
#endif
|
||||
}
|
||||
paint_brush_set_from_asset_reference(bmain, *r_paint);
|
||||
paint_brush_update_from_asset_reference(bmain, *r_paint);
|
||||
paint_eraser_brush_set_from_asset_reference(bmain, *r_paint);
|
||||
return true;
|
||||
}
|
||||
@@ -1437,7 +1568,17 @@ 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);
|
||||
MEM_delete(paint->eraser_brush_asset_reference);
|
||||
|
||||
LISTBASE_FOREACH_MUTABLE (NamedBrushAssetReference *,
|
||||
brush_ref,
|
||||
&paint->tool_brush_bindings.active_brush_per_brush_type)
|
||||
{
|
||||
MEM_delete(brush_ref->name);
|
||||
MEM_delete(brush_ref->brush_asset_reference);
|
||||
MEM_delete(brush_ref);
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_copy(const Paint *src, Paint *dst, const int flag)
|
||||
@@ -1449,10 +1590,23 @@ void BKE_paint_copy(const Paint *src, Paint *dst, const int flag)
|
||||
dst->brush_asset_reference = MEM_new<AssetWeakReference>(__func__,
|
||||
*src->brush_asset_reference);
|
||||
}
|
||||
if (src->tool_brush_bindings.main_brush_asset_reference) {
|
||||
dst->tool_brush_bindings.main_brush_asset_reference = MEM_new<AssetWeakReference>(
|
||||
__func__, *src->tool_brush_bindings.main_brush_asset_reference);
|
||||
}
|
||||
if (src->eraser_brush_asset_reference) {
|
||||
dst->eraser_brush_asset_reference = MEM_new<AssetWeakReference>(
|
||||
__func__, *src->eraser_brush_asset_reference);
|
||||
}
|
||||
BLI_duplicatelist(&dst->tool_brush_bindings.active_brush_per_brush_type,
|
||||
&src->tool_brush_bindings.active_brush_per_brush_type);
|
||||
LISTBASE_FOREACH (
|
||||
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<AssetWeakReference>(
|
||||
__func__, *brush_ref->brush_asset_reference);
|
||||
}
|
||||
|
||||
if ((flag & LIB_ID_CREATE_NO_USER_REFCOUNT) == 0) {
|
||||
id_us_plus((ID *)dst->palette);
|
||||
@@ -1482,6 +1636,25 @@ void BKE_paint_blend_write(BlendWriter *writer, Paint *paint)
|
||||
if (paint->eraser_brush_asset_reference) {
|
||||
BKE_asset_weak_reference_write(writer, paint->eraser_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);
|
||||
}
|
||||
BLO_write_struct_list(
|
||||
writer, NamedBrushAssetReference, &tool_brush_bindings.active_brush_per_brush_type);
|
||||
LISTBASE_FOREACH (
|
||||
NamedBrushAssetReference *, brush_ref, &tool_brush_bindings.active_brush_per_brush_type)
|
||||
{
|
||||
BLO_write_string(writer, brush_ref->name);
|
||||
if (brush_ref->brush_asset_reference) {
|
||||
BKE_asset_weak_reference_write(writer, brush_ref->brush_asset_reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Paint *paint)
|
||||
@@ -1498,12 +1671,34 @@ void BKE_paint_blend_read_data(BlendDataReader *reader, const Scene *scene, Pain
|
||||
if (paint->brush_asset_reference) {
|
||||
BKE_asset_weak_reference_read(reader, paint->brush_asset_reference);
|
||||
}
|
||||
|
||||
BLO_read_struct(reader, AssetWeakReference, &paint->eraser_brush_asset_reference);
|
||||
if (paint->eraser_brush_asset_reference) {
|
||||
BKE_asset_weak_reference_read(reader, paint->eraser_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);
|
||||
LISTBASE_FOREACH (
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
paint->paint_cursor = nullptr;
|
||||
paint_runtime_init(scene->toolsettings, paint);
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
#include "BKE_asset.hh"
|
||||
#include "BKE_asset_edit.hh"
|
||||
@@ -65,7 +66,10 @@ static int brush_asset_activate_exec(bContext *C, wmOperator *op)
|
||||
|
||||
Paint *paint = BKE_paint_get_active_from_context(C);
|
||||
|
||||
if (!BKE_paint_brush_set(paint, brush)) {
|
||||
/* Activate brush through tool system rather than calling #BKE_paint_brush_set() directly, to let
|
||||
* the tool system switch tools if necessary, and update which brush was the last recently used
|
||||
* one for the current tool. */
|
||||
if (!WM_toolsystem_activate_brush_and_tool(C, paint, brush)) {
|
||||
/* Note brush datablock was still added, so was not a no-op. */
|
||||
BKE_report(op->reports, RPT_WARNING, "Unable to activate brush, wrong object mode");
|
||||
return OPERATOR_FINISHED;
|
||||
@@ -73,7 +77,6 @@ static int brush_asset_activate_exec(bContext *C, wmOperator *op)
|
||||
|
||||
WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, nullptr);
|
||||
WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, nullptr);
|
||||
WM_toolsystem_ref_set_by_id(C, "builtin.brush");
|
||||
|
||||
return OPERATOR_FINISHED;
|
||||
}
|
||||
@@ -260,7 +263,7 @@ static int brush_asset_save_as_exec(bContext *C, wmOperator *op)
|
||||
brush = reinterpret_cast<Brush *>(
|
||||
bke::asset_edit_id_from_weak_reference(*bmain, ID_BR, brush_asset_reference));
|
||||
|
||||
if (!BKE_paint_brush_set(paint, brush)) {
|
||||
if (!WM_toolsystem_activate_brush_and_tool(C, paint, brush)) {
|
||||
/* Note brush asset was still saved in editable asset library, so was not a no-op. */
|
||||
BKE_report(op->reports, RPT_WARNING, "Unable to activate just-saved brush asset");
|
||||
}
|
||||
|
||||
@@ -925,6 +925,38 @@ typedef struct Paint_Runtime {
|
||||
char _pad[2];
|
||||
} Paint_Runtime;
|
||||
|
||||
typedef struct NamedBrushAssetReference {
|
||||
struct NamedBrushAssetReference *next, *prev;
|
||||
|
||||
const char *name;
|
||||
struct AssetWeakReference *brush_asset_reference;
|
||||
} NamedBrushAssetReference;
|
||||
|
||||
/**
|
||||
* For the tool system: Storage to remember the last active brush for specific tools.
|
||||
*
|
||||
* This stores a "main" brush reference, which is used for any tool that uses brushes but isn't
|
||||
* limited to a specific brush type, and a list of brush references identified by the brush type,
|
||||
* for tools that are limited to a brush type.
|
||||
*
|
||||
* The tool system updates these fields as the active brush or active tool changes. It also
|
||||
* determines the brush to remember/restore on tool changes and activates it.
|
||||
*/
|
||||
typedef struct ToolSystemBrushBindings {
|
||||
struct AssetWeakReference *main_brush_asset_reference;
|
||||
|
||||
/**
|
||||
* The tool system exposes tools for some brush types, like an eraser tool to access eraser
|
||||
* brushes. Switching between tools should remember the last used brush for a brush type, e.g.
|
||||
* which eraser was used last by the eraser tool.
|
||||
*
|
||||
* Note that multiple tools may use the same brush type, for example primitive draw tools (to
|
||||
* draw rectangles, circles, lines, etc.) all use a "DRAW" brush, which will then be shared
|
||||
* amongst them.
|
||||
*/
|
||||
ListBase active_brush_per_brush_type; /* #NamedBrushAssetReference */
|
||||
} ToolSystemBrushBindings;
|
||||
|
||||
/** Paint Tool Base. */
|
||||
typedef struct Paint {
|
||||
/**
|
||||
@@ -944,6 +976,8 @@ typedef struct Paint {
|
||||
struct Brush *eraser_brush;
|
||||
struct AssetWeakReference *eraser_brush_asset_reference;
|
||||
|
||||
ToolSystemBrushBindings tool_brush_bindings;
|
||||
|
||||
struct Palette *palette;
|
||||
/** Cavity curve. */
|
||||
struct CurveMapping *cavity_curve;
|
||||
|
||||
@@ -41,8 +41,15 @@ typedef struct bToolRef_Runtime {
|
||||
/** One of these 4 must be defined. */
|
||||
char keymap[64];
|
||||
char gizmo_group[64];
|
||||
char brush_type[64];
|
||||
char data_block[64];
|
||||
/**
|
||||
* The brush type this tool is limited too, if #TOOLREF_FLAG_USE_BRUSHES is set. Note that this
|
||||
* is a different enum in different modes, e.g. #eBrushSculptType in sculpt mode,
|
||||
* #eBrushVertexPaintType in vertex paint mode.
|
||||
*
|
||||
* -1 means any brush type may be used (0 is used by brush type enums of some modes).
|
||||
*/
|
||||
int brush_type;
|
||||
|
||||
/** Keymap for #bToolRef.idname_fallback, if set. */
|
||||
char keymap_fallback[64];
|
||||
|
||||
@@ -406,6 +406,11 @@ void rna_userdef_is_dirty_update_impl();
|
||||
*/
|
||||
void rna_userdef_is_dirty_update(Main *bmain, Scene *scene, PointerRNA *ptr);
|
||||
|
||||
const EnumPropertyItem *rna_WorkSpaceTool_brush_type_itemf(bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA *prop,
|
||||
bool *r_free);
|
||||
|
||||
/* API functions */
|
||||
|
||||
void RNA_api_action(StructRNA *srna);
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
# include "BLI_listbase.h"
|
||||
|
||||
# include "BKE_global.hh"
|
||||
# include "BKE_paint.hh"
|
||||
|
||||
# include "DNA_object_types.h"
|
||||
# include "DNA_screen_types.h"
|
||||
@@ -197,16 +198,42 @@ static bool rna_WorkSpaceTool_use_brushes_get(PointerRNA *ptr)
|
||||
return (tref->runtime) ? ((tref->runtime->flag & TOOLREF_FLAG_USE_BRUSHES) != 0) : false;
|
||||
}
|
||||
|
||||
static void rna_WorkSpaceTool_brush_type_get(PointerRNA *ptr, char *value)
|
||||
static int rna_WorkSpaceTool_brush_type_get(PointerRNA *ptr)
|
||||
{
|
||||
bToolRef *tref = static_cast<bToolRef *>(ptr->data);
|
||||
strcpy(value, tref->runtime ? tref->runtime->brush_type : "");
|
||||
return tref->runtime ? tref->runtime->brush_type : -1;
|
||||
}
|
||||
|
||||
static int rna_WorkSpaceTool_brush_type_length(PointerRNA *ptr)
|
||||
const EnumPropertyItem *rna_WorkSpaceTool_brush_type_itemf(bContext *C,
|
||||
PointerRNA *ptr,
|
||||
PropertyRNA * /*prop*/,
|
||||
bool *r_free)
|
||||
{
|
||||
bToolRef *tref = static_cast<bToolRef *>(ptr->data);
|
||||
return tref->runtime ? strlen(tref->runtime->brush_type) : 0;
|
||||
|
||||
PaintMode paint_mode = [&]() {
|
||||
if (ptr->type == &RNA_WorkSpaceTool) {
|
||||
const bToolRef *tref = static_cast<bToolRef *>(ptr->data);
|
||||
return BKE_paintmode_get_from_tool(tref);
|
||||
}
|
||||
return C ? BKE_paintmode_get_active_from_context(C) : PaintMode::Invalid;
|
||||
}();
|
||||
|
||||
EnumPropertyItem *items = nullptr;
|
||||
int totitem = 0;
|
||||
|
||||
EnumPropertyItem unset_item = {
|
||||
-1, "ANY", 0, "Any", "Donnot limit this tool to a specific brush type"};
|
||||
RNA_enum_item_add(&items, &totitem, &unset_item);
|
||||
|
||||
if (paint_mode != PaintMode::Invalid) {
|
||||
const EnumPropertyItem *valid_items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
|
||||
RNA_enum_items_add(&items, &totitem, valid_items);
|
||||
}
|
||||
|
||||
RNA_enum_item_end(&items, &totitem);
|
||||
|
||||
*r_free = true;
|
||||
return items;
|
||||
}
|
||||
|
||||
static void rna_WorkSpaceTool_widget_get(PointerRNA *ptr, char *value)
|
||||
@@ -324,14 +351,15 @@ static void rna_def_workspace_tool(BlenderRNA *brna)
|
||||
RNA_def_property_ui_text(prop, "Uses Brushes", "");
|
||||
RNA_def_property_boolean_funcs(prop, "rna_WorkSpaceTool_use_brushes_get", nullptr);
|
||||
|
||||
prop = RNA_def_property(srna, "brush_type", PROP_STRING, PROP_NONE);
|
||||
prop = RNA_def_property(srna, "brush_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_clear_flag(prop, PROP_EDITABLE);
|
||||
RNA_def_property_ui_text(prop,
|
||||
"Brush Type",
|
||||
"If the tool uses brushes and is limited to a specific brush type, the "
|
||||
"identifier of the brush type");
|
||||
RNA_def_property_string_funcs(
|
||||
prop, "rna_WorkSpaceTool_brush_type_get", "rna_WorkSpaceTool_brush_type_length", nullptr);
|
||||
RNA_def_property_enum_items(prop, rna_enum_dummy_DEFAULT_items);
|
||||
RNA_def_property_enum_funcs(
|
||||
prop, "rna_WorkSpaceTool_brush_type_get", nullptr, "rna_WorkSpaceTool_brush_type_itemf");
|
||||
|
||||
RNA_define_verify_sdna(true);
|
||||
|
||||
|
||||
@@ -37,7 +37,7 @@ static void rna_WorkSpaceTool_setup(ID *id,
|
||||
int cursor,
|
||||
const char *keymap,
|
||||
const char *gizmo_group,
|
||||
const char *brush_type,
|
||||
int brush_type,
|
||||
const char *data_block,
|
||||
const char *op_idname,
|
||||
int index,
|
||||
@@ -50,9 +50,9 @@ static void rna_WorkSpaceTool_setup(ID *id,
|
||||
tref_rt.cursor = cursor;
|
||||
STRNCPY(tref_rt.keymap, keymap);
|
||||
STRNCPY(tref_rt.gizmo_group, gizmo_group);
|
||||
STRNCPY(tref_rt.brush_type, brush_type);
|
||||
STRNCPY(tref_rt.data_block, data_block);
|
||||
STRNCPY(tref_rt.op, op_idname);
|
||||
tref_rt.brush_type = brush_type;
|
||||
tref_rt.index = index;
|
||||
tref_rt.flag = options;
|
||||
|
||||
@@ -146,7 +146,11 @@ void RNA_api_workspace_tool(StructRNA *srna)
|
||||
RNA_def_property_enum_items(parm, rna_enum_window_cursor_items);
|
||||
RNA_def_string(func, "keymap", nullptr, KMAP_MAX_NAME, "Key Map", "");
|
||||
RNA_def_string(func, "gizmo_group", nullptr, MAX_NAME, "Gizmo Group", "");
|
||||
RNA_def_string(func, "brush_type", nullptr, MAX_NAME, "Brush Type", "");
|
||||
parm = RNA_def_property(func, "brush_type", PROP_ENUM, PROP_NONE);
|
||||
RNA_def_property_enum_items(parm, rna_enum_dummy_NULL_items);
|
||||
RNA_def_property_enum_funcs(parm, nullptr, nullptr, "rna_WorkSpaceTool_brush_type_itemf");
|
||||
RNA_def_property_enum_default(parm, -1);
|
||||
RNA_def_property_ui_text(parm, "Brush Type", "Limit this tool to a specific type of brush");
|
||||
RNA_def_string(func, "data_block", nullptr, MAX_NAME, "Data Block", "");
|
||||
RNA_def_string(func, "operator", nullptr, MAX_NAME, "Operator", "");
|
||||
RNA_def_int(func, "index", 0, INT_MIN, INT_MAX, "Index", "", INT_MIN, INT_MAX);
|
||||
|
||||
@@ -7,10 +7,10 @@
|
||||
* \ingroup wm
|
||||
*/
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
|
||||
struct Brush;
|
||||
struct IDProperty;
|
||||
struct Main;
|
||||
struct Paint;
|
||||
struct PointerRNA;
|
||||
struct Scene;
|
||||
struct ScrArea;
|
||||
@@ -51,6 +51,15 @@ bToolRef *WM_toolsystem_ref_set_by_id(bContext *C, const char *name);
|
||||
bToolRef_Runtime *WM_toolsystem_runtime_from_context(const bContext *C);
|
||||
bToolRef_Runtime *WM_toolsystem_runtime_find(WorkSpace *workspace, const bToolKey *tkey);
|
||||
|
||||
/**
|
||||
* Activate the brush through the tool system. This will call #BKE_paint_brush_set() with \a brush,
|
||||
* but it will also switch to the tool appropriate for this brush type (if necessary) and update
|
||||
* the current tool-brush references to remember the last used brush for that tool.
|
||||
*
|
||||
* \return True if the brush was successfully activated.
|
||||
*/
|
||||
bool WM_toolsystem_activate_brush_and_tool(bContext *C, Paint *paint, Brush *brush);
|
||||
|
||||
void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tkey);
|
||||
void WM_toolsystem_refresh(const bContext *C, WorkSpace *workspace, const bToolKey *tkey);
|
||||
void WM_toolsystem_reinit(bContext *C, WorkSpace *workspace, const bToolKey *tkey);
|
||||
|
||||
@@ -19,12 +19,14 @@
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
#include "DNA_brush_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_space_types.h"
|
||||
#include "DNA_windowmanager_types.h"
|
||||
#include "DNA_workspace_types.h"
|
||||
|
||||
#include "BKE_asset_edit.hh"
|
||||
#include "BKE_brush.hh"
|
||||
#include "BKE_context.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
@@ -135,6 +137,257 @@ void WM_toolsystem_unlink(bContext *C, WorkSpace *workspace, const bToolKey *tke
|
||||
}
|
||||
}
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Brush Tools
|
||||
* \{ */
|
||||
|
||||
static std::optional<blender::StringRefNull> find_tool_id_from_brush_type_id(const bContext *C,
|
||||
const int brush_type)
|
||||
{
|
||||
const WorkSpace *workspace = CTX_wm_workspace(C);
|
||||
LISTBASE_FOREACH (const bToolRef *, tref, &workspace->tools) {
|
||||
if (tref->runtime && (tref->runtime->brush_type != -1) &&
|
||||
tref->runtime->brush_type == brush_type)
|
||||
{
|
||||
return tref->idname;
|
||||
}
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static const char *brush_type_identifier_get(const int brush_type, const PaintMode paint_mode)
|
||||
{
|
||||
const EnumPropertyItem *type_enum = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
|
||||
const int item_idx = RNA_enum_from_value(type_enum, brush_type);
|
||||
if (item_idx == -1) {
|
||||
return "";
|
||||
}
|
||||
return type_enum[item_idx].identifier;
|
||||
}
|
||||
|
||||
static bool brush_type_is_compatible_with_active_tool(bContext *C, const int brush_type)
|
||||
{
|
||||
if (!WM_toolsystem_active_tool_is_brush(C)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bToolRef *active_tool = WM_toolsystem_ref_from_context(C);
|
||||
|
||||
BLI_assert(BKE_paintmode_get_active_from_context(C) == BKE_paintmode_get_from_tool(active_tool));
|
||||
|
||||
/* Tool supports any brush type, no need to check further. */
|
||||
if (active_tool->runtime->brush_type == -1) {
|
||||
return true;
|
||||
}
|
||||
return active_tool->runtime->brush_type == brush_type;
|
||||
}
|
||||
|
||||
static NamedBrushAssetReference *toolsystem_brush_type_binding_lookup(const Paint *paint,
|
||||
const char *brush_type_name)
|
||||
{
|
||||
return static_cast<NamedBrushAssetReference *>(
|
||||
BLI_findstring_ptr(&paint->tool_brush_bindings.active_brush_per_brush_type,
|
||||
brush_type_name,
|
||||
offsetof(NamedBrushAssetReference, name)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the bindings so the main brush reference matches the currently active brush.
|
||||
*/
|
||||
static void toolsystem_main_brush_binding_update_from_active(Paint *paint)
|
||||
{
|
||||
MEM_delete(paint->tool_brush_bindings.main_brush_asset_reference);
|
||||
paint->tool_brush_bindings.main_brush_asset_reference = nullptr;
|
||||
|
||||
if (paint->brush != nullptr) {
|
||||
if (std::optional<AssetWeakReference> brush_asset_reference =
|
||||
blender::bke::asset_edit_weak_reference_from_id(paint->brush->id))
|
||||
{
|
||||
paint->tool_brush_bindings.main_brush_asset_reference = MEM_new<AssetWeakReference>(
|
||||
__func__, *brush_asset_reference);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void toolsystem_brush_type_binding_update(Paint *paint,
|
||||
const PaintMode paint_mode,
|
||||
const int brush_type)
|
||||
{
|
||||
if (paint->brush == nullptr) {
|
||||
return;
|
||||
}
|
||||
const char *brush_type_name = brush_type_identifier_get(brush_type, paint_mode);
|
||||
if (!brush_type_name || !brush_type_name[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Update existing reference. */
|
||||
if (NamedBrushAssetReference *existing_brush_ref = toolsystem_brush_type_binding_lookup(
|
||||
paint, brush_type_name))
|
||||
{
|
||||
MEM_delete(existing_brush_ref->brush_asset_reference);
|
||||
existing_brush_ref->brush_asset_reference = MEM_new<AssetWeakReference>(
|
||||
__func__, *paint->brush_asset_reference);
|
||||
}
|
||||
/* Add new reference. */
|
||||
else {
|
||||
NamedBrushAssetReference *new_brush_ref = MEM_cnew<NamedBrushAssetReference>(__func__);
|
||||
|
||||
new_brush_ref->name = BLI_strdup(brush_type_name);
|
||||
new_brush_ref->brush_asset_reference = MEM_new<AssetWeakReference>(
|
||||
__func__, *paint->brush_asset_reference);
|
||||
BLI_addhead(&paint->tool_brush_bindings.active_brush_per_brush_type, new_brush_ref);
|
||||
}
|
||||
}
|
||||
|
||||
bool WM_toolsystem_activate_brush_and_tool(bContext *C, Paint *paint, Brush *brush)
|
||||
{
|
||||
const bToolRef *active_tool = WM_toolsystem_ref_from_context(C);
|
||||
const PaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
|
||||
|
||||
if (!BKE_paint_brush_set(paint, brush)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (active_tool->runtime->brush_type == -1) {
|
||||
/* Only update the main brush binding to reference the newly active brush. */
|
||||
toolsystem_main_brush_binding_update_from_active(paint);
|
||||
return true;
|
||||
}
|
||||
|
||||
toolsystem_brush_type_binding_update(paint, paint_mode, active_tool->runtime->brush_type);
|
||||
|
||||
/* If necessary, find a compatible tool to switch to. */
|
||||
{
|
||||
std::optional<int> brush_type = BKE_paint_get_brush_type_from_paintmode(brush, paint_mode);
|
||||
if (!brush_type) {
|
||||
BLI_assert_unreachable();
|
||||
WM_toolsystem_ref_set_by_id(C, "builtin.brush");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!brush_type_is_compatible_with_active_tool(C, *brush_type)) {
|
||||
std::optional<blender::StringRefNull> compatible_tool = find_tool_id_from_brush_type_id(
|
||||
C, *brush_type);
|
||||
WM_toolsystem_ref_set_by_id(C, compatible_tool.value_or("builtin.brush").c_str());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
static void toolsystem_brush_activate_from_toolref_for_object_particle(const bContext *C,
|
||||
const WorkSpace *workspace,
|
||||
const bToolRef *tref)
|
||||
{
|
||||
const Main *bmain = CTX_data_main(C);
|
||||
const bToolRef_Runtime *tref_rt = tref->runtime;
|
||||
|
||||
if (!tref_rt->data_block[0]) {
|
||||
return;
|
||||
}
|
||||
|
||||
const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items;
|
||||
const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
|
||||
if (i == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
|
||||
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
|
||||
if (workspace == WM_window_get_active_workspace(win)) {
|
||||
Scene *scene = WM_window_get_active_scene(win);
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
ts->particle.brushtype = items[i].value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void toolsystem_brush_activate_from_toolref_for_object_paint(const bContext *C,
|
||||
const WorkSpace *workspace,
|
||||
const bToolRef *tref)
|
||||
{
|
||||
Main *bmain = CTX_data_main(C);
|
||||
bToolRef_Runtime *tref_rt = tref->runtime;
|
||||
|
||||
const PaintMode paint_mode = BKE_paintmode_get_from_tool(tref);
|
||||
BLI_assert(paint_mode != PaintMode::Invalid);
|
||||
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
|
||||
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
|
||||
if (workspace != WM_window_get_active_workspace(win)) {
|
||||
continue;
|
||||
}
|
||||
Scene *scene = WM_window_get_active_scene(win);
|
||||
BKE_paint_ensure_from_paintmode(bmain, scene, paint_mode);
|
||||
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
|
||||
|
||||
/* Attempt to re-activate a brush remembered for this brush type, as stored in a brush
|
||||
* binding. */
|
||||
if (tref_rt->brush_type != -1) {
|
||||
std::optional<AssetWeakReference> brush_asset_reference =
|
||||
[&]() -> std::optional<AssetWeakReference> {
|
||||
const char *brush_type_name = brush_type_identifier_get(tref_rt->brush_type, paint_mode);
|
||||
const NamedBrushAssetReference *brush_ref = toolsystem_brush_type_binding_lookup(
|
||||
paint, brush_type_name);
|
||||
|
||||
if (brush_ref && brush_ref->brush_asset_reference) {
|
||||
return *brush_ref->brush_asset_reference;
|
||||
}
|
||||
/* No remembered brush found for this type, use a default for the type. */
|
||||
return BKE_paint_brush_type_default_reference(eObjectMode(paint->runtime.ob_mode),
|
||||
tref_rt->brush_type);
|
||||
}();
|
||||
|
||||
if (brush_asset_reference) {
|
||||
BKE_paint_brush_set(bmain, paint, &*brush_asset_reference);
|
||||
}
|
||||
}
|
||||
/* Re-activate the main brush, regardless of the brush type. */
|
||||
else {
|
||||
if (paint->tool_brush_bindings.main_brush_asset_reference) {
|
||||
BKE_paint_brush_set(bmain, paint, paint->tool_brush_bindings.main_brush_asset_reference);
|
||||
toolsystem_main_brush_binding_update_from_active(paint);
|
||||
}
|
||||
else {
|
||||
std::optional<AssetWeakReference> main_brush_asset_reference =
|
||||
[&]() -> std::optional<AssetWeakReference> {
|
||||
if (paint->tool_brush_bindings.main_brush_asset_reference) {
|
||||
return *paint->tool_brush_bindings.main_brush_asset_reference;
|
||||
}
|
||||
return BKE_paint_brush_type_default_reference(eObjectMode(paint->runtime.ob_mode),
|
||||
std::nullopt);
|
||||
}();
|
||||
|
||||
if (main_brush_asset_reference) {
|
||||
BKE_paint_brush_set(bmain, paint, &*main_brush_asset_reference);
|
||||
toolsystem_main_brush_binding_update_from_active(paint);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Activate a brush compatible with \a tref, call when the active tool changes.
|
||||
*/
|
||||
static void toolsystem_brush_activate_from_toolref(const bContext *C,
|
||||
WorkSpace *workspace,
|
||||
bToolRef *tref)
|
||||
{
|
||||
BLI_assert(tref->runtime->flag & TOOLREF_FLAG_USE_BRUSHES);
|
||||
|
||||
if (tref->space_type == SPACE_VIEW3D) {
|
||||
if (tref->mode == CTX_MODE_PARTICLE) {
|
||||
toolsystem_brush_activate_from_toolref_for_object_particle(C, workspace, tref);
|
||||
}
|
||||
else {
|
||||
toolsystem_brush_activate_from_toolref_for_object_paint(C, workspace, tref);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** \} */
|
||||
|
||||
static void toolsystem_ref_link(const bContext *C, WorkSpace *workspace, bToolRef *tref)
|
||||
{
|
||||
bToolRef_Runtime *tref_rt = tref->runtime;
|
||||
@@ -157,25 +410,7 @@ static void toolsystem_ref_link(const bContext *C, WorkSpace *workspace, bToolRe
|
||||
}
|
||||
|
||||
if (tref_rt->flag & TOOLREF_FLAG_USE_BRUSHES) {
|
||||
Main *bmain = CTX_data_main(C);
|
||||
|
||||
if ((tref->space_type == SPACE_VIEW3D) && (tref->mode == CTX_MODE_PARTICLE) &&
|
||||
tref_rt->data_block[0])
|
||||
{
|
||||
const EnumPropertyItem *items = rna_enum_particle_edit_hair_brush_items;
|
||||
const int i = RNA_enum_from_identifier(items, tref_rt->data_block);
|
||||
if (i != -1) {
|
||||
const int value = items[i].value;
|
||||
wmWindowManager *wm = static_cast<wmWindowManager *>(bmain->wm.first);
|
||||
LISTBASE_FOREACH (wmWindow *, win, &wm->windows) {
|
||||
if (workspace == WM_window_get_active_workspace(win)) {
|
||||
Scene *scene = WM_window_get_active_scene(win);
|
||||
ToolSettings *ts = scene->toolsettings;
|
||||
ts->particle.brushtype = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
toolsystem_brush_activate_from_toolref(C, workspace, tref);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user