WIP: Brush assets project #106303
|
@ -107,10 +107,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
||||||
kmi_hack_properties = kmi_hack.properties
|
kmi_hack_properties = kmi_hack.properties
|
||||||
kmi_hack.active = False
|
kmi_hack.active = False
|
||||||
|
|
||||||
kmi_hack_brush_select = keymap.keymap_items.new("paint.brush_select", 'NONE', 'PRESS')
|
|
||||||
kmi_hack_brush_select_properties = kmi_hack_brush_select.properties
|
|
||||||
kmi_hack_brush_select.active = False
|
|
||||||
|
|
||||||
if use_release_confirm or use_tap_reset:
|
if use_release_confirm or use_tap_reset:
|
||||||
kmi_toolbar = wm.keyconfigs.find_item_from_operator(
|
kmi_toolbar = wm.keyconfigs.find_item_from_operator(
|
||||||
idname="wm.toolbar",
|
idname="wm.toolbar",
|
||||||
|
@ -169,46 +165,6 @@ def generate(context, space_type, *, use_fallback_keys=True, use_reset=True):
|
||||||
include={'KEYBOARD'},
|
include={'KEYBOARD'},
|
||||||
)[1]
|
)[1]
|
||||||
|
|
||||||
if kmi_found is None:
|
|
||||||
if item.data_block:
|
|
||||||
# PAINT_OT_brush_select
|
|
||||||
mode = context.active_object.mode
|
|
||||||
# See: BKE_paint_get_tool_prop_id_from_paintmode
|
|
||||||
if space_type == 'IMAGE_EDITOR':
|
|
||||||
if context.space_data.mode == 'PAINT':
|
|
||||||
attr = "image_tool"
|
|
||||||
else:
|
|
||||||
attr = None
|
|
||||||
elif space_type == 'VIEW_3D':
|
|
||||||
attr = {
|
|
||||||
'SCULPT': "sculpt_tool",
|
|
||||||
'VERTEX_PAINT': "vertex_tool",
|
|
||||||
'WEIGHT_PAINT': "weight_tool",
|
|
||||||
'TEXTURE_PAINT': "image_tool",
|
|
||||||
'PAINT_GPENCIL': "gpencil_tool",
|
|
||||||
'VERTEX_GPENCIL': "gpencil_vertex_tool",
|
|
||||||
'SCULPT_GPENCIL': "gpencil_sculpt_tool",
|
|
||||||
'WEIGHT_GPENCIL': "gpencil_weight_tool",
|
|
||||||
'SCULPT_CURVES': "curves_sculpt_tool",
|
|
||||||
}.get(mode, None)
|
|
||||||
else:
|
|
||||||
attr = None
|
|
||||||
|
|
||||||
if attr is not None:
|
|
||||||
setattr(kmi_hack_brush_select_properties, attr, item.data_block)
|
|
||||||
kmi_found = wm.keyconfigs.find_item_from_operator(
|
|
||||||
idname="paint.brush_select",
|
|
||||||
context='INVOKE_REGION_WIN',
|
|
||||||
properties=kmi_hack_brush_select_properties,
|
|
||||||
include={'KEYBOARD'},
|
|
||||||
)[1]
|
|
||||||
elif mode in {'EDIT', 'PARTICLE_EDIT', 'SCULPT_GPENCIL'}:
|
|
||||||
# Doesn't use brushes
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
print("Unsupported mode:", mode)
|
|
||||||
del mode, attr
|
|
||||||
|
|
||||||
else:
|
else:
|
||||||
kmi_found = None
|
kmi_found = None
|
||||||
|
|
||||||
|
|
|
@ -5501,27 +5501,6 @@ def km_sculpt(params):
|
||||||
op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}),
|
op_menu_pie("VIEW3D_MT_sculpt_automasking_pie", {"type": 'A', "alt": True, "value": 'PRESS'}),
|
||||||
op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}),
|
op_menu_pie("VIEW3D_MT_sculpt_face_sets_edit_pie", {"type": 'W', "value": 'PRESS', "alt": True}),
|
||||||
*_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event),
|
*_template_items_context_panel("VIEW3D_PT_sculpt_context_menu", params.context_menu_event),
|
||||||
# Tools
|
|
||||||
("paint.brush_select", {"type": 'V', "value": 'PRESS'},
|
|
||||||
{"properties": [("sculpt_tool", 'DRAW')]}),
|
|
||||||
("paint.brush_select", {"type": 'S', "value": 'PRESS'},
|
|
||||||
{"properties": [("sculpt_tool", 'SMOOTH')]}),
|
|
||||||
("paint.brush_select", {"type": 'P', "value": 'PRESS'},
|
|
||||||
{"properties": [("sculpt_tool", 'PINCH')]}),
|
|
||||||
("paint.brush_select", {"type": 'I', "value": 'PRESS'},
|
|
||||||
{"properties": [("sculpt_tool", 'INFLATE')]}),
|
|
||||||
("paint.brush_select", {"type": 'G', "value": 'PRESS'},
|
|
||||||
{"properties": [("sculpt_tool", 'GRAB')]}),
|
|
||||||
("paint.brush_select", {"type": 'T', "value": 'PRESS', "shift": True},
|
|
||||||
{"properties": [("sculpt_tool", 'SCRAPE')]}),
|
|
||||||
("paint.brush_select", {"type": 'C', "value": 'PRESS'},
|
|
||||||
{"properties": [("sculpt_tool", 'CLAY_STRIPS')]}),
|
|
||||||
("paint.brush_select", {"type": 'C', "value": 'PRESS', "shift": True},
|
|
||||||
{"properties": [("sculpt_tool", 'CREASE')]}),
|
|
||||||
("paint.brush_select", {"type": 'K', "value": 'PRESS'},
|
|
||||||
{"properties": [("sculpt_tool", 'SNAKE_HOOK')]}),
|
|
||||||
("paint.brush_select", {"type": 'M', "value": 'PRESS'},
|
|
||||||
{"properties": [("sculpt_tool", 'MASK'), ("toggle", True), ("create_missing", True)]}),
|
|
||||||
])
|
])
|
||||||
|
|
||||||
# Lasso Masking.
|
# Lasso Masking.
|
||||||
|
|
|
@ -559,7 +559,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
||||||
/* There are different kinds of shortcuts:
|
/* There are different kinds of shortcuts:
|
||||||
*
|
*
|
||||||
* - Direct access to the tool (as if the toolbar button is pressed).
|
* - Direct access to the tool (as if the toolbar button is pressed).
|
||||||
* - The key is bound to a brush type (not the exact brush name).
|
|
||||||
* - The key is assigned to the operator itself
|
* - The key is assigned to the operator itself
|
||||||
* (bypassing the tool, executing the operator).
|
* (bypassing the tool, executing the operator).
|
||||||
*
|
*
|
||||||
|
@ -567,36 +566,6 @@ static uiTooltipData *ui_tooltip_data_from_tool(bContext *C, uiBut *but, bool is
|
||||||
*/
|
*/
|
||||||
std::string shortcut = UI_but_string_get_operator_keymap(*C, *but);
|
std::string shortcut = UI_but_string_get_operator_keymap(*C, *but);
|
||||||
|
|
||||||
if (shortcut.empty()) {
|
|
||||||
const PaintMode paint_mode = BKE_paintmode_get_active_from_context(C);
|
|
||||||
const char *tool_attr = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
|
|
||||||
if (tool_attr != nullptr) {
|
|
||||||
const EnumPropertyItem *items = BKE_paint_get_tool_enum_from_paintmode(paint_mode);
|
|
||||||
const char *tool_id_lstrip = strrchr(tool_id, '.');
|
|
||||||
const int tool_id_offset = tool_id_lstrip ? ((tool_id_lstrip - tool_id) + 1) : 0;
|
|
||||||
const int i = RNA_enum_from_name(items, tool_id + tool_id_offset);
|
|
||||||
|
|
||||||
if (i != -1) {
|
|
||||||
wmOperatorType *ot = WM_operatortype_find("paint.brush_select", true);
|
|
||||||
PointerRNA op_props;
|
|
||||||
WM_operator_properties_create_ptr(&op_props, ot);
|
|
||||||
RNA_enum_set(&op_props, tool_attr, items[i].value);
|
|
||||||
|
|
||||||
/* Check for direct access to the tool. */
|
|
||||||
if (std::optional<std::string> shortcut_brush = WM_key_event_operator_string(
|
|
||||||
C,
|
|
||||||
ot->idname,
|
|
||||||
WM_OP_INVOKE_REGION_WIN,
|
|
||||||
static_cast<IDProperty *>(op_props.data),
|
|
||||||
true))
|
|
||||||
{
|
|
||||||
shortcut = *shortcut_brush;
|
|
||||||
}
|
|
||||||
WM_operator_properties_free(&op_props);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shortcut.empty()) {
|
if (shortcut.empty()) {
|
||||||
/* Check for direct access to the tool. */
|
/* Check for direct access to the tool. */
|
||||||
if (std::optional<std::string> shortcut_toolbar = WM_key_event_operator_string(
|
if (std::optional<std::string> shortcut_toolbar = WM_key_event_operator_string(
|
||||||
|
|
|
@ -754,206 +754,6 @@ static void BRUSH_OT_reset(wmOperatorType *ot)
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int brush_tool(const Brush *brush, size_t tool_offset)
|
|
||||||
{
|
|
||||||
return *(((char *)brush) + tool_offset);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void brush_tool_set(const Brush *brush, size_t tool_offset, int tool)
|
|
||||||
{
|
|
||||||
*(((char *)brush) + tool_offset) = tool;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Brush *brush_tool_cycle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool)
|
|
||||||
{
|
|
||||||
Brush *brush, *first_brush;
|
|
||||||
|
|
||||||
if (!brush_orig && !(brush_orig = static_cast<Brush *>(bmain->brushes.first))) {
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (brush_tool(brush_orig, paint->runtime.tool_offset) != tool) {
|
|
||||||
/* If current brush's tool is different from what we need,
|
|
||||||
* start cycling from the beginning of the list.
|
|
||||||
* Such logic will activate the same exact brush not relating from
|
|
||||||
* which tool user requests other tool.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/* Try to tool-slot first. */
|
|
||||||
first_brush = BKE_paint_toolslots_brush_get(paint, tool);
|
|
||||||
if (first_brush == nullptr) {
|
|
||||||
first_brush = static_cast<Brush *>(bmain->brushes.first);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
/* If user wants to switch to brush with the same tool as
|
|
||||||
* currently active brush do a cycling via all possible
|
|
||||||
* brushes with requested tool. */
|
|
||||||
first_brush = brush_orig->id.next ? static_cast<Brush *>(brush_orig->id.next) :
|
|
||||||
static_cast<Brush *>(bmain->brushes.first);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get the next brush with the active tool */
|
|
||||||
brush = first_brush;
|
|
||||||
do {
|
|
||||||
if ((brush->ob_mode & paint->runtime.ob_mode) &&
|
|
||||||
(brush_tool(brush, paint->runtime.tool_offset) == tool))
|
|
||||||
{
|
|
||||||
return brush;
|
|
||||||
}
|
|
||||||
|
|
||||||
brush = brush->id.next ? static_cast<Brush *>(brush->id.next) :
|
|
||||||
static_cast<Brush *>(bmain->brushes.first);
|
|
||||||
} while (brush != first_brush);
|
|
||||||
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static Brush *brush_tool_toggle(Main *bmain, Paint *paint, Brush *brush_orig, const int tool)
|
|
||||||
{
|
|
||||||
if (!brush_orig || brush_tool(brush_orig, paint->runtime.tool_offset) != tool) {
|
|
||||||
Brush *br;
|
|
||||||
/* if the current brush is not using the desired tool, look
|
|
||||||
* for one that is */
|
|
||||||
br = brush_tool_cycle(bmain, paint, brush_orig, tool);
|
|
||||||
/* store the previously-selected brush */
|
|
||||||
if (br) {
|
|
||||||
br->toggle_brush = brush_orig;
|
|
||||||
}
|
|
||||||
|
|
||||||
return br;
|
|
||||||
}
|
|
||||||
if (brush_orig->toggle_brush) {
|
|
||||||
/* if current brush is using the desired tool, try to toggle
|
|
||||||
* back to the previously selected brush. */
|
|
||||||
return brush_orig->toggle_brush;
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool brush_generic_tool_set(bContext *C,
|
|
||||||
Main *bmain,
|
|
||||||
Paint *paint,
|
|
||||||
const int tool,
|
|
||||||
const char *tool_name,
|
|
||||||
const bool create_missing,
|
|
||||||
const bool toggle)
|
|
||||||
{
|
|
||||||
Brush *brush, *brush_orig = BKE_paint_brush(paint);
|
|
||||||
|
|
||||||
if (toggle) {
|
|
||||||
brush = brush_tool_toggle(bmain, paint, brush_orig, tool);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
brush = brush_tool_cycle(bmain, paint, brush_orig, tool);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (((brush == nullptr) && create_missing) &&
|
|
||||||
((brush_orig == nullptr) || brush_tool(brush_orig, paint->runtime.tool_offset) != tool))
|
|
||||||
{
|
|
||||||
brush = BKE_brush_add(bmain, tool_name, eObjectMode(paint->runtime.ob_mode));
|
|
||||||
id_us_min(&brush->id); /* fake user only */
|
|
||||||
brush_tool_set(brush, paint->runtime.tool_offset, tool);
|
|
||||||
brush->toggle_brush = brush_orig;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (brush) {
|
|
||||||
BKE_paint_brush_set(paint, brush);
|
|
||||||
BKE_paint_invalidate_overlay_all();
|
|
||||||
|
|
||||||
WM_main_add_notifier(NC_BRUSH | NA_EDITED, brush);
|
|
||||||
WM_toolsystem_ref_set_by_id(C, "builtin.brush");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const PaintMode brush_select_paint_modes[] = {
|
|
||||||
PaintMode::Sculpt,
|
|
||||||
PaintMode::Vertex,
|
|
||||||
PaintMode::Weight,
|
|
||||||
PaintMode::Texture3D,
|
|
||||||
PaintMode::GPencil,
|
|
||||||
PaintMode::VertexGPencil,
|
|
||||||
PaintMode::SculptGPencil,
|
|
||||||
PaintMode::WeightGPencil,
|
|
||||||
PaintMode::SculptCurves,
|
|
||||||
};
|
|
||||||
|
|
||||||
static int brush_select_exec(bContext *C, wmOperator *op)
|
|
||||||
{
|
|
||||||
Main *bmain = CTX_data_main(C);
|
|
||||||
Scene *scene = CTX_data_scene(C);
|
|
||||||
const bool create_missing = RNA_boolean_get(op->ptr, "create_missing");
|
|
||||||
const bool toggle = RNA_boolean_get(op->ptr, "toggle");
|
|
||||||
const char *tool_name = "Brush";
|
|
||||||
int tool = 0;
|
|
||||||
|
|
||||||
PaintMode paint_mode = PaintMode::Invalid;
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) {
|
|
||||||
paint_mode = brush_select_paint_modes[i];
|
|
||||||
const char *op_prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
|
|
||||||
PropertyRNA *prop = RNA_struct_find_property(op->ptr, op_prop_id);
|
|
||||||
if (RNA_property_is_set(op->ptr, prop)) {
|
|
||||||
tool = RNA_property_enum_get(op->ptr, prop);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (paint_mode == PaintMode::Invalid) {
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
Paint *paint = BKE_paint_get_active_from_paintmode(scene, paint_mode);
|
|
||||||
if (paint == nullptr) {
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: won't work with asset brushes, needs different main.
|
|
||||||
if (brush_generic_tool_set(C, bmain, paint, tool, tool_name, create_missing, toggle)) {
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void PAINT_OT_brush_select(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
PropertyRNA *prop;
|
|
||||||
|
|
||||||
/* identifiers */
|
|
||||||
ot->name = "Brush Select";
|
|
||||||
ot->description = "Select a paint mode's brush by tool type";
|
|
||||||
ot->idname = "PAINT_OT_brush_select";
|
|
||||||
|
|
||||||
/* api callbacks */
|
|
||||||
ot->exec = brush_select_exec;
|
|
||||||
|
|
||||||
/* flags */
|
|
||||||
ot->flag = 0;
|
|
||||||
|
|
||||||
/* props */
|
|
||||||
/* All properties are hidden, so as not to show the redo panel. */
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(brush_select_paint_modes); i++) {
|
|
||||||
const PaintMode paint_mode = brush_select_paint_modes[i];
|
|
||||||
const char *prop_id = BKE_paint_get_tool_prop_id_from_paintmode(paint_mode);
|
|
||||||
prop = RNA_def_enum(
|
|
||||||
ot->srna, prop_id, BKE_paint_get_tool_enum_from_paintmode(paint_mode), 0, prop_id, "");
|
|
||||||
RNA_def_property_translation_context(
|
|
||||||
prop, BKE_paint_get_tool_enum_translation_context_from_paintmode(paint_mode));
|
|
||||||
RNA_def_property_flag(prop, PROP_HIDDEN);
|
|
||||||
}
|
|
||||||
|
|
||||||
prop = RNA_def_boolean(
|
|
||||||
ot->srna, "toggle", false, "Toggle", "Toggle between two brushes rather than cycling");
|
|
||||||
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
|
|
||||||
prop = RNA_def_boolean(ot->srna,
|
|
||||||
"create_missing",
|
|
||||||
false,
|
|
||||||
"Create Missing",
|
|
||||||
"If the requested brush type does not exist, create a new brush");
|
|
||||||
RNA_def_property_flag(prop, PropertyFlag(PROP_HIDDEN | PROP_SKIP_SAVE));
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace blender::ed::sculpt_paint {
|
namespace blender::ed::sculpt_paint {
|
||||||
|
|
||||||
/**************************** Brush Assets **********************************/
|
/**************************** Brush Assets **********************************/
|
||||||
|
@ -2078,9 +1878,6 @@ void ED_operatortypes_paint()
|
||||||
WM_operatortype_append(BRUSH_OT_asset_update);
|
WM_operatortype_append(BRUSH_OT_asset_update);
|
||||||
WM_operatortype_append(BRUSH_OT_asset_revert);
|
WM_operatortype_append(BRUSH_OT_asset_revert);
|
||||||
|
|
||||||
/* NOTE: particle uses a different system, can be added with existing operators in `wm.py`. */
|
|
||||||
WM_operatortype_append(PAINT_OT_brush_select);
|
|
||||||
|
|
||||||
/* image */
|
/* image */
|
||||||
WM_operatortype_append(PAINT_OT_texture_paint_toggle);
|
WM_operatortype_append(PAINT_OT_texture_paint_toggle);
|
||||||
WM_operatortype_append(PAINT_OT_image_paint);
|
WM_operatortype_append(PAINT_OT_image_paint);
|
||||||
|
|
Loading…
Reference in New Issue