From 9def00a8ca8307c28ba064dbf80869ab00235711 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Mon, 1 Mar 2021 11:27:24 +0100 Subject: Work around T86015: Crash undoing in certain scenario (disabling Global Undo). Simply disable advanced 're-use current blend data` process when loading a memfile step and Global Undo is disabled, since there is no way to ensure we have a proper 'differential' state in the stack then. NOTE: this is a quick work-around to fix the crash, not a satisfying solution by far (pretty sure there can still be crashes if you then re-enable Global Undo afterwards e.g.). --- source/blender/editors/undo/memfile_undo.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/undo/memfile_undo.c b/source/blender/editors/undo/memfile_undo.c index 4fd8c180a4b..9189adaf4d1 100644 --- a/source/blender/editors/undo/memfile_undo.c +++ b/source/blender/editors/undo/memfile_undo.c @@ -152,7 +152,7 @@ static void memfile_undosys_step_decode(struct bContext *C, bool use_old_bmain_data = true; - if (USER_EXPERIMENTAL_TEST(&U, use_undo_legacy)) { + if (USER_EXPERIMENTAL_TEST(&U, use_undo_legacy) || !(U.uiflag & USER_GLOBALUNDO)) { use_old_bmain_data = false; } else if (undo_direction == STEP_REDO) { -- cgit v1.2.3 From e06f5f64aeac914c17cfc267b1335869d0f24309 Mon Sep 17 00:00:00 2001 From: Germano Cavalcante Date: Mon, 1 Mar 2021 09:35:15 -0300 Subject: Cleanup: Use LISTBASE_FOREACH and LISTBASE_FOREACH_MUTABLE macro --- source/blender/editors/space_api/spacetypes.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index c112c678a09..1bd8d13b25b 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -274,9 +274,7 @@ void ED_region_draw_cb_draw(const bContext *C, ARegion *region, int type) void ED_region_draw_cb_remove_by_type(ARegionType *art, void *draw_fn, void (*free)(void *)) { - RegionDrawCB *rdc = art->drawcalls.first; - while (rdc) { - RegionDrawCB *rdc_next = rdc->next; + LISTBASE_FOREACH_MUTABLE (RegionDrawCB *, rdc, &art->drawcalls) { if (rdc->draw == draw_fn) { if (free) { free(rdc->customdata); @@ -284,7 +282,6 @@ void ED_region_draw_cb_remove_by_type(ARegionType *art, void *draw_fn, void (*fr BLI_remlink(&art->drawcalls, rdc); MEM_freeN(rdc); } - rdc = rdc_next; } } -- cgit v1.2.3 From fdb2c24c097d9a4613493659533863efc3c6f809 Mon Sep 17 00:00:00 2001 From: Christoph Lendenfeld Date: Mon, 1 Mar 2021 16:20:55 +0100 Subject: Cleanup: move some drawing code into ed_draw.c Move some drawing code from `area.c` and `ed_util.c` into `ed_draw.c`. This is to support the new generic slider that wil be used in T81785. No functional changes. Reviewed By: #animation_rigging, #user_interface, Severin, sybren Maniphest Tasks: T81785 Differential Revision: https://developer.blender.org/D9313 --- source/blender/editors/gpencil/gpencil_uv.c | 1 + source/blender/editors/include/ED_screen.h | 2 - source/blender/editors/include/ED_space_api.h | 5 - source/blender/editors/include/ED_util.h | 8 + source/blender/editors/mesh/editmesh_bevel.c | 1 + source/blender/editors/mesh/editmesh_inset.c | 1 + source/blender/editors/screen/area.c | 291 ---------------- source/blender/editors/space_clip/clip_draw.c | 1 + source/blender/editors/space_image/image_draw.c | 1 + source/blender/editors/space_image/space_image.c | 1 + .../editors/space_sequencer/sequencer_draw.c | 1 + source/blender/editors/util/CMakeLists.txt | 2 + source/blender/editors/util/ed_draw.c | 385 +++++++++++++++++++++ source/blender/editors/util/ed_util.c | 38 -- 14 files changed, 402 insertions(+), 336 deletions(-) create mode 100644 source/blender/editors/util/ed_draw.c (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/gpencil_uv.c b/source/blender/editors/gpencil/gpencil_uv.c index 677451eaabc..6bd0540a9d4 100644 --- a/source/blender/editors/gpencil/gpencil_uv.c +++ b/source/blender/editors/gpencil/gpencil_uv.c @@ -44,6 +44,7 @@ #include "ED_numinput.h" #include "ED_screen.h" #include "ED_space_api.h" +#include "ED_util.h" #include "ED_view3d.h" #include "DEG_depsgraph.h" diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index deb6b7502c7..61e1f0fdf7d 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -123,8 +123,6 @@ void ED_region_info_draw_multiline(ARegion *region, const char *text_array[], float fill_color[4], const bool full_redraw); -void ED_region_image_metadata_draw( - int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy); void ED_region_image_metadata_panel_draw(struct ImBuf *ibuf, struct uiLayout *layout); void ED_region_grid_draw(struct ARegion *region, float zoomx, float zoomy, float x0, float y0); float ED_region_blend_alpha(struct ARegion *region); diff --git a/source/blender/editors/include/ED_space_api.h b/source/blender/editors/include/ED_space_api.h index c50bbc2f1e9..fc474ea464d 100644 --- a/source/blender/editors/include/ED_space_api.h +++ b/source/blender/editors/include/ED_space_api.h @@ -76,11 +76,6 @@ void ED_region_draw_cb_exit(struct ARegionType *, void *); void ED_region_draw_cb_remove_by_type(struct ARegionType *art, void *draw_fn, void (*free)(void *)); -/* generic callbacks */ -/* ed_util.c */ -void ED_region_draw_mouse_line_cb(const struct bContext *C, - struct ARegion *region, - void *arg_info); #ifdef __cplusplus } diff --git a/source/blender/editors/include/ED_util.h b/source/blender/editors/include/ED_util.h index 073186f6335..953f26aa45f 100644 --- a/source/blender/editors/include/ED_util.h +++ b/source/blender/editors/include/ED_util.h @@ -53,6 +53,14 @@ void ED_spacedata_id_remap(struct ScrArea *area, void ED_operatortypes_edutils(void); +/* Drawing */ +void ED_region_draw_mouse_line_cb(const struct bContext *C, + struct ARegion *region, + void *arg_info); + +void ED_region_image_metadata_draw( + int x, int y, struct ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy); + /* ************** XXX OLD CRUFT WARNING ************* */ void apply_keyb_grid( diff --git a/source/blender/editors/mesh/editmesh_bevel.c b/source/blender/editors/mesh/editmesh_bevel.c index 66a7b97b440..340a7ae92ff 100644 --- a/source/blender/editors/mesh/editmesh_bevel.c +++ b/source/blender/editors/mesh/editmesh_bevel.c @@ -50,6 +50,7 @@ #include "ED_screen.h" #include "ED_space_api.h" #include "ED_transform.h" +#include "ED_util.h" #include "ED_view3d.h" #include "mesh_intern.h" /* own include */ diff --git a/source/blender/editors/mesh/editmesh_inset.c b/source/blender/editors/mesh/editmesh_inset.c index 9000a942e50..73d79805f60 100644 --- a/source/blender/editors/mesh/editmesh_inset.c +++ b/source/blender/editors/mesh/editmesh_inset.c @@ -46,6 +46,7 @@ #include "ED_screen.h" #include "ED_space_api.h" #include "ED_transform.h" +#include "ED_util.h" #include "ED_view3d.h" #include "mesh_intern.h" /* own include */ diff --git a/source/blender/editors/screen/area.c b/source/blender/editors/screen/area.c index 2c71345699f..bd2b1c4c553 100644 --- a/source/blender/editors/screen/area.c +++ b/source/blender/editors/screen/area.c @@ -3516,297 +3516,6 @@ void ED_region_info_draw(ARegion *region, ED_region_info_draw_multiline(region, text_array, fill_color, full_redraw); } -#define MAX_METADATA_STR 1024 - -static const char *meta_data_list[] = { - "File", - "Strip", - "Date", - "RenderTime", - "Note", - "Marker", - "Time", - "Frame", - "Camera", - "Scene", -}; - -BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset) -{ - return (IMB_metadata_get_field( - ibuf->metadata, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) && - r_str[0]); -} - -BLI_INLINE bool metadata_is_custom_drawable(const char *field) -{ - /* Metadata field stored by Blender for multilayer EXR images. Is rather - * useless to be viewed all the time. Can still be seen in the Metadata - * panel. */ - if (STREQ(field, "BlenderMultiChannel")) { - return false; - } - /* Is almost always has value "scanlineimage", also useless to be seen - * all the time. */ - if (STREQ(field, "type")) { - return false; - } - return !BKE_stamp_is_known_field(field); -} - -typedef struct MetadataCustomDrawContext { - int fontid; - int xmin, ymin; - int vertical_offset; - int current_y; -} MetadataCustomDrawContext; - -static void metadata_custom_draw_fields(const char *field, const char *value, void *ctx_v) -{ - if (!metadata_is_custom_drawable(field)) { - return; - } - MetadataCustomDrawContext *ctx = (MetadataCustomDrawContext *)ctx_v; - char temp_str[MAX_METADATA_STR]; - BLI_snprintf(temp_str, MAX_METADATA_STR, "%s: %s", field, value); - BLF_position(ctx->fontid, ctx->xmin, ctx->ymin + ctx->current_y, 0.0f); - BLF_draw(ctx->fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); - ctx->current_y += ctx->vertical_offset; -} - -static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const bool is_top) -{ - char temp_str[MAX_METADATA_STR]; - int ofs_y = 0; - const float height = BLF_height_max(fontid); - const float margin = height / 8; - const float vertical_offset = (height + margin); - - /* values taking margins into account */ - const float descender = BLF_descender(fontid); - const float xmin = (rect->xmin + margin); - const float xmax = (rect->xmax - margin); - const float ymin = (rect->ymin + margin) - descender; - const float ymax = (rect->ymax - margin) - descender; - - if (is_top) { - for (int i = 0; i < 4; i++) { - /* first line */ - if (i == 0) { - bool do_newline = false; - int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[0]); - if (metadata_is_valid(ibuf, temp_str, 0, len)) { - BLF_position(fontid, xmin, ymax - vertical_offset, 0.0f); - BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); - do_newline = true; - } - - len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[1]); - if (metadata_is_valid(ibuf, temp_str, 1, len)) { - int line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); - BLF_position(fontid, xmax - line_width, ymax - vertical_offset, 0.0f); - BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); - do_newline = true; - } - - if (do_newline) { - ofs_y += vertical_offset; - } - } /* Strip */ - else if (ELEM(i, 1, 2)) { - int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]); - if (metadata_is_valid(ibuf, temp_str, i + 1, len)) { - BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f); - BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); - ofs_y += vertical_offset; - } - } /* Note (wrapped) */ - else if (i == 3) { - int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]); - if (metadata_is_valid(ibuf, temp_str, i + 1, len)) { - struct ResultBLF info; - BLF_enable(fontid, BLF_WORD_WRAP); - BLF_wordwrap(fontid, ibuf->x - (margin * 2)); - BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f); - BLF_draw_ex(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX, &info); - BLF_wordwrap(fontid, 0); - BLF_disable(fontid, BLF_WORD_WRAP); - ofs_y += vertical_offset * info.lines; - } - } - else { - int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]); - if (metadata_is_valid(ibuf, temp_str, i + 1, len)) { - int line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); - BLF_position(fontid, xmax - line_width, ymax - vertical_offset - ofs_y, 0.0f); - BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); - ofs_y += vertical_offset; - } - } - } - } - else { - MetadataCustomDrawContext ctx; - ctx.fontid = fontid; - ctx.xmin = xmin; - ctx.ymin = ymin; - ctx.current_y = ofs_y; - ctx.vertical_offset = vertical_offset; - IMB_metadata_foreach(ibuf, metadata_custom_draw_fields, &ctx); - int ofs_x = 0; - ofs_y = ctx.current_y; - for (int i = 5; i < 10; i++) { - int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i]); - if (metadata_is_valid(ibuf, temp_str, i, len)) { - BLF_position(fontid, xmin + ofs_x, ymin + ofs_y, 0.0f); - BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); - - ofs_x += BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX) + UI_UNIT_X; - } - } - } -} - -typedef struct MetadataCustomCountContext { - int count; -} MetadataCustomCountContext; - -static void metadata_custom_count_fields(const char *field, const char *UNUSED(value), void *ctx_v) -{ - if (!metadata_is_custom_drawable(field)) { - return; - } - MetadataCustomCountContext *ctx = (MetadataCustomCountContext *)ctx_v; - ctx->count++; -} - -static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top) -{ - const float height = BLF_height_max(fontid); - const float margin = (height / 8); - char str[MAX_METADATA_STR] = ""; - short count = 0; - - if (is_top) { - if (metadata_is_valid(ibuf, str, 0, 0) || metadata_is_valid(ibuf, str, 1, 0)) { - count++; - } - for (int i = 2; i < 5; i++) { - if (metadata_is_valid(ibuf, str, i, 0)) { - if (i == 4) { - struct { - struct ResultBLF info; - rctf rect; - } wrap; - - BLF_enable(fontid, BLF_WORD_WRAP); - BLF_wordwrap(fontid, ibuf->x - (margin * 2)); - BLF_boundbox_ex(fontid, str, sizeof(str), &wrap.rect, &wrap.info); - BLF_wordwrap(fontid, 0); - BLF_disable(fontid, BLF_WORD_WRAP); - - count += wrap.info.lines; - } - else { - count++; - } - } - } - } - else { - for (int i = 5; i < 10; i++) { - if (metadata_is_valid(ibuf, str, i, 0)) { - count = 1; - break; - } - } - MetadataCustomCountContext ctx; - ctx.count = 0; - IMB_metadata_foreach(ibuf, metadata_custom_count_fields, &ctx); - count += ctx.count; - } - - if (count) { - return (height + margin) * count; - } - - return 0; -} - -#undef MAX_METADATA_STR - -void ED_region_image_metadata_draw( - int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy) -{ - const uiStyle *style = UI_style_get_dpi(); - - if (!ibuf->metadata) { - return; - } - - /* find window pixel coordinates of origin */ - GPU_matrix_push(); - - /* offset and zoom using ogl */ - GPU_matrix_translate_2f(x, y); - GPU_matrix_scale_2f(zoomx, zoomy); - - BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f * U.pixelsize, U.dpi); - - /* *** upper box*** */ - - /* get needed box height */ - float box_y = metadata_box_height_get(ibuf, blf_mono_font, true); - - if (box_y) { - /* set up rect */ - rctf rect; - BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymax, frame->ymax + box_y); - /* draw top box */ - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColor(TH_METADATA_BG); - immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); - immUnbindProgram(); - - BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax); - BLF_enable(blf_mono_font, BLF_CLIPPING); - - UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT); - metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true); - - BLF_disable(blf_mono_font, BLF_CLIPPING); - } - - /* *** lower box*** */ - - box_y = metadata_box_height_get(ibuf, blf_mono_font, false); - - if (box_y) { - /* set up box rect */ - rctf rect; - BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymin - box_y, frame->ymin); - /* draw top box */ - GPUVertFormat *format = immVertexFormat(); - uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - immUniformThemeColor(TH_METADATA_BG); - immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); - immUnbindProgram(); - - BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax); - BLF_enable(blf_mono_font, BLF_CLIPPING); - - UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT); - metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false); - - BLF_disable(blf_mono_font, BLF_CLIPPING); - } - - GPU_matrix_pop(); -} - typedef struct MetadataPanelDrawContext { uiLayout *layout; } MetadataPanelDrawContext; diff --git a/source/blender/editors/space_clip/clip_draw.c b/source/blender/editors/space_clip/clip_draw.c index cbe8ec4ba00..471b4a4bf5b 100644 --- a/source/blender/editors/space_clip/clip_draw.c +++ b/source/blender/editors/space_clip/clip_draw.c @@ -46,6 +46,7 @@ #include "ED_gpencil.h" #include "ED_mask.h" #include "ED_screen.h" +#include "ED_util.h" #include "BIF_glutil.h" diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c index 67d5055ec65..2be6d31369c 100644 --- a/source/blender/editors/space_image/image_draw.c +++ b/source/blender/editors/space_image/image_draw.c @@ -68,6 +68,7 @@ #include "ED_mask.h" #include "ED_render.h" #include "ED_screen.h" +#include "ED_util.h" #include "UI_interface.h" #include "UI_resources.h" diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 95ca8aba399..c51d2f25efd 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -54,6 +54,7 @@ #include "ED_screen.h" #include "ED_space_api.h" #include "ED_transform.h" +#include "ED_util.h" #include "ED_uvedit.h" #include "WM_api.h" diff --git a/source/blender/editors/space_sequencer/sequencer_draw.c b/source/blender/editors/space_sequencer/sequencer_draw.c index b9fb577eb43..9828368ccf7 100644 --- a/source/blender/editors/space_sequencer/sequencer_draw.c +++ b/source/blender/editors/space_sequencer/sequencer_draw.c @@ -66,6 +66,7 @@ #include "ED_sequencer.h" #include "ED_space_api.h" #include "ED_time_scrub_ui.h" +#include "ED_util.h" #include "BIF_glutil.h" diff --git a/source/blender/editors/util/CMakeLists.txt b/source/blender/editors/util/CMakeLists.txt index 38655b8490e..7d7d10004a3 100644 --- a/source/blender/editors/util/CMakeLists.txt +++ b/source/blender/editors/util/CMakeLists.txt @@ -21,6 +21,7 @@ set(INC ../../blenkernel ../../blenlib ../../blentranslation + ../../blenfont ../../bmesh ../../depsgraph ../../gpu @@ -36,6 +37,7 @@ set(INC set(SRC + ed_draw.c ed_transverts.c ed_util.c ed_util_imbuf.c diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c new file mode 100644 index 00000000000..94adba36664 --- /dev/null +++ b/source/blender/editors/util/ed_draw.c @@ -0,0 +1,385 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edutil + */ + +#include +#include +#include + +#include "MEM_guardedalloc.h" + +#include "BLI_listbase.h" +#include "BLI_path_util.h" +#include "BLI_rect.h" +#include "BLI_string.h" +#include "BLI_utildefines.h" + +#include "BKE_context.h" +#include "BKE_image.h" + +#include "BLF_api.h" + +#include "IMB_imbuf_types.h" +#include "IMB_metadata.h" + +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_util.h" + +#include "GPU_immediate.h" +#include "GPU_matrix.h" +#include "GPU_state.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "RNA_access.h" +#include "WM_api.h" +#include "WM_types.h" + +/** + * Callback that draws a line between the mouse and a position given as the initial argument. + */ +void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *region, void *arg_info) +{ + wmWindow *win = CTX_wm_window(C); + const float *mval_src = (float *)arg_info; + const float mval_dst[2] = { + win->eventstate->x - region->winrct.xmin, + win->eventstate->y - region->winrct.ymin, + }; + + const uint shdr_pos = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + + GPU_line_width(1.0f); + + immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); + + float viewport_size[4]; + GPU_viewport_size_get_f(viewport_size); + immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC); + + immUniform1i("colors_len", 0); /* "simple" mode */ + immUniformThemeColor3(TH_VIEW_OVERLAY); + immUniform1f("dash_width", 6.0f); + immUniform1f("dash_factor", 0.5f); + + immBegin(GPU_PRIM_LINES, 2); + immVertex2fv(shdr_pos, mval_src); + immVertex2fv(shdr_pos, mval_dst); + immEnd(); + + immUnbindProgram(); +} + +#define MAX_METADATA_STR 1024 + +static const char *meta_data_list[] = { + "File", + "Strip", + "Date", + "RenderTime", + "Note", + "Marker", + "Time", + "Frame", + "Camera", + "Scene", +}; + +BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int offset) +{ + return (IMB_metadata_get_field( + ibuf->metadata, meta_data_list[index], r_str + offset, MAX_METADATA_STR - offset) && + r_str[0]); +} + +BLI_INLINE bool metadata_is_custom_drawable(const char *field) +{ + /* Metadata field stored by Blender for multilayer EXR images. Is rather + * useless to be viewed all the time. Can still be seen in the Metadata + * panel. */ + if (STREQ(field, "BlenderMultiChannel")) { + return false; + } + /* Is almost always has value "scanlineimage", also useless to be seen + * all the time. */ + if (STREQ(field, "type")) { + return false; + } + return !BKE_stamp_is_known_field(field); +} + +typedef struct MetadataCustomDrawContext { + int fontid; + int xmin, ymin; + int vertical_offset; + int current_y; +} MetadataCustomDrawContext; + +static void metadata_custom_draw_fields(const char *field, const char *value, void *ctx_v) +{ + if (!metadata_is_custom_drawable(field)) { + return; + } + MetadataCustomDrawContext *ctx = (MetadataCustomDrawContext *)ctx_v; + char temp_str[MAX_METADATA_STR]; + BLI_snprintf(temp_str, MAX_METADATA_STR, "%s: %s", field, value); + BLF_position(ctx->fontid, ctx->xmin, ctx->ymin + ctx->current_y, 0.0f); + BLF_draw(ctx->fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + ctx->current_y += ctx->vertical_offset; +} + +static void metadata_draw_imbuf(ImBuf *ibuf, const rctf *rect, int fontid, const bool is_top) +{ + char temp_str[MAX_METADATA_STR]; + int ofs_y = 0; + const float height = BLF_height_max(fontid); + const float margin = height / 8; + const float vertical_offset = (height + margin); + + /* values taking margins into account */ + const float descender = BLF_descender(fontid); + const float xmin = (rect->xmin + margin); + const float xmax = (rect->xmax - margin); + const float ymin = (rect->ymin + margin) - descender; + const float ymax = (rect->ymax - margin) - descender; + + if (is_top) { + for (int i = 0; i < 4; i++) { + /* first line */ + if (i == 0) { + bool do_newline = false; + int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[0]); + if (metadata_is_valid(ibuf, temp_str, 0, len)) { + BLF_position(fontid, xmin, ymax - vertical_offset, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + do_newline = true; + } + + len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[1]); + if (metadata_is_valid(ibuf, temp_str, 1, len)) { + int line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + BLF_position(fontid, xmax - line_width, ymax - vertical_offset, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + do_newline = true; + } + + if (do_newline) { + ofs_y += vertical_offset; + } + } /* Strip */ + else if (i == 1 || i == 2) { + int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]); + if (metadata_is_valid(ibuf, temp_str, i + 1, len)) { + BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + ofs_y += vertical_offset; + } + } /* Note (wrapped) */ + else if (i == 3) { + int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]); + if (metadata_is_valid(ibuf, temp_str, i + 1, len)) { + struct ResultBLF info; + BLF_enable(fontid, BLF_WORD_WRAP); + BLF_wordwrap(fontid, ibuf->x - (margin * 2)); + BLF_position(fontid, xmin, ymax - vertical_offset - ofs_y, 0.0f); + BLF_draw_ex(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX, &info); + BLF_wordwrap(fontid, 0); + BLF_disable(fontid, BLF_WORD_WRAP); + ofs_y += vertical_offset * info.lines; + } + } + else { + int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i + 1]); + if (metadata_is_valid(ibuf, temp_str, i + 1, len)) { + int line_width = BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + BLF_position(fontid, xmax - line_width, ymax - vertical_offset - ofs_y, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + ofs_y += vertical_offset; + } + } + } + } + else { + MetadataCustomDrawContext ctx; + ctx.fontid = fontid; + ctx.xmin = xmin; + ctx.ymin = ymin; + ctx.current_y = ofs_y; + ctx.vertical_offset = vertical_offset; + IMB_metadata_foreach(ibuf, metadata_custom_draw_fields, &ctx); + int ofs_x = 0; + ofs_y = ctx.current_y; + for (int i = 5; i < 10; i++) { + int len = BLI_snprintf_rlen(temp_str, MAX_METADATA_STR, "%s: ", meta_data_list[i]); + if (metadata_is_valid(ibuf, temp_str, i, len)) { + BLF_position(fontid, xmin + ofs_x, ymin + ofs_y, 0.0f); + BLF_draw(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX); + + ofs_x += BLF_width(fontid, temp_str, BLF_DRAW_STR_DUMMY_MAX) + UI_UNIT_X; + } + } + } +} + +typedef struct MetadataCustomCountContext { + int count; +} MetadataCustomCountContext; + +static void metadata_custom_count_fields(const char *field, const char *UNUSED(value), void *ctx_v) +{ + if (!metadata_is_custom_drawable(field)) { + return; + } + MetadataCustomCountContext *ctx = (MetadataCustomCountContext *)ctx_v; + ctx->count++; +} + +static float metadata_box_height_get(ImBuf *ibuf, int fontid, const bool is_top) +{ + const float height = BLF_height_max(fontid); + const float margin = (height / 8); + char str[MAX_METADATA_STR] = ""; + short count = 0; + + if (is_top) { + if (metadata_is_valid(ibuf, str, 0, 0) || metadata_is_valid(ibuf, str, 1, 0)) { + count++; + } + for (int i = 2; i < 5; i++) { + if (metadata_is_valid(ibuf, str, i, 0)) { + if (i == 4) { + struct { + struct ResultBLF info; + rctf rect; + } wrap; + + BLF_enable(fontid, BLF_WORD_WRAP); + BLF_wordwrap(fontid, ibuf->x - (margin * 2)); + BLF_boundbox_ex(fontid, str, sizeof(str), &wrap.rect, &wrap.info); + BLF_wordwrap(fontid, 0); + BLF_disable(fontid, BLF_WORD_WRAP); + + count += wrap.info.lines; + } + else { + count++; + } + } + } + } + else { + for (int i = 5; i < 10; i++) { + if (metadata_is_valid(ibuf, str, i, 0)) { + count = 1; + break; + } + } + MetadataCustomCountContext ctx; + ctx.count = 0; + IMB_metadata_foreach(ibuf, metadata_custom_count_fields, &ctx); + count += ctx.count; + } + + if (count) { + return (height + margin) * count; + } + + return 0; +} + +/* Should be kept in sync with BKE_image_stamp_buf */ +void ED_region_image_metadata_draw( + int x, int y, ImBuf *ibuf, const rctf *frame, float zoomx, float zoomy) +{ + const uiStyle *style = UI_style_get_dpi(); + + if (!ibuf->metadata) { + return; + } + + /* find window pixel coordinates of origin */ + GPU_matrix_push(); + + /* offset and zoom using ogl */ + GPU_matrix_translate_2f(x, y); + GPU_matrix_scale_2f(zoomx, zoomy); + + BLF_size(blf_mono_font, style->widgetlabel.points * 1.5f * U.pixelsize, U.dpi); + + /* *** upper box*** */ + + /* get needed box height */ + float box_y = metadata_box_height_get(ibuf, blf_mono_font, true); + + if (box_y) { + /* set up rect */ + rctf rect; + BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymax, frame->ymax + box_y); + /* draw top box */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor(TH_METADATA_BG); + immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + immUnbindProgram(); + + BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + BLF_enable(blf_mono_font, BLF_CLIPPING); + + UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT); + metadata_draw_imbuf(ibuf, &rect, blf_mono_font, true); + + BLF_disable(blf_mono_font, BLF_CLIPPING); + } + + /* *** lower box*** */ + + box_y = metadata_box_height_get(ibuf, blf_mono_font, false); + + if (box_y) { + /* set up box rect */ + rctf rect; + BLI_rctf_init(&rect, frame->xmin, frame->xmax, frame->ymin - box_y, frame->ymin); + /* draw top box */ + GPUVertFormat *format = immVertexFormat(); + uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + immUniformThemeColor(TH_METADATA_BG); + immRectf(pos, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + immUnbindProgram(); + + BLF_clipping(blf_mono_font, rect.xmin, rect.ymin, rect.xmax, rect.ymax); + BLF_enable(blf_mono_font, BLF_CLIPPING); + + UI_FontThemeColor(blf_mono_font, TH_METADATA_TEXT); + metadata_draw_imbuf(ibuf, &rect, blf_mono_font, false); + + BLF_disable(blf_mono_font, BLF_CLIPPING); + } + + GPU_matrix_pop(); +} + +#undef MAX_METADATA_STR diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 695db9ba246..9903711834a 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -434,44 +434,6 @@ void unpack_menu(bContext *C, UI_popup_menu_end(C, pup); } -/* ********************* generic callbacks for drawcall api *********************** */ - -/** - * Callback that draws a line between the mouse and a position given as the initial argument. - */ -void ED_region_draw_mouse_line_cb(const bContext *C, ARegion *region, void *arg_info) -{ - wmWindow *win = CTX_wm_window(C); - const float *mval_src = (float *)arg_info; - const float mval_dst[2] = { - win->eventstate->x - region->winrct.xmin, - win->eventstate->y - region->winrct.ymin, - }; - - const uint shdr_pos = GPU_vertformat_attr_add( - immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - - GPU_line_width(1.0f); - - immBindBuiltinProgram(GPU_SHADER_2D_LINE_DASHED_UNIFORM_COLOR); - - float viewport_size[4]; - GPU_viewport_size_get_f(viewport_size); - immUniform2f("viewport_size", viewport_size[2] / UI_DPI_FAC, viewport_size[3] / UI_DPI_FAC); - - immUniform1i("colors_len", 0); /* "simple" mode */ - immUniformThemeColor3(TH_VIEW_OVERLAY); - immUniform1f("dash_width", 6.0f); - immUniform1f("dash_factor", 0.5f); - - immBegin(GPU_PRIM_LINES, 2); - immVertex2fv(shdr_pos, mval_src); - immVertex2fv(shdr_pos, mval_dst); - immEnd(); - - immUnbindProgram(); -} - /** * Use to free ID references within runtime data (stored outside of DNA) * -- cgit v1.2.3 From b35a3933eee7b8f1977ccd711c1ecd862bf618e5 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Mon, 1 Mar 2021 16:24:07 +0100 Subject: Build-system: Use C-linkage for internal interface header That way the header can be included in C++ files. --- source/blender/editors/interface/interface_intern.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 3da66d45abd..7e931eae749 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -31,6 +31,10 @@ #include "UI_interface.h" #include "UI_resources.h" +#ifdef __cplusplus +extern "C" { +#endif + struct ARegion; struct AnimationEvalContext; struct CurveMapping; @@ -1200,3 +1204,7 @@ bool ui_jump_to_target_button_poll(struct bContext *C); /* interface_queries.c */ void ui_interface_tag_script_reload_queries(void); + +#ifdef __cplusplus +} +#endif -- cgit v1.2.3 From b279fef85d1a561ceb71e2cdce458bd44b4d853a Mon Sep 17 00:00:00 2001 From: Philipp Oeser Date: Wed, 3 Feb 2021 14:39:24 +0100 Subject: Geometry Nodes: show "Show Texture in texture tab" button This enables the quick access button [to show the relevant Texture in the Properties Editor] for textures used in geometry nodes. This goes in line to what we do for other textures: - modifier textures have this button - particle textures have this button - brush textures will soon have it, too (see D9813) When outside of the Properties Editor, the button will always show (if a texture is actually assigned), but will be inactive if no suiting Properties Editor to show the texture in can be found. Note this also changes the behavior to not show the button if _no_ texture is assigned (as in: we are still showing the "New" button). Previously it was always there (e.g. for modifier textures), even if it would take us to an empty texture tab. (Sure, we could add a texture there then, but imho it makes more sense to just start showing it once a texture is already there) For this to work with geometry nodes, the following chages were done: - implement foreachTexLink for geonode modifiers - new buttons_texture_user_node_property_add() that stores prop as well as node - also use NODE_ACTIVE_TEXTURE flag in geometry nodetrees notes: - this still uses the first suiting (as in: pinning does not interfere) Properties Editor it finds, this should (maybe?) find the _closest_ Property Editor instead (see related feedback in D9813). - this will already show the button for brush textures as well (disabled), but there is another mandatory change in an upcomming commit to make it work there as well (see D9813) ref. T85278 Maniphest Tasks: T85278 Differential Revision: https://developer.blender.org/D10293 --- .../editors/space_buttons/buttons_texture.c | 251 ++++++++++++++++----- 1 file changed, 194 insertions(+), 57 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_buttons/buttons_texture.c b/source/blender/editors/space_buttons/buttons_texture.c index 4847e8738df..43128ed00fa 100644 --- a/source/blender/editors/space_buttons/buttons_texture.c +++ b/source/blender/editors/space_buttons/buttons_texture.c @@ -64,13 +64,42 @@ #include "ED_screen.h" #include "WM_api.h" +#include "WM_types.h" #include "../interface/interface_intern.h" #include "buttons_intern.h" /* own include */ +static ScrArea *find_area_properties(const bContext *C); +static SpaceProperties *find_space_properties(const bContext *C); + /************************* Texture User **************************/ +static void buttons_texture_user_node_property_add(ListBase *users, + ID *id, + PointerRNA ptr, + PropertyRNA *prop, + bNodeTree *ntree, + bNode *node, + const char *category, + int icon, + const char *name) +{ + ButsTextureUser *user = MEM_callocN(sizeof(ButsTextureUser), "ButsTextureUser"); + + user->id = id; + user->ptr = ptr; + user->prop = prop; + user->ntree = ntree; + user->node = node; + user->category = category; + user->icon = icon; + user->name = name; + user->index = BLI_listbase_count(users); + + BLI_addtail(users, user); +} + static void buttons_texture_user_property_add(ListBase *users, ID *id, PointerRNA ptr, @@ -139,20 +168,66 @@ static void buttons_texture_users_find_nodetree(ListBase *users, } } +static void buttons_texture_modifier_geonodes_users_add(Object *ob, + NodesModifierData *nmd, + bNodeTree *node_tree, + ListBase *users) +{ + PointerRNA ptr; + PropertyRNA *prop; + + LISTBASE_FOREACH (bNode *, node, &node_tree->nodes) { + if (node->type == NODE_GROUP && node->id) { + /* Recurse into the node group */ + buttons_texture_modifier_geonodes_users_add(ob, nmd, (bNodeTree *)node->id, users); + } + else if (node->type == GEO_NODE_ATTRIBUTE_SAMPLE_TEXTURE) { + RNA_pointer_create(&node_tree->id, &RNA_Node, node, &ptr); + prop = RNA_struct_find_property(&ptr, "texture"); + if (prop == NULL) { + continue; + } + + PointerRNA texptr = RNA_property_pointer_get(&ptr, prop); + Tex *tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? (Tex *)texptr.data : NULL; + if (tex != NULL) { + buttons_texture_user_node_property_add(users, + &ob->id, + ptr, + prop, + node_tree, + node, + N_("Geometry Nodes"), + RNA_struct_ui_icon(ptr.type), + nmd->modifier.name); + } + } + } +} + static void buttons_texture_modifier_foreach(void *userData, Object *ob, ModifierData *md, const char *propname) { - PointerRNA ptr; - PropertyRNA *prop; ListBase *users = userData; - RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr); - prop = RNA_struct_find_property(&ptr, propname); + if (md->type == eModifierType_Nodes) { + NodesModifierData *nmd = (NodesModifierData *)md; + if (nmd->node_group != NULL) { + buttons_texture_modifier_geonodes_users_add(ob, nmd, nmd->node_group, users); + } + } + else { + PointerRNA ptr; + PropertyRNA *prop; - buttons_texture_user_property_add( - users, &ob->id, ptr, prop, N_("Modifiers"), RNA_struct_ui_icon(ptr.type), md->name); + RNA_pointer_create(&ob->id, &RNA_Modifier, md, &ptr); + prop = RNA_struct_find_property(&ptr, propname); + + buttons_texture_user_property_add( + users, &ob->id, ptr, prop, N_("Modifiers"), RNA_struct_ui_icon(ptr.type), md->name); + } } static void buttons_texture_modifier_gpencil_foreach(void *userData, @@ -325,31 +400,32 @@ void buttons_texture_context_compute(const bContext *C, SpaceProperties *sbuts) ct->texture = NULL; if (ct->user) { + if (ct->user->node != NULL) { + /* Detect change of active texture node in same node tree, in that + * case we also automatically switch to the other node. */ + if ((ct->user->node->flag & NODE_ACTIVE_TEXTURE) == 0) { + ButsTextureUser *user; + for (user = ct->users.first; user; user = user->next) { + if (user->ntree == ct->user->ntree && user->node != ct->user->node) { + if (user->node->flag & NODE_ACTIVE_TEXTURE) { + ct->user = user; + ct->index = BLI_findindex(&ct->users, user); + break; + } + } + } + } + } if (ct->user->ptr.data) { PointerRNA texptr; Tex *tex; - /* get texture datablock pointer if it's a property */ + /* Get texture datablock pointer if it's a property. */ texptr = RNA_property_pointer_get(&ct->user->ptr, ct->user->prop); tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL; ct->texture = tex; } - else if (ct->user->node && !(ct->user->node->flag & NODE_ACTIVE_TEXTURE)) { - ButsTextureUser *user; - - /* detect change of active texture node in same node tree, in that - * case we also automatically switch to the other node */ - for (user = ct->users.first; user; user = user->next) { - if (user->ntree == ct->user->ntree && user->node != ct->user->node) { - if (user->node->flag & NODE_ACTIVE_TEXTURE) { - ct->user = user; - ct->index = BLI_findindex(&ct->users, user); - break; - } - } - } - } } } } @@ -357,7 +433,7 @@ void buttons_texture_context_compute(const bContext *C, SpaceProperties *sbuts) static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg)) { /* callback when selecting a texture user in the menu */ - SpaceProperties *sbuts = CTX_wm_space_properties(C); + SpaceProperties *sbuts = find_space_properties(C); ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL; ButsTextureUser *user = (ButsTextureUser *)user_p; PointerRNA texptr; @@ -371,8 +447,15 @@ static void template_texture_select(bContext *C, void *user_p, void *UNUSED(arg) if (user->node) { ED_node_set_active(CTX_data_main(C), user->ntree, user->node, NULL); ct->texture = NULL; + + /* Not totally sure if we should also change selection? */ + LISTBASE_FOREACH (bNode *, node, &user->ntree->nodes) { + nodeSetSelected(node, false); + } + nodeSetSelected(user->node, true); + WM_event_add_notifier(C, NC_NODE | NA_SELECTED, NULL); } - else { + if (user->ptr.data) { texptr = RNA_property_pointer_get(&user->ptr, user->prop); tex = (RNA_struct_is_a(texptr.type, &RNA_Texture)) ? texptr.data : NULL; @@ -511,16 +594,53 @@ void uiTemplateTextureUser(uiLayout *layout, bContext *C) /************************* Texture Show **************************/ +static ScrArea *find_area_properties(const bContext *C) +{ + bScreen *screen = CTX_wm_screen(C); + Object *ob = CTX_data_active_object(C); + + LISTBASE_FOREACH (ScrArea *, area, &screen->areabase) { + if (area->spacetype == SPACE_PROPERTIES) { + /* Only if unpinned, or if pinned object matches. */ + SpaceProperties *sbuts = area->spacedata.first; + ID *pinid = sbuts->pinid; + if (pinid == NULL || ((GS(pinid->name) == ID_OB) && (Object *)pinid == ob)) { + return area; + } + } + } + + return NULL; +} + +static SpaceProperties *find_space_properties(const bContext *C) +{ + ScrArea *area = find_area_properties(C); + if (area != NULL) { + return area->spacedata.first; + } + + return NULL; +} + static void template_texture_show(bContext *C, void *data_p, void *prop_p) { - SpaceProperties *sbuts = CTX_wm_space_properties(C); - ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL; - ButsTextureUser *user; + if (data_p == NULL || prop_p == NULL) { + return; + } + + ScrArea *area = find_area_properties(C); + if (area == NULL) { + return; + } + SpaceProperties *sbuts = (SpaceProperties *)area->spacedata.first; + ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL; if (!ct) { return; } + ButsTextureUser *user; for (user = ct->users.first; user; user = user->next) { if (user->ptr.data == data_p && user->prop == prop_p) { break; @@ -537,48 +657,65 @@ static void template_texture_show(bContext *C, void *data_p, void *prop_p) sbuts->preview = 1; /* redraw editor */ - ED_area_tag_redraw(CTX_wm_area(C)); + ED_area_tag_redraw(area); } } +/* Button to quickly show texture in Properties Editor texture tab. */ void uiTemplateTextureShow(uiLayout *layout, const bContext *C, PointerRNA *ptr, PropertyRNA *prop) { - /* button to quickly show texture in texture tab */ - SpaceProperties *sbuts = CTX_wm_space_properties(C); - ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL; - ButsTextureUser *user; + /* Only show the button if there is actually a texture assigned. */ + Tex *texture = RNA_property_pointer_get(ptr, prop).data; + if (texture == NULL) { + return; + } - /* only show button in other tabs in properties editor */ - if (!ct || sbuts->mainb == BCONTEXT_TEXTURE) { + /* Only show the button if we are not in the Properties Editor's texture tab. */ + SpaceProperties *sbuts_context = CTX_wm_space_properties(C); + if (sbuts_context != NULL && sbuts_context->mainb == BCONTEXT_TEXTURE) { return; } + SpaceProperties *sbuts = find_space_properties(C); + ButsContextTexture *ct = (sbuts) ? sbuts->texuser : NULL; + /* find corresponding texture user */ - for (user = ct->users.first; user; user = user->next) { - if (user->ptr.data == ptr->data && user->prop == prop) { - break; + ButsTextureUser *user; + bool user_found = false; + if (ct != NULL) { + for (user = ct->users.first; user; user = user->next) { + if (user->ptr.data == ptr->data && user->prop == prop) { + user_found = true; + break; + } } } - /* draw button */ - if (user) { - uiBlock *block = uiLayoutGetBlock(layout); - uiBut *but; - - but = uiDefIconBut(block, - UI_BTYPE_BUT, - 0, - ICON_PROPERTIES, - 0, - 0, - UI_UNIT_X, - UI_UNIT_Y, - NULL, - 0.0, - 0.0, - 0.0, - 0.0, - TIP_("Show texture in texture tab")); - UI_but_func_set(but, template_texture_show, user->ptr.data, user->prop); + /* Draw button (disabled if we cannot find a Properties Editor to display this in). */ + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but; + but = uiDefIconBut(block, + UI_BTYPE_BUT, + 0, + ICON_PROPERTIES, + 0, + 0, + UI_UNIT_X, + UI_UNIT_Y, + NULL, + 0.0, + 0.0, + 0.0, + 0.0, + TIP_("Show texture in texture tab")); + UI_but_func_set(but, + template_texture_show, + user_found ? user->ptr.data : NULL, + user_found ? user->prop : NULL); + if (ct == NULL) { + UI_but_disable(but, TIP_("No (unpinned) Properties Editor found to display texture in")); + } + else if (!user_found) { + UI_but_disable(but, TIP_("No texture user found")); } } -- cgit v1.2.3 From bbb1936411a5f98f5a49ed0d63bcf1a547cbdb59 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 2 Mar 2021 12:08:16 +0100 Subject: VSE: Refactor VSE strip loading code Isolate RNA and operator logic from functions that create strips. - Operator specific code was removed from `SeqLoadInfo` structure and `SEQ_add_*` functions. - Strip loading code was removed from RNA and operator functions. - `SEQ_add_*` API was unified to work on `SeqLoadData` struct. Only exception is image strip, which require files to be loaded separately to strip creation itself. This is not ideal, but I think it's acceptable. - Some functions and variables were refactored so the code reads better. There are minor functional changes (coincidental bugfixes): - Operator errors are reported per-strip. Previously they were not reported at all? - `new_sound()` RNA API function now create sound with length of 1 if source file does not exist. Previously it created strip with length of 0. - Replace selection operator property wasn't working correctly. Fixed in this patch. Reviewed By: sergey Differential Revision: https://developer.blender.org/D9760 --- .../editors/space_sequencer/sequencer_add.c | 581 +++++++++++---------- 1 file changed, 298 insertions(+), 283 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index a9033b98708..baa2e9cc964 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -36,6 +36,7 @@ #include "DNA_mask_types.h" #include "DNA_scene_types.h" +#include "DNA_sound_types.h" #include "BKE_context.h" #include "BKE_lib_id.h" @@ -44,6 +45,8 @@ #include "BKE_movieclip.h" #include "BKE_report.h" +#include "IMB_imbuf.h" + #include "WM_api.h" #include "WM_types.h" @@ -89,8 +92,6 @@ typedef struct SequencerAddData { #define SEQPROP_NOCHAN (1 << 3) #define SEQPROP_FIT_METHOD (1 << 4) -#define SELECT 1 - static const EnumPropertyItem scale_fit_methods[] = { {SEQ_SCALE_TO_FIT, "FIT", 0, "Scale to Fit", "Scale image to fit within the canvas"}, {SEQ_SCALE_TO_FILL, "FILL", 0, "Scale to Fill", "Scale image to completely fill the canvas"}, @@ -216,7 +217,7 @@ static void sequencer_generic_invoke_xy__internal(bContext *C, wmOperator *op, i } } -static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperator *op) +static void load_data_init_from_operator(SeqLoadData *load_data, bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); @@ -224,69 +225,56 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato const bool relative = (prop = RNA_struct_find_property(op->ptr, "relative_path")) && RNA_property_boolean_get(op->ptr, prop); int is_file = -1; - memset(seq_load, 0, sizeof(SeqLoadInfo)); + memset(load_data, 0, sizeof(SeqLoadData)); - seq_load->start_frame = RNA_int_get(op->ptr, "frame_start"); - seq_load->end_frame = seq_load->start_frame; - seq_load->channel = RNA_int_get(op->ptr, "channel"); - seq_load->len = 1; - seq_load->fit_method = RNA_enum_get(op->ptr, "fit_method"); - SEQ_tool_settings_fit_method_set(CTX_data_scene(C), seq_load->fit_method); + load_data->start_frame = RNA_int_get(op->ptr, "frame_start"); + load_data->channel = RNA_int_get(op->ptr, "channel"); + load_data->image.end_frame = load_data->start_frame; + load_data->image.len = 1; + load_data->fit_method = RNA_enum_get(op->ptr, "fit_method"); + SEQ_tool_settings_fit_method_set(CTX_data_scene(C), load_data->fit_method); if ((prop = RNA_struct_find_property(op->ptr, "filepath"))) { /* Full path, file is set by the caller. */ - RNA_property_string_get(op->ptr, prop, seq_load->path); + RNA_property_string_get(op->ptr, prop, load_data->path); is_file = 1; } else if ((prop = RNA_struct_find_property(op->ptr, "directory"))) { /* Full path, file is set by the caller. */ - RNA_property_string_get(op->ptr, prop, seq_load->path); + RNA_property_string_get(op->ptr, prop, load_data->path); is_file = 0; } if ((is_file != -1) && relative) { - BLI_path_rel(seq_load->path, BKE_main_blendfile_path(bmain)); + BLI_path_rel(load_data->path, BKE_main_blendfile_path(bmain)); } if ((prop = RNA_struct_find_property(op->ptr, "frame_end"))) { - seq_load->end_frame = RNA_property_int_get(op->ptr, prop); - } - - if ((prop = RNA_struct_find_property(op->ptr, "replace_sel")) && - RNA_property_boolean_get(op->ptr, prop)) { - seq_load->flag |= SEQ_LOAD_REPLACE_SEL; + load_data->image.end_frame = RNA_property_int_get(op->ptr, prop); } if ((prop = RNA_struct_find_property(op->ptr, "cache")) && RNA_property_boolean_get(op->ptr, prop)) { - seq_load->flag |= SEQ_LOAD_SOUND_CACHE; + load_data->flags |= SEQ_LOAD_SOUND_CACHE; } if ((prop = RNA_struct_find_property(op->ptr, "mono")) && RNA_property_boolean_get(op->ptr, prop)) { - seq_load->flag |= SEQ_LOAD_SOUND_MONO; - } - - if ((prop = RNA_struct_find_property(op->ptr, "sound")) && - RNA_property_boolean_get(op->ptr, prop)) { - seq_load->flag |= SEQ_LOAD_MOVIE_SOUND; + load_data->flags |= SEQ_LOAD_SOUND_MONO; } if ((prop = RNA_struct_find_property(op->ptr, "use_framerate")) && RNA_property_boolean_get(op->ptr, prop)) { - seq_load->flag |= SEQ_LOAD_SYNC_FPS; + load_data->flags |= SEQ_LOAD_MOVIE_SYNC_FPS; } - /* Create consecutive array of strips. */ - seq_load->flag |= SEQ_LOAD_FRAME_ADVANCE; - if (is_file == 1) { - BLI_strncpy(seq_load->name, BLI_path_basename(seq_load->path), sizeof(seq_load->name)); + BLI_strncpy(load_data->name, BLI_path_basename(load_data->path), sizeof(load_data->name)); } else if ((prop = RNA_struct_find_property(op->ptr, "files"))) { RNA_PROP_BEGIN (op->ptr, itemptr, prop) { char *name = RNA_string_get_alloc(&itemptr, "name", NULL, 0); - BLI_strncpy(seq_load->name, name, sizeof(seq_load->name)); + BLI_strncpy(load_data->name, name, sizeof(load_data->name)); MEM_freeN(name); break; } @@ -299,36 +287,31 @@ static void seq_load_operator_info(SeqLoadInfo *seq_load, bContext *C, wmOperato SequencerAddData *sad = op->customdata; ImageFormatData *imf = &sad->im_format; - seq_load->views_format = imf->views_format; - seq_load->flag |= SEQ_USE_VIEWS; - seq_load->stereo3d_format = &imf->stereo3d_format; + load_data->use_multiview = true; + load_data->views_format = imf->views_format; + load_data->stereo3d_format = &imf->stereo3d_format; } } } -/** - * Apply generic operator options. - */ -static void sequencer_add_apply_overlap(bContext *C, wmOperator *op, Sequence *seq) +static void seq_load_apply_generic_options(bContext *C, wmOperator *op, Sequence *seq) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, false); - if (RNA_boolean_get(op->ptr, "overlap") == false) { - if (SEQ_transform_test_overlap(ed->seqbasep, seq)) { - SEQ_transform_seqbase_shuffle(ed->seqbasep, seq, scene); - } + if (seq == NULL) { + return; } -} - -static void sequencer_add_apply_replace_sel(bContext *C, wmOperator *op, Sequence *seq) -{ - Scene *scene = CTX_data_scene(C); if (RNA_boolean_get(op->ptr, "replace_sel")) { - ED_sequencer_deselect_all(scene); - SEQ_select_active_set(scene, seq); seq->flag |= SELECT; + SEQ_select_active_set(scene, seq); + } + + if (RNA_boolean_get(op->ptr, "overlap") == false) { + if (SEQ_transform_test_overlap(ed->seqbasep, seq)) { + SEQ_transform_seqbase_shuffle(ed->seqbasep, seq, scene); + } } } @@ -356,34 +339,24 @@ static int sequencer_add_scene_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); - Scene *sce_seq; - Sequence *seq; - - int start_frame, channel; - start_frame = RNA_int_get(op->ptr, "frame_start"); - channel = RNA_int_get(op->ptr, "channel"); - sce_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene")); + const Editing *ed = SEQ_editing_get(scene, true); + Scene *sce_seq = BLI_findlink(&bmain->scenes, RNA_enum_get(op->ptr, "scene")); if (sce_seq == NULL) { BKE_report(op->reports, RPT_ERROR, "Scene not found"); return OPERATOR_CANCELLED; } - seq = SEQ_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_SCENE); - seq->blend_mode = SEQ_TYPE_CROSS; - seq->scene = sce_seq; - seq->len = sce_seq->r.efra - sce_seq->r.sfra + 1; - - BLI_strncpy(seq->name + 2, sce_seq->id.name + 2, sizeof(seq->name) - 2); - SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seq); + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } - SEQ_time_update_sequence_bounds(scene, seq); - SEQ_sort(scene); + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + load_data.scene = sce_seq; - sequencer_add_apply_replace_sel(C, op, seq); - sequencer_add_apply_overlap(C, op, seq); - SEQ_relations_invalidate_cache_composite(scene, seq); + Sequence *seq = SEQ_add_scene_strip(scene, ed->seqbasep, &load_data); + seq_load_apply_generic_options(C, op, seq); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); DEG_relations_tag_update(bmain); @@ -430,36 +403,24 @@ static int sequencer_add_movieclip_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); - MovieClip *clip; - Sequence *seq; - - int start_frame, channel; - start_frame = RNA_int_get(op->ptr, "frame_start"); - channel = RNA_int_get(op->ptr, "channel"); - clip = BLI_findlink(&bmain->movieclips, RNA_enum_get(op->ptr, "clip")); + const Editing *ed = SEQ_editing_get(scene, true); + MovieClip *clip = BLI_findlink(&bmain->movieclips, RNA_enum_get(op->ptr, "clip")); if (clip == NULL) { BKE_report(op->reports, RPT_ERROR, "Movie clip not found"); return OPERATOR_CANCELLED; } - seq = SEQ_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_MOVIECLIP); - seq->blend_mode = SEQ_TYPE_CROSS; - seq->clip = clip; - seq->len = BKE_movieclip_get_duration(clip); - - id_us_ensure_real(&seq->clip->id); - - BLI_strncpy(seq->name + 2, clip->id.name + 2, sizeof(seq->name) - 2); - SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seq); + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } - SEQ_time_update_sequence_bounds(scene, seq); - SEQ_sort(scene); + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + load_data.clip = clip; - sequencer_add_apply_replace_sel(C, op, seq); - sequencer_add_apply_overlap(C, op, seq); - SEQ_relations_invalidate_cache_composite(scene, seq); + Sequence *seq = SEQ_add_movieclip_strip(scene, ed->seqbasep, &load_data); + seq_load_apply_generic_options(C, op, seq); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -506,36 +467,24 @@ static int sequencer_add_mask_strip_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); - Mask *mask; - Sequence *seq; - - int start_frame, channel; - start_frame = RNA_int_get(op->ptr, "frame_start"); - channel = RNA_int_get(op->ptr, "channel"); - mask = BLI_findlink(&bmain->masks, RNA_enum_get(op->ptr, "mask")); + const Editing *ed = SEQ_editing_get(scene, true); + Mask *mask = BLI_findlink(&bmain->masks, RNA_enum_get(op->ptr, "mask")); if (mask == NULL) { BKE_report(op->reports, RPT_ERROR, "Mask not found"); return OPERATOR_CANCELLED; } - seq = SEQ_sequence_alloc(ed->seqbasep, start_frame, channel, SEQ_TYPE_MASK); - seq->blend_mode = SEQ_TYPE_CROSS; - seq->mask = mask; - seq->len = BKE_mask_get_duration(mask); - - id_us_ensure_real(&seq->mask->id); - - BLI_strncpy(seq->name + 2, mask->id.name + 2, sizeof(seq->name) - 2); - SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seq); + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } - SEQ_time_update_sequence_bounds(scene, seq); - SEQ_sort(scene); + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + load_data.mask = mask; - sequencer_add_apply_replace_sel(C, op, seq); - sequencer_add_apply_overlap(C, op, seq); - SEQ_relations_invalidate_cache_composite(scene, seq); + Sequence *seq = SEQ_add_mask_strip(scene, ed->seqbasep, &load_data); + seq_load_apply_generic_options(C, op, seq); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); @@ -577,100 +526,120 @@ void SEQUENCER_OT_mask_strip_add(struct wmOperatorType *ot) ot->prop = prop; } -static int sequencer_add_generic_strip_exec(bContext *C, wmOperator *op, SeqLoadFn seq_load_fn) +static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op) { - Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); - SeqLoadInfo seq_load; - int tot_files; - - seq_load_operator_info(&seq_load, C, op); + op->customdata = MEM_callocN(sizeof(SequencerAddData), __func__); +} - if (seq_load.flag & SEQ_LOAD_REPLACE_SEL) { - ED_sequencer_deselect_all(scene); +static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op) +{ + if (op->customdata) { + MEM_freeN(op->customdata); } + op->customdata = NULL; +} - tot_files = RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); - - if (tot_files > 1) { - char dir_only[FILE_MAX]; - char file_only[FILE_MAX]; - - RNA_BEGIN (op->ptr, itemptr, "files") { - Sequence *seq; +static bool sequencer_add_draw_check_fn(PointerRNA *UNUSED(ptr), + PropertyRNA *prop, + void *UNUSED(user_data)) +{ + const char *prop_id = RNA_property_identifier(prop); - RNA_string_get(op->ptr, "directory", dir_only); - RNA_string_get(&itemptr, "name", file_only); - BLI_join_dirfile(seq_load.path, sizeof(seq_load.path), dir_only, file_only); + return !(STR_ELEM(prop_id, "filepath", "directory", "filename")); +} - /* Set seq_load.name, otherwise all video/audio files get the same name. */ - BLI_strncpy(seq_load.name, file_only, sizeof(seq_load.name)); +static void sequencer_add_movie_multiple_strips(bContext *C, + wmOperator *op, + SeqLoadData *load_data) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + const Editing *ed = SEQ_editing_get(scene, true); - seq = seq_load_fn(C, ed->seqbasep, &seq_load); - if (seq) { - if (seq_load.seq_sound) { - sequencer_add_apply_overlap(C, op, seq_load.seq_sound); - } - sequencer_add_apply_overlap(C, op, seq); - } + RNA_BEGIN (op->ptr, itemptr, "files") { + char dir_only[FILE_MAX]; + char file_only[FILE_MAX]; + RNA_string_get(op->ptr, "directory", dir_only); + RNA_string_get(&itemptr, "name", file_only); + BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only); + BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); + Sequence *seq_movie = NULL; + Sequence *seq_sound = NULL; + load_data->channel++; + seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data); + load_data->channel--; + if (seq_movie == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); } - RNA_END; - } - else { /* Single file./ */ - Sequence *seq; - seq = seq_load_fn(C, ed->seqbasep, &seq_load); - - if (seq) { - if (seq_load.seq_sound) { - sequencer_add_apply_overlap(C, op, seq_load.seq_sound); + else { + if (RNA_boolean_get(op->ptr, "sound")) { + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); } - sequencer_add_apply_overlap(C, op, seq); + load_data->start_frame += seq_movie->enddisp - seq_movie->startdisp; + seq_load_apply_generic_options(C, op, seq_sound); + seq_load_apply_generic_options(C, op, seq_movie); } } + RNA_END; +} - if (op->customdata) { - MEM_freeN(op->customdata); - } +static bool sequencer_add_movie_single_strip(bContext *C, wmOperator *op, SeqLoadData *load_data) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + const Editing *ed = SEQ_editing_get(scene, true); - if (seq_load.tot_success == 0) { - BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", seq_load.path); + Sequence *seq_movie = NULL; + Sequence *seq_sound = NULL; + load_data->channel++; + seq_movie = SEQ_add_movie_strip(bmain, scene, ed->seqbasep, load_data); + load_data->channel--; - return OPERATOR_CANCELLED; + if (seq_movie == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); + return false; } + if (RNA_boolean_get(op->ptr, "sound")) { + seq_sound = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + } + seq_load_apply_generic_options(C, op, seq_sound); + seq_load_apply_generic_options(C, op, seq_movie); - SEQ_sort(scene); - - DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); - - return OPERATOR_FINISHED; + return true; } -static void sequencer_add_init(bContext *UNUSED(C), wmOperator *op) +static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) { - op->customdata = MEM_callocN(sizeof(SequencerAddData), __func__); -} + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + SeqLoadData load_data; + + load_data_init_from_operator(&load_data, C, op); + + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } + + const int tot_files = RNA_property_collection_length(op->ptr, + RNA_struct_find_property(op->ptr, "files")); + if (tot_files > 1) { + sequencer_add_movie_multiple_strips(C, op, &load_data); + } + else { + if (!sequencer_add_movie_single_strip(C, op, &load_data)) { + return OPERATOR_CANCELLED; + } + } -static void sequencer_add_cancel(bContext *UNUSED(C), wmOperator *op) -{ if (op->customdata) { MEM_freeN(op->customdata); } - op->customdata = NULL; -} -static bool sequencer_add_draw_check_fn(PointerRNA *UNUSED(ptr), - PropertyRNA *prop, - void *UNUSED(user_data)) -{ - const char *prop_id = RNA_property_identifier(prop); - - return !(STR_ELEM(prop_id, "filepath", "directory", "filename")); -} + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); -static int sequencer_add_movie_strip_exec(bContext *C, wmOperator *op) -{ - return sequencer_add_generic_strip_exec(C, op, SEQ_add_movie_strip); + return OPERATOR_FINISHED; } static int sequencer_add_movie_strip_invoke(bContext *C, @@ -681,7 +650,8 @@ static int sequencer_add_movie_strip_invoke(bContext *C, Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, false); - /* Only enable "use_framerate" if there aren't any existing strips, unless overridden by user. */ + /* Only enable "use_framerate" if there aren't any existing strips, unless overridden by user. + */ if (ed && ed->seqbasep && ed->seqbasep->first) { RNA_boolean_set(op->ptr, "use_framerate", false); } @@ -761,9 +731,80 @@ void SEQUENCER_OT_movie_strip_add(struct wmOperatorType *ot) "Use framerate from the movie to keep sound and video in sync"); } +static void sequencer_add_sound_multiple_strips(bContext *C, + wmOperator *op, + SeqLoadData *load_data) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene, true); + + RNA_BEGIN (op->ptr, itemptr, "files") { + char dir_only[FILE_MAX]; + char file_only[FILE_MAX]; + RNA_string_get(op->ptr, "directory", dir_only); + RNA_string_get(&itemptr, "name", file_only); + BLI_join_dirfile(load_data->path, sizeof(load_data->path), dir_only, file_only); + BLI_strncpy(load_data->name, file_only, sizeof(load_data->name)); + Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + if (seq == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); + } + else { + seq_load_apply_generic_options(C, op, seq); + load_data->start_frame += seq->enddisp - seq->startdisp; + } + } + RNA_END; +} + +static bool sequencer_add_sound_single_strip(bContext *C, wmOperator *op, SeqLoadData *load_data) +{ + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene, true); + + Sequence *seq = SEQ_add_sound_strip(bmain, scene, ed->seqbasep, load_data); + if (seq == NULL) { + BKE_reportf(op->reports, RPT_ERROR, "File '%s' could not be loaded", load_data->path); + return false; + } + seq_load_apply_generic_options(C, op, seq); + + return true; +} + static int sequencer_add_sound_strip_exec(bContext *C, wmOperator *op) { - return sequencer_add_generic_strip_exec(C, op, SEQ_add_sound_strip); + Main *bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } + + const int tot_files = RNA_property_collection_length(op->ptr, + RNA_struct_find_property(op->ptr, "files")); + if (tot_files > 1) { + sequencer_add_sound_multiple_strips(C, op, &load_data); + } + else { + if (!sequencer_add_sound_single_strip(C, op, &load_data)) { + return OPERATOR_CANCELLED; + } + } + + if (op->customdata) { + MEM_freeN(op->customdata); + } + + DEG_relations_tag_update(bmain); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); + WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + + return OPERATOR_FINISHED; } static int sequencer_add_sound_strip_invoke(bContext *C, @@ -873,78 +914,86 @@ void sequencer_image_seq_reserve_frames( } } -static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) +static int sequencer_add_image_strip_calculate_length(wmOperator *op, + const int start_frame, + int *minframe, + int *numdigits) { - int minframe, numdigits; - Scene *scene = CTX_data_scene(C); - Editing *ed = SEQ_editing_get(scene, true); - SeqLoadInfo seq_load; - Sequence *seq; - Strip *strip; - StripElem *se; const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders"); - seq_load_operator_info(&seq_load, C, op); - - /* Images are unique in how they handle this - 1 per strip elem. */ if (use_placeholders) { - seq_load.len = sequencer_image_seq_get_minmax_frame( - op, seq_load.start_frame, &minframe, &numdigits); + return sequencer_image_seq_get_minmax_frame(op, start_frame, minframe, numdigits); } else { - seq_load.len = RNA_property_collection_length(op->ptr, - RNA_struct_find_property(op->ptr, "files")); - } - - if (seq_load.len == 0) { - return OPERATOR_CANCELLED; + return RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); } +} - if (seq_load.flag & SEQ_LOAD_REPLACE_SEL) { - ED_sequencer_deselect_all(scene); - } +static void sequencer_add_image_strip_load_files(bContext *C, + wmOperator *op, + Sequence *seq, + SeqLoadData *load_data, + const int minframe, + const int numdigits) +{ + const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders"); - /* Main adding function. */ - seq = SEQ_add_image_strip(C, ed->seqbasep, &seq_load); - strip = seq->strip; - se = strip->stripdata; - seq->blend_mode = SEQ_TYPE_ALPHAOVER; + SEQ_add_image_set_directory(seq, load_data->path); if (use_placeholders) { - sequencer_image_seq_reserve_frames(op, se, seq_load.len, minframe, numdigits); + sequencer_image_seq_reserve_frames( + op, seq->strip->stripdata, load_data->image.len, minframe, numdigits); } else { + size_t strip_frame = 0; RNA_BEGIN (op->ptr, itemptr, "files") { char *filename = RNA_string_get_alloc(&itemptr, "name", NULL, 0); - BLI_strncpy(se->name, filename, sizeof(se->name)); + SEQ_add_image_load_file(seq, strip_frame, filename); MEM_freeN(filename); - se++; + strip_frame++; } RNA_END; } +} - if (seq_load.len == 1) { - if (seq_load.start_frame < seq_load.end_frame) { - seq->endstill = seq_load.end_frame - seq_load.start_frame; - } +static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Editing *ed = SEQ_editing_get(scene, true); + + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + + int minframe, numdigits; + load_data.image.len = sequencer_add_image_strip_calculate_length( + op, load_data.start_frame, &minframe, &numdigits); + if (load_data.image.len == 0) { + return OPERATOR_CANCELLED; } - SEQ_render_init_colorspace(seq); - SEQ_time_update_sequence_bounds(scene, seq); - SEQ_sort(scene); + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); + } - /* Last active name. */ - BLI_strncpy(ed->act_imagedir, strip->dir, sizeof(ed->act_imagedir)); - sequencer_add_apply_overlap(C, op, seq); + Sequence *seq = SEQ_add_image_strip(CTX_data_main(C), scene, ed->seqbasep, &load_data); + sequencer_add_image_strip_load_files(C, op, seq, &load_data, minframe, numdigits); + SEQ_add_image_init_alpha_mode(seq); - if (op->customdata) { - MEM_freeN(op->customdata); + /* Adjust length. */ + if (load_data.image.len == 1) { + SEQ_transform_set_right_handle_frame(seq, load_data.image.end_frame); + SEQ_time_update_sequence(scene, seq); } - SEQ_relations_invalidate_cache_composite(scene, seq); + seq_load_apply_generic_options(C, op, seq); + DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); + if (op->customdata) { + MEM_freeN(op->customdata); + } + return OPERATOR_FINISHED; } @@ -1016,80 +1065,46 @@ static int sequencer_add_effect_strip_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, true); - Sequence *seq; - struct SeqEffectHandle sh; - Sequence *seq1, *seq2, *seq3; const char *error_msg; - int start_frame, end_frame, channel, type; - start_frame = RNA_int_get(op->ptr, "frame_start"); - end_frame = RNA_int_get(op->ptr, "frame_end"); - channel = RNA_int_get(op->ptr, "channel"); - type = RNA_enum_get(op->ptr, "type"); + SeqLoadData load_data; + load_data_init_from_operator(&load_data, C, op); + load_data.effect.type = RNA_enum_get(op->ptr, "type"); - if (!seq_effect_find_selected(scene, NULL, type, &seq1, &seq2, &seq3, &error_msg)) { + Sequence *seq1, *seq2, *seq3; + if (!seq_effect_find_selected( + scene, NULL, load_data.effect.type, &seq1, &seq2, &seq3, &error_msg)) { BKE_report(op->reports, RPT_ERROR, error_msg); return OPERATOR_CANCELLED; } - /* Check its start and end frames are valid. */ - if (seq1 == NULL && end_frame <= start_frame) { - end_frame = start_frame + 1; - RNA_int_set(op->ptr, "frame_end", end_frame); - } - - seq = SEQ_sequence_alloc(ed->seqbasep, start_frame, channel, type); - BLI_strncpy(seq->name + 2, SEQ_sequence_give_name(seq), sizeof(seq->name) - 2); - SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seq); - - sh = SEQ_effect_handle_get(seq); - sh.init(seq); - seq->seq1 = seq1; - seq->seq2 = seq2; - seq->seq3 = seq3; - - if (!seq1) { - seq->len = 1; /* Effect is generator, set non zero length. */ - SEQ_transform_set_right_handle_frame(seq, end_frame); + if (RNA_boolean_get(op->ptr, "replace_sel")) { + ED_sequencer_deselect_all(scene); } - seq->flag |= SEQ_USE_EFFECT_DEFAULT_FADE; - SEQ_time_update_sequence(scene, seq); - - if (seq->type == SEQ_TYPE_COLOR) { - SolidColorVars *colvars = (SolidColorVars *)seq->effectdata; - RNA_float_get_array(op->ptr, "color", colvars->col); - seq->blend_mode = SEQ_TYPE_CROSS; - } - else if (seq->type == SEQ_TYPE_ADJUSTMENT) { - seq->blend_mode = SEQ_TYPE_CROSS; - } - else if (seq->type == SEQ_TYPE_TEXT) { - seq->blend_mode = SEQ_TYPE_ALPHAOVER; - } - else if (SEQ_effect_get_num_inputs(seq->type) == 1) { - seq->blend_mode = seq1->blend_mode; - } + load_data.effect.seq1 = seq1; + load_data.effect.seq2 = seq2; + load_data.effect.seq3 = seq3; /* Set channel. If unset, use lowest free one above strips. */ if (!RNA_struct_property_is_set(op->ptr, "channel")) { - if (seq->seq1) { - int chan = max_iii(seq->seq1 ? seq->seq1->machine : 0, - seq->seq2 ? seq->seq2->machine : 0, - seq->seq3 ? seq->seq3->machine : 0); + if (seq1 != NULL) { + int chan = max_iii( + seq1 ? seq1->machine : 0, seq2 ? seq2->machine : 0, seq3 ? seq3->machine : 0); if (chan < MAXSEQ) { - seq->machine = chan; + load_data.channel = chan; } } } - sequencer_add_apply_replace_sel(C, op, seq); - sequencer_add_apply_overlap(C, op, seq); + Sequence *seq = SEQ_add_effect_strip(scene, ed->seqbasep, &load_data); + seq_load_apply_generic_options(C, op, seq); - SEQ_relations_update_changed_seq_and_deps(scene, seq, 1, 1); /* Runs SEQ_time_update_sequence. */ - SEQ_sort(scene); + if (seq->type == SEQ_TYPE_COLOR) { + SolidColorVars *colvars = (SolidColorVars *)seq->effectdata; + RNA_float_get_array(op->ptr, "color", colvars->col); + } - SEQ_relations_invalidate_cache_composite(scene, seq); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); -- cgit v1.2.3 From e1f3996d740cf8c0299ce6ea76362648f421fa41 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 2 Mar 2021 12:34:03 +0100 Subject: VSE: Refactor meta operators Move low level logic to module code and versioning logic to versioning code. Metas strip position was handled in diffrent way compared to other strips. This was introduced in c8b0d25794be as bugfix for T28158. I disagree with such design. Meta strips should be handled in same way as any other strips. I have tested this change and haven't found any problems. No problems after checking T28158 as well. There should be no functional changes on user level. Reviewed By: campbellbarton Differential Revision: https://developer.blender.org/D9972 --- .../editors/space_sequencer/sequencer_edit.c | 209 +++++---------------- .../editors/space_sequencer/sequencer_select.c | 25 +++ .../transform/transform_convert_sequencer.c | 52 +---- 3 files changed, 73 insertions(+), 213 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 608e220c582..78d263dffad 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -1870,93 +1870,28 @@ void SEQUENCER_OT_images_separate(wmOperatorType *ot) /** \name Toggle Meta Strip Operator * \{ */ -void recurs_sel_seq(Sequence *seqm) -{ - Sequence *seq; - - seq = seqm->seqbase.first; - while (seq) { - - if (seqm->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) { - seq->flag &= ~SEQ_ALLSEL; - } - else if (seqm->flag & SELECT) { - seq->flag |= SELECT; - } - else { - seq->flag &= ~SEQ_ALLSEL; - } - - if (seq->seqbase.first) { - recurs_sel_seq(seq); - } - - seq = seq->next; - } -} - static int sequencer_meta_toggle_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, false); - Sequence *last_seq = SEQ_select_active_get(scene); - MetaStack *ms; + Sequence *active_seq = SEQ_select_active_get(scene); - if (last_seq && last_seq->type == SEQ_TYPE_META && last_seq->flag & SELECT) { + if (active_seq && active_seq->type == SEQ_TYPE_META && active_seq->flag & SELECT) { /* Enter metastrip. */ - ms = MEM_mallocN(sizeof(MetaStack), "metastack"); - BLI_addtail(&ed->metastack, ms); - ms->parseq = last_seq; - ms->oldbasep = ed->seqbasep; - copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp); - - ed->seqbasep = &last_seq->seqbase; - + SEQ_meta_stack_alloc(ed, active_seq); + SEQ_seqbase_active_set(ed, &active_seq->seqbase); SEQ_select_active_set(scene, NULL); } else { /* Exit metastrip if possible. */ - - Sequence *seq; - if (BLI_listbase_is_empty(&ed->metastack)) { return OPERATOR_CANCELLED; } - ms = ed->metastack.last; - BLI_remlink(&ed->metastack, ms); - - ed->seqbasep = ms->oldbasep; - - /* For old files, update from meta. */ - if (ms->disp_range[0] == ms->disp_range[1]) { - copy_v2_v2_int(ms->disp_range, &ms->parseq->startdisp); - } - - /* Recalc all: the meta can have effects connected to it. */ - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - SEQ_time_update_sequence(scene, seq); - } - - /* 2.73+, keeping endpoints is important! - * Moving them around means you can't usefully use metas in a complex edit. */ -#if 1 - SEQ_transform_set_left_handle_frame(ms->parseq, ms->disp_range[0]); - SEQ_transform_set_right_handle_frame(ms->parseq, ms->disp_range[1]); - SEQ_transform_fix_single_image_seq_offsets(ms->parseq); - SEQ_time_update_sequence(scene, ms->parseq); -#else - if (SEQ_transform_test_overlap(ed->seqbasep, ms->parseq)) { - SEQ_transform_seqbase_shuffle(ed->seqbasep, ms->parseq, scene); - } -#endif - + MetaStack *ms = SEQ_meta_stack_active_get(ed); + SEQ_seqbase_active_set(ed, ms->oldbasep); SEQ_select_active_set(scene, ms->parseq); - - ms->parseq->flag |= SELECT; - recurs_sel_seq(ms->parseq); - - MEM_freeN(ms); + SEQ_meta_stack_free(ed, ms); } DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); @@ -1990,48 +1925,44 @@ static int sequencer_meta_make_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, false); + Sequence *active_seq = SEQ_select_active_get(scene); + ListBase *active_seqbase = SEQ_active_seqbase_get(ed); - Sequence *seq, *seqm, *next, *last_seq = SEQ_select_active_get(scene); - int channel_max = 1; - - if (SEQ_transform_seqbase_isolated_sel_check(ed->seqbasep) == false) { + if (SEQ_transform_seqbase_isolated_sel_check(active_seqbase) == false) { BKE_report(op->reports, RPT_ERROR, "Please select all related strips"); return OPERATOR_CANCELLED; } SEQ_prefetch_stop(scene); - /* Remove all selected from main list, and put in meta. */ - - seqm = SEQ_sequence_alloc(ed->seqbasep, 1, 1, SEQ_TYPE_META); /* Channel number set later. */ - strcpy(seqm->name + 2, "MetaStrip"); - seqm->flag = SELECT; + int channel_max = 1, meta_start_frame = MAXFRAME, meta_end_frame = MINFRAME; + Sequence *seqm = SEQ_sequence_alloc(active_seqbase, 1, 1, SEQ_TYPE_META); - seq = ed->seqbasep->first; - while (seq) { - next = seq->next; - if (seq != seqm && (seq->flag & SELECT)) { - SEQ_relations_invalidate_cache_composite(scene, seq); - channel_max = max_ii(seq->machine, channel_max); - /* Sequence is moved within the same edit, no need to re-generate the UUID. */ - BLI_remlink(ed->seqbasep, seq); + /* Remove all selected from main list, and put in meta. + * Sequence is moved within the same edit, no need to re-generate the UUID. */ + LISTBASE_FOREACH_MUTABLE (Sequence *, seq, active_seqbase) { + if (seq != seqm && seq->flag & SELECT) { + BLI_remlink(active_seqbase, seq); BLI_addtail(&seqm->seqbase, seq); + SEQ_relations_invalidate_cache_preprocessed(scene, seq); + channel_max = max_ii(seq->machine, channel_max); + meta_start_frame = min_ii(seq->startdisp, meta_start_frame); + meta_end_frame = max_ii(seq->enddisp, meta_end_frame); } - seq = next; } - seqm->machine = last_seq ? last_seq->machine : channel_max; - SEQ_time_update_sequence(scene, seqm); + seqm->machine = active_seq ? active_seq->machine : channel_max; + strcpy(seqm->name + 2, "MetaStrip"); + SEQ_sequence_base_unique_name_recursive(&ed->seqbase, seqm); + seqm->start = meta_start_frame; + seqm->len = meta_end_frame - meta_start_frame; + SEQ_time_update_sequence(scene, seqm); SEQ_select_active_set(scene, seqm); - - if (SEQ_transform_test_overlap(ed->seqbasep, seqm)) { - SEQ_transform_seqbase_shuffle(ed->seqbasep, seqm, scene); + if (SEQ_transform_test_overlap(active_seqbase, seqm)) { + SEQ_transform_seqbase_shuffle(active_seqbase, seqm, scene); } DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); - - SEQ_sequence_base_unique_name_recursive(&scene->ed->seqbase, seqm); - SEQ_relations_invalidate_cache_composite(scene, seqm); WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; @@ -2058,95 +1989,43 @@ void SEQUENCER_OT_meta_make(wmOperatorType *ot) /** \name UnMeta Strip Operator * \{ */ -static int seq_depends_on_meta(Sequence *seq, Sequence *seqm) -{ - if (seq == seqm) { - return 1; - } - if (seq->seq1 && seq_depends_on_meta(seq->seq1, seqm)) { - return 1; - } - if (seq->seq2 && seq_depends_on_meta(seq->seq2, seqm)) { - return 1; - } - if (seq->seq3 && seq_depends_on_meta(seq->seq3, seqm)) { - return 1; - } - return 0; -} - -static void recurs_del_seq_flag(Scene *scene, ListBase *lb, short flag, short deleteall) -{ - Sequence *seq, *seqn; - Sequence *last_seq = SEQ_select_active_get(scene); - - seq = lb->first; - while (seq) { - seqn = seq->next; - if ((seq->flag & flag) || deleteall) { - BLI_remlink(lb, seq); - if (seq == last_seq) { - SEQ_select_active_set(scene, NULL); - } - if (seq->type == SEQ_TYPE_META) { - recurs_del_seq_flag(scene, &seq->seqbase, flag, 1); - } - SEQ_sequence_free(scene, seq, true); - } - seq = seqn; - } -} - static int sequencer_meta_separate_exec(bContext *C, wmOperator *UNUSED(op)) { Scene *scene = CTX_data_scene(C); Editing *ed = SEQ_editing_get(scene, false); + Sequence *active_seq = SEQ_select_active_get(scene); - Sequence *seq, *last_seq = SEQ_select_active_get(scene); /* last_seq checks (ed == NULL) */ - - if (last_seq == NULL || last_seq->type != SEQ_TYPE_META) { + if (active_seq == NULL || active_seq->type != SEQ_TYPE_META) { return OPERATOR_CANCELLED; } SEQ_prefetch_stop(scene); - for (seq = last_seq->seqbase.first; seq != NULL; seq = seq->next) { - SEQ_relations_invalidate_cache_composite(scene, seq); + LISTBASE_FOREACH (Sequence *, seq, &active_seq->seqbase) { + SEQ_relations_invalidate_cache_preprocessed(scene, seq); } - /* This moves strips from meta to parent, sating within same edit and no new strips are - * allocated. If the UUID was unique already (as it should) it will stay unique. - * No need to re-generate the UUIDs. */ - BLI_movelisttolist(ed->seqbasep, &last_seq->seqbase); + /* Remove all selected from meta, and put in main list. + * Sequence is moved within the same edit, no need to re-generate the UUID. */ + BLI_movelisttolist(ed->seqbasep, &active_seq->seqbase); + BLI_listbase_clear(&active_seq->seqbase); - BLI_listbase_clear(&last_seq->seqbase); + ListBase *active_seqbase = SEQ_active_seqbase_get(ed); + SEQ_edit_flag_for_removal(scene, active_seqbase, active_seq); + SEQ_edit_remove_flagged_sequences(scene, active_seqbase); - BLI_remlink(ed->seqbasep, last_seq); - SEQ_sequence_free(scene, last_seq, true); - - /* Empty meta strip, delete all effects depending on it. */ - for (seq = ed->seqbasep->first; seq; seq = seq->next) { - if ((seq->type & SEQ_TYPE_EFFECT) && seq_depends_on_meta(seq, last_seq)) { - seq->flag |= SEQ_FLAG_DELETE; - } - } - - recurs_del_seq_flag(scene, ed->seqbasep, SEQ_FLAG_DELETE, 0); - - /* Test for effects and overlap - * don't use SEQ_CURRENT_BEGIN since that would be recursive. */ - for (seq = ed->seqbasep->first; seq; seq = seq->next) { + /* Test for effects and overlap. */ + LISTBASE_FOREACH (Sequence *, seq, active_seqbase) { if (seq->flag & SELECT) { seq->flag &= ~SEQ_OVERLAP; - if (SEQ_transform_test_overlap(ed->seqbasep, seq)) { - SEQ_transform_seqbase_shuffle(ed->seqbasep, seq, scene); + if (SEQ_transform_test_overlap(active_seqbase, seq)) { + SEQ_transform_seqbase_shuffle(active_seqbase, seq, scene); } } } SEQ_sort(scene); DEG_id_tag_update(&scene->id, ID_RECALC_SEQUENCER_STRIPS); - WM_event_add_notifier(C, NC_SCENE | ND_SEQUENCER, scene); return OPERATOR_FINISHED; diff --git a/source/blender/editors/space_sequencer/sequencer_select.c b/source/blender/editors/space_sequencer/sequencer_select.c index ffcb3d35d5a..5f0a18fbd0b 100644 --- a/source/blender/editors/space_sequencer/sequencer_select.c +++ b/source/blender/editors/space_sequencer/sequencer_select.c @@ -360,6 +360,31 @@ static void select_neighbor_from_last(Scene *scene, int lr) } #endif +void recurs_sel_seq(Sequence *seq_meta) +{ + Sequence *seq; + + seq = seq_meta->seqbase.first; + while (seq) { + + if (seq_meta->flag & (SEQ_LEFTSEL + SEQ_RIGHTSEL)) { + seq->flag &= ~SEQ_ALLSEL; + } + else if (seq_meta->flag & SELECT) { + seq->flag |= SELECT; + } + else { + seq->flag &= ~SEQ_ALLSEL; + } + + if (seq->seqbase.first) { + recurs_sel_seq(seq); + } + + seq = seq->next; + } +} + /** \} */ /* -------------------------------------------------------------------- */ diff --git a/source/blender/editors/transform/transform_convert_sequencer.c b/source/blender/editors/transform/transform_convert_sequencer.c index 4ab52b78002..30418471d6d 100644 --- a/source/blender/editors/transform/transform_convert_sequencer.c +++ b/source/blender/editors/transform/transform_convert_sequencer.c @@ -90,35 +90,17 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c Scene *scene = t->scene; int cfra = CFRA; - int left = SEQ_transform_get_left_handle_frame(seq, true); - int right = SEQ_transform_get_right_handle_frame(seq, true); + int left = SEQ_transform_get_left_handle_frame(seq, false); + int right = SEQ_transform_get_right_handle_frame(seq, false); if (seq->depth == 0 && ((seq->flag & SELECT) == 0 || (seq->flag & SEQ_LOCK))) { *r_recursive = false; *r_count = 0; *r_flag = 0; } - else if (seq->type == SEQ_TYPE_META) { - - /* for meta's we only ever need to extend their children, no matter what depth - * just check the meta's are in the bounds */ - if (t->frame_side == 'R' && right <= cfra) { - *r_recursive = false; - } - else if (t->frame_side == 'L' && left >= cfra) { - *r_recursive = false; - } - else { - *r_recursive = true; - } - - *r_count = 1; - *r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL); - } else { - - *r_recursive = false; /* not a meta, so no thinking here */ - *r_count = 1; /* unless its set to 0, extend will never set 2 handles at once */ + *r_recursive = false; + *r_count = 1; /* unless its set to 0, extend will never set 2 handles at once */ *r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL); if (t->frame_side == 'R') { @@ -183,26 +165,9 @@ static void SeqTransInfo(TransInfo *t, Sequence *seq, int *r_recursive, int *r_c else { /* Nested, different rules apply */ -#ifdef SEQ_TX_NESTED_METAS *r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL); *r_count = 1; /* ignore the selection for nested */ *r_recursive = (seq->type == SEQ_TYPE_META); -#else - if (seq->type == SEQ_TYPE_META) { - /* Meta's can only directly be moved between channels since they - * don't have their start and length set directly (children affect that) - * since this Meta is nested we don't need any of its data in fact. - * SEQ_time_update_sequence() will update its settings when run on the top-level meta. */ - *r_flag = 0; - *r_count = 0; - *r_recursive = true; - } - else { - *r_flag = (seq->flag | SELECT) & ~(SEQ_LEFTSEL | SEQ_RIGHTSEL); - *r_count = 1; /* ignore the selection for nested */ - *r_recursive = false; - } -#endif } } } @@ -645,8 +610,6 @@ void createTransSeqData(TransInfo *t) /* commented _only_ because the meta may have animation data which * needs moving too T28158. */ -#define SEQ_TX_NESTED_METAS - BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int sel_flag) { if (seq->depth == 0) { @@ -693,17 +656,10 @@ static void flushTransSeq(TransInfo *t) switch (tdsq->sel_flag) { case SELECT: -#ifdef SEQ_TX_NESTED_METAS if ((seq->depth != 0 || SEQ_transform_sequence_can_be_translated(seq))) { /* for meta's, their children move */ seq->start = new_frame - tdsq->start_offset; } -#else - if (seq->type != SEQ_TYPE_META && (seq->depth != 0 || seq_tx_test(seq))) { - /* for meta's, their children move */ - seq->start = new_frame - tdsq->start_offset; - } -#endif if (seq->depth == 0) { seq->machine = round_fl_to_int(td2d->loc[1]); CLAMP(seq->machine, 1, MAXSEQ); -- cgit v1.2.3 From fece538e3df15bba10b933bebd96e4d7d8e6d541 Mon Sep 17 00:00:00 2001 From: Richard Antalik Date: Tue, 2 Mar 2021 14:21:10 +0100 Subject: Cleanup: remove unused arguments --- source/blender/editors/space_sequencer/sequencer_add.c | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index baa2e9cc964..037ce78b9a2 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -929,12 +929,8 @@ static int sequencer_add_image_strip_calculate_length(wmOperator *op, } } -static void sequencer_add_image_strip_load_files(bContext *C, - wmOperator *op, - Sequence *seq, - SeqLoadData *load_data, - const int minframe, - const int numdigits) +static void sequencer_add_image_strip_load_files( + wmOperator *op, Sequence *seq, SeqLoadData *load_data, const int minframe, const int numdigits) { const bool use_placeholders = RNA_boolean_get(op->ptr, "use_placeholders"); @@ -976,7 +972,7 @@ static int sequencer_add_image_strip_exec(bContext *C, wmOperator *op) } Sequence *seq = SEQ_add_image_strip(CTX_data_main(C), scene, ed->seqbasep, &load_data); - sequencer_add_image_strip_load_files(C, op, seq, &load_data, minframe, numdigits); + sequencer_add_image_strip_load_files(op, seq, &load_data, minframe, numdigits); SEQ_add_image_init_alpha_mode(seq); /* Adjust length. */ -- cgit v1.2.3 From 82e7032477283a9bf2747ee607da7c5ba3eb8986 Mon Sep 17 00:00:00 2001 From: Pablo Dobarro Date: Mon, 1 Mar 2021 23:37:48 +0100 Subject: Sculpt: Expand Operator Expand is a new operator for Sculpt Mode which is intended to be the main tool for masking, Face Set editing, interacting with the filters and pattern creation. The fundamentals of the tool are similar to the previous sculpt.mask_expand operator. It shares the same default shortcuts and functionality, making the previous operator obsolete. The shortcuts to execute the operator are: - Shift + A: Expand mask - Shift + Alt + A: Expand mask by normals - Shift + W: Expand Face Set - Shift + Alt + W: Resize current Face Set The main changes compared to the previous sculpt.mask_expand operator are: - Modal keymap, all operator options can be changed in real time while the operator is running. - Supports creating Mask, Face Sets and Sculpt Vertex Colors. - Much better code, new features can be easily integrated. Limitations: - All Mask operations are supported for Sculpt Vertex colors, but not exposed by default as their support is still experimental. - Dyntopo does not support any Face Set or Sculpt Vertex Colors. functionality (they are not implemented in general for Dyntopo). - Multires does not support any feature related to geodesic distances. - Multires does not support vertex colors. - Multires does not support recursions. - In Multires, Face Sets snaping does not initialize all current enabled Face Sets when toggling snapping. - In Multires, Face Sets are created at base mesh level (works by this by design, like any other tool). - Unlike the previous mask_expand operator, this one does not blur the mask by default after finishing Expand as that does not fit the new design. The mask can still be blurred by using the mask filter manually. Reviewed By: JacquesLucke Differential Revision: https://developer.blender.org/D10455 --- source/blender/editors/sculpt_paint/CMakeLists.txt | 2 + source/blender/editors/sculpt_paint/paint_cursor.c | 10 + source/blender/editors/sculpt_paint/paint_ops.c | 3 + source/blender/editors/sculpt_paint/sculpt.c | 12 +- .../blender/editors/sculpt_paint/sculpt_boundary.c | 8 +- .../blender/editors/sculpt_paint/sculpt_expand.c | 2173 ++++++++++++++++++++ .../blender/editors/sculpt_paint/sculpt_geodesic.c | 360 ++++ .../blender/editors/sculpt_paint/sculpt_intern.h | 171 ++ 8 files changed, 2734 insertions(+), 5 deletions(-) create mode 100644 source/blender/editors/sculpt_paint/sculpt_expand.c create mode 100644 source/blender/editors/sculpt_paint/sculpt_geodesic.c (limited to 'source/blender/editors') diff --git a/source/blender/editors/sculpt_paint/CMakeLists.txt b/source/blender/editors/sculpt_paint/CMakeLists.txt index fff8d27ef5b..fff172c0707 100644 --- a/source/blender/editors/sculpt_paint/CMakeLists.txt +++ b/source/blender/editors/sculpt_paint/CMakeLists.txt @@ -61,10 +61,12 @@ set(SRC sculpt_cloth.c sculpt_detail.c sculpt_dyntopo.c + sculpt_expand.c sculpt_face_set.c sculpt_filter_color.c sculpt_filter_mask.c sculpt_filter_mesh.c + sculpt_geodesic.c sculpt_mask_expand.c sculpt_multiplane_scrape.c sculpt_paint_color.c diff --git a/source/blender/editors/sculpt_paint/paint_cursor.c b/source/blender/editors/sculpt_paint/paint_cursor.c index e9f33d2fbd3..b44f2f66d92 100644 --- a/source/blender/editors/sculpt_paint/paint_cursor.c +++ b/source/blender/editors/sculpt_paint/paint_cursor.c @@ -1626,6 +1626,16 @@ static void paint_cursor_draw_3d_view_brush_cursor_inactive(PaintCursorContext * paint_cursor_pose_brush_origins_draw(pcontext); } + /* Expand operation origin. */ + if (pcontext->ss->expand_cache) { + cursor_draw_point_screen_space( + pcontext->pos, + pcontext->region, + SCULPT_vertex_co_get(pcontext->ss, pcontext->ss->expand_cache->initial_active_vertex), + pcontext->vc.obact->obmat, + 2); + } + if (brush->sculpt_tool == SCULPT_TOOL_BOUNDARY) { paint_cursor_preview_boundary_data_update(pcontext, update_previews); paint_cursor_preview_boundary_data_pivot_draw(pcontext); diff --git a/source/blender/editors/sculpt_paint/paint_ops.c b/source/blender/editors/sculpt_paint/paint_ops.c index f4586fa130d..e1dc8fa30b9 100644 --- a/source/blender/editors/sculpt_paint/paint_ops.c +++ b/source/blender/editors/sculpt_paint/paint_ops.c @@ -1393,4 +1393,7 @@ void ED_keymap_paint(wmKeyConfig *keyconf) /* paint stroke */ keymap = paint_stroke_modal_keymap(keyconf); WM_modalkeymap_assign(keymap, "SCULPT_OT_brush_stroke"); + + /* sculpt expand. */ + sculpt_expand_modal_keymap(keyconf); } diff --git a/source/blender/editors/sculpt_paint/sculpt.c b/source/blender/editors/sculpt_paint/sculpt.c index 3a18d7a10de..631327ddfe8 100644 --- a/source/blender/editors/sculpt_paint/sculpt.c +++ b/source/blender/editors/sculpt_paint/sculpt.c @@ -1103,6 +1103,12 @@ void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index) BLI_gsqueue_push(flood->queue, &index); } +void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index) +{ + BLI_gsqueue_push(flood->queue, &index); + BLI_BITMAP_ENABLE(flood->visited_vertices, index); +} + void SCULPT_floodfill_add_initial_with_symmetry( Sculpt *sd, Object *ob, SculptSession *ss, SculptFloodFill *flood, int index, float radius) { @@ -9006,7 +9012,7 @@ static bool SCULPT_connected_components_floodfill_cb( return true; } -static void sculpt_connected_components_ensure(Object *ob) +void SCULPT_connected_components_ensure(Object *ob) { SculptSession *ss = ob->sculpt; @@ -9081,7 +9087,7 @@ void SCULPT_fake_neighbors_ensure(Sculpt *sd, Object *ob, const float max_dist) return; } - sculpt_connected_components_ensure(ob); + SCULPT_connected_components_ensure(ob); SCULPT_fake_neighbor_init(ss, max_dist); for (int i = 0; i < totvert; i++) { @@ -9790,4 +9796,6 @@ void ED_operatortypes_sculpt(void) WM_operatortype_append(SCULPT_OT_color_filter); WM_operatortype_append(SCULPT_OT_mask_by_color); WM_operatortype_append(SCULPT_OT_dyntopo_detail_size_edit); + + WM_operatortype_append(SCULPT_OT_expand); } diff --git a/source/blender/editors/sculpt_paint/sculpt_boundary.c b/source/blender/editors/sculpt_paint/sculpt_boundary.c index fca19c04b98..f1fb402ae41 100644 --- a/source/blender/editors/sculpt_paint/sculpt_boundary.c +++ b/source/blender/editors/sculpt_paint/sculpt_boundary.c @@ -518,11 +518,13 @@ SculptBoundary *SCULPT_boundary_data_init(Object *object, SculptBoundary *boundary = MEM_callocN(sizeof(SculptBoundary), "Boundary edit data"); - const bool init_boundary_distances = brush->boundary_falloff_type != - BRUSH_BOUNDARY_FALLOFF_CONSTANT; + const bool init_boundary_distances = brush ? brush->boundary_falloff_type != + BRUSH_BOUNDARY_FALLOFF_CONSTANT : + false; + sculpt_boundary_indices_init(ss, boundary, init_boundary_distances, boundary_initial_vertex); - const float boundary_radius = radius * (1.0f + brush->boundary_offset); + const float boundary_radius = brush ? radius * (1.0f + brush->boundary_offset) : radius; sculpt_boundary_edit_data_init(ss, boundary, boundary_initial_vertex, boundary_radius); return boundary; diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c new file mode 100644 index 00000000000..aaa4bed0d2e --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -0,0 +1,2173 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2021 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_linklist_stack.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_subdiv_ccg.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" + +#include "bmesh.h" + +#include +#include + +/* Sculpt Expand. */ +/* Operator for creating selections and patterns in Sculpt Mode. Expand can create masks, face sets + * and fill vertex colors. */ +/* The main functionality of the operator + * - The operator initializes a value per vertex, called "falloff". There are multiple algorithms + * to generate these falloff values which will create different patterns in the result when using + * the operator. These falloff values require algorithms that rely on mesh connectivity, so they + * are only valid on parts of the mesh that are in the same connected component as the given + * initial vertices. If needed, these falloff values are propagated from vertex or grids into the + * base mesh faces. + * - On each modal callback, the operator gets the active vertex and face and gets its falloff + * value from its precalculated falloff. This is now the active falloff value. + * - Using the active falloff value and the settings of the expand operation (which can be modified + * during execution using the modal keymap), the operator loops over all elements in the mesh to + * check if they are enabled of not. + * - Based on each element state after evaluating the settings, the desired mesh data (mask, face + * sets, colors...) is updated. + */ + +/* Used for defining an invalid vertex state (for example, when the cursor is not over the mesh). + */ +#define SCULPT_EXPAND_VERTEX_NONE -1 + +/* Used for defining an uninitialized active component index for an unused symmetry pass. */ + +#define EXPAND_ACTIVE_COMPONENT_NONE -1 +/* Defines how much each time the texture distortion is increased/decreased when using the modal + * keymap. */ +#define SCULPT_EXPAND_TEXTURE_DISTORTION_STEP 0.01f + +/* This threshold offsets the required falloff value to start a new loop. This is needed because in + * some situations, vertices which have the same falloff value as max_falloff will start a new + * loop, which is undesired. */ +#define SCULPT_EXPAND_LOOP_THRESHOLD 0.00001f + +/* Defines how much changes in curvature in the mesh affect the falloff shape when using normal + * falloff. This default was found experimentally and it works well in most cases, but can be + * exposed for tweaking if needed. */ +#define SCULPT_EXPAND_NORMALS_FALLOFF_EDGE_SENSITIVITY 300 + +/* Expand Modal Keymap. */ +enum { + SCULPT_EXPAND_MODAL_CONFIRM = 1, + SCULPT_EXPAND_MODAL_CANCEL, + SCULPT_EXPAND_MODAL_INVERT, + SCULPT_EXPAND_MODAL_PRESERVE_TOGGLE, + SCULPT_EXPAND_MODAL_GRADIENT_TOGGLE, + SCULPT_EXPAND_MODAL_FALLOFF_CYCLE, + SCULPT_EXPAND_MODAL_RECURSION_STEP_GEODESIC, + SCULPT_EXPAND_MODAL_RECURSION_STEP_TOPOLOGY, + SCULPT_EXPAND_MODAL_MOVE_TOGGLE, + SCULPT_EXPAND_MODAL_FALLOFF_GEODESIC, + SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY, + SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY_DIAGONALS, + SCULPT_EXPAND_MODAL_FALLOFF_SPHERICAL, + SCULPT_EXPAND_MODAL_SNAP_TOGGLE, + SCULPT_EXPAND_MODAL_LOOP_COUNT_INCREASE, + SCULPT_EXPAND_MODAL_LOOP_COUNT_DECREASE, + SCULPT_EXPAND_MODAL_BRUSH_GRADIENT_TOGGLE, + SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_INCREASE, + SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_DECREASE, +}; + +/* Functions for getting the state of mesh elements (vertices and base mesh faces). When the main + * functions for getting the state of an element return true it means that data associated to that + * element will be modified by expand. */ + +/* Returns true if the vertex is in a connected component with correctly initialized falloff + * values. */ +static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss, + ExpandCache *expand_cache, + const int v) +{ + for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { + if (ss->vertex_info.connected_component[v] == expand_cache->active_connected_components[i]) { + return true; + } + } + return false; +} + +/* Returns true if the face is in a connected component with correctly initialized falloff values. + */ +static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, + ExpandCache *expand_cache, + const int f) +{ + const MLoop *loop = &ss->mloop[ss->mpoly[f].loopstart]; + return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v); +} + +/* Returns the falloff value of a vertex. This function includes texture distortion, which is not + * precomputed into the initial falloff values. */ +static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, + ExpandCache *expand_cache, + const int v) +{ + if (expand_cache->texture_distortion_strength == 0.0f) { + return expand_cache->vert_falloff[v]; + } + + if (!expand_cache->brush->mtex.tex) { + return expand_cache->vert_falloff[v]; + } + + float rgba[4]; + const float *vertex_co = SCULPT_vertex_co_get(ss, v); + const float avg = BKE_brush_sample_tex_3d( + expand_cache->scene, expand_cache->brush, vertex_co, rgba, 0, ss->tex_pool); + + const float distortion = (avg - 0.5f) * expand_cache->texture_distortion_strength * + expand_cache->max_vert_falloff; + return expand_cache->vert_falloff[v] + distortion; +} + +/* Returns the maximum valid falloff value stored in the falloff array, taking the maximum possible + * texture distortion into account. */ +static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache) +{ + if (expand_cache->texture_distortion_strength == 0.0f) { + return expand_cache->max_vert_falloff; + } + + if (!expand_cache->brush->mtex.tex) { + return expand_cache->max_vert_falloff; + } + + return expand_cache->max_vert_falloff + + (0.5f * expand_cache->texture_distortion_strength * expand_cache->max_vert_falloff); +} + +/* Main function to get the state of a vertex for the current state and settings of a ExpandCache. + * Retruns true when the target data should be modified by expand. + */ +static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v) +{ + if (!SCULPT_vertex_visible_get(ss, v)) { + return false; + } + + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, v)) { + return false; + } + + if (expand_cache->all_enabled) { + return true; + } + + bool enabled = false; + + if (expand_cache->snap) { + /* Face Sets are not being modified when using this function, so it is ok to get this directly + * from the Sculpt API instead of implementing a custom function to get them from + * expand_cache->original_face_sets. */ + const int face_set = SCULPT_vertex_face_set_get(ss, v); + enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set)); + } + else { + const float max_falloff_factor = sculpt_expand_max_vertex_falloff_get(expand_cache); + const float loop_len = (max_falloff_factor / expand_cache->loop_count) + + SCULPT_EXPAND_LOOP_THRESHOLD; + + const float vertex_falloff_factor = sculpt_expand_falloff_value_vertex_get( + ss, expand_cache, v); + const float active_factor = fmod(expand_cache->active_falloff, loop_len); + const float falloff_factor = fmod(vertex_falloff_factor, loop_len); + + enabled = falloff_factor < active_factor; + } + + if (expand_cache->invert) { + enabled = !enabled; + } + return enabled; +} + +/* Main function to get the state of a face for the current state and settings of a ExpandCache. + * Returs true when the traget data should be modified by expand. */ +static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f) +{ + if (ss->face_sets[f] <= 0) { + return false; + } + + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, f)) { + return false; + } + + if (expand_cache->all_enabled) { + return true; + } + + bool enabled = false; + + if (expand_cache->snap_enabled_face_sets) { + const int face_set = expand_cache->original_face_sets[f]; + enabled = BLI_gset_haskey(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set)); + } + else { + const float loop_len = (expand_cache->max_face_falloff / expand_cache->loop_count) + + SCULPT_EXPAND_LOOP_THRESHOLD; + + const float active_factor = fmod(expand_cache->active_falloff, loop_len); + const float falloff_factor = fmod(expand_cache->face_falloff[f], loop_len); + enabled = falloff_factor < active_factor; + } + + if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET) { + if (ss->face_sets[f] == expand_cache->initial_active_face_set) { + enabled = false; + } + } + + if (expand_cache->invert) { + enabled = !enabled; + } + + return enabled; +} + +/* For target modes that support gradients (such as sculpt masks or colors), this function returns + * the corresponding gradient value for an enabled vertex. */ +static float sculpt_expand_gradient_value_get(SculptSession *ss, + ExpandCache *expand_cache, + const int v) +{ + if (!expand_cache->falloff_gradient) { + return 1.0f; + } + + const float max_falloff_factor = sculpt_expand_max_vertex_falloff_get(expand_cache); + const float loop_len = (max_falloff_factor / expand_cache->loop_count) + + SCULPT_EXPAND_LOOP_THRESHOLD; + + const float vertex_falloff_factor = sculpt_expand_falloff_value_vertex_get(ss, expand_cache, v); + const float active_factor = fmod(expand_cache->active_falloff, loop_len); + const float falloff_factor = fmod(vertex_falloff_factor, loop_len); + + float linear_falloff; + + if (expand_cache->invert) { + /* Active factor is the result of a modulus operation using loop_len, so they will never be + * equal and loop_len - active_factor should never be 0. */ + BLI_assert((loop_len - active_factor) != 0.0f); + linear_falloff = (falloff_factor - active_factor) / (loop_len - active_factor); + } + else { + linear_falloff = 1.0f - (falloff_factor / active_factor); + } + + if (!expand_cache->brush_gradient) { + return linear_falloff; + } + + return BKE_brush_curve_strength(expand_cache->brush, linear_falloff, 1.0f); +} + +/* Utility functions for getting all vertices state during expand. */ + +/* Returns a bitmap indexed by vertex index which contains if the vertex was enabled or not for a + * give expand_cache state. */ +static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCache *expand_cache) +{ + const int totvert = SCULPT_vertex_count_get(ss); + BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); + for (int i = 0; i < totvert; i++) { + const bool enabled = sculpt_expand_state_get(ss, expand_cache, i); + BLI_BITMAP_SET(enabled_vertices, i, enabled); + } + return enabled_vertices; +} + +/* Returns a bitmap indexed by vertex index which contains if the vertex is in the boundary of the + * enabled vertices. This is defined as vertices that are enabled and at least have one connected + * vertex that is not enabled. */ +static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, + BLI_bitmap *enabled_vertices, + const bool use_mesh_boundary) +{ + const int totvert = SCULPT_vertex_count_get(ss); + BLI_bitmap *boundary_vertices = BLI_BITMAP_NEW(totvert, "boundary vertices"); + for (int i = 0; i < totvert; i++) { + if (!BLI_BITMAP_TEST(enabled_vertices, i)) { + continue; + } + + bool is_expand_boundary = false; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + if (!BLI_BITMAP_TEST(enabled_vertices, ni.index)) { + is_expand_boundary = true; + } + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + + if (use_mesh_boundary && SCULPT_vertex_is_boundary(ss, i)) { + is_expand_boundary = true; + } + + BLI_BITMAP_SET(boundary_vertices, i, is_expand_boundary); + } + + return boundary_vertices; +} + +/* Functions implementing different algorithms for initializing falloff values. */ + +/* Utility function to get the closet vertex after flipping an original vertex possition based on + * an symmetry pass iteration index. */ +static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, + const char symm_it, + const int original_vertex) +{ + SculptSession *ss = ob->sculpt; + int symm_vertex = SCULPT_EXPAND_VERTEX_NONE; + if (symm_it == 0) { + symm_vertex = original_vertex; + } + else { + float location[3]; + flip_v3_v3(location, SCULPT_vertex_co_get(ss, original_vertex), symm_it); + symm_vertex = SCULPT_nearest_vertex_get(NULL, ob, location, FLT_MAX, false); + } + return symm_vertex; +} + +/* Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking + * symmetry into account. */ +static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v) +{ + return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX); +} + +/* Topology: Initializes the falloff using a floodfill operation, increasing the falloff value by 1 + * when visiting a new vertex. */ +typedef struct ExpandFloodFillData { + float original_normal[3]; + float edge_sensitivity; + float *dists; + float *edge_factor; +} ExpandFloodFillData; + +static bool expand_topology_floodfill_cb( + SculptSession *UNUSED(ss), int from_v, int to_v, bool is_duplicate, void *userdata) +{ + ExpandFloodFillData *data = userdata; + if (!is_duplicate) { + const float to_it = data->dists[from_v] + 1.0f; + data->dists[to_v] = to_it; + } + else { + data->dists[to_v] = data->dists[from_v]; + } + return true; +} + +static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, const int v) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "topology dist"); + + SculptFloodFill flood; + SCULPT_floodfill_init(ss, &flood); + SCULPT_floodfill_add_initial_with_symmetry(sd, ob, ss, &flood, v, FLT_MAX); + + ExpandFloodFillData fdata; + fdata.dists = dists; + + SCULPT_floodfill_execute(ss, &flood, expand_topology_floodfill_cb, &fdata); + SCULPT_floodfill_free(&flood); + + return dists; +} + +/* Normals: Floodfills the mesh and reduces the falloff depending on the normal difference between + * each vertex and the previous one. This creates falloff pattens that follow and snap to the hard + * edges of the object. */ + +static bool mask_expand_normal_floodfill_cb( + SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) +{ + ExpandFloodFillData *data = userdata; + if (!is_duplicate) { + float current_normal[3], prev_normal[3]; + SCULPT_vertex_normal_get(ss, to_v, current_normal); + SCULPT_vertex_normal_get(ss, from_v, prev_normal); + const float from_edge_factor = data->edge_factor[from_v]; + data->edge_factor[to_v] = dot_v3v3(current_normal, prev_normal) * from_edge_factor; + data->dists[to_v] = dot_v3v3(data->original_normal, current_normal) * + powf(from_edge_factor, data->edge_sensitivity); + CLAMP(data->dists[to_v], 0.0f, 1.0f); + } + else { + /* PBVH_GRIDS duplicate handling. */ + data->edge_factor[to_v] = data->edge_factor[from_v]; + data->dists[to_v] = data->dists[from_v]; + } + + return true; +} + +static float *sculpt_expand_normal_falloff_create(Sculpt *sd, + Object *ob, + const int v, + const float edge_sensitivity) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + float *dists = MEM_malloc_arrayN(sizeof(float), totvert, "normal dist"); + float *edge_factor = MEM_callocN(sizeof(float) * totvert, "mask edge factor"); + for (int i = 0; i < totvert; i++) { + edge_factor[i] = 1.0f; + } + + SculptFloodFill flood; + SCULPT_floodfill_init(ss, &flood); + SCULPT_floodfill_add_initial_with_symmetry(sd, ob, ss, &flood, v, FLT_MAX); + + ExpandFloodFillData fdata; + fdata.dists = dists; + fdata.edge_factor = edge_factor; + fdata.edge_sensitivity = edge_sensitivity; + SCULPT_vertex_normal_get(ss, v, fdata.original_normal); + + SCULPT_floodfill_execute(ss, &flood, mask_expand_normal_floodfill_cb, &fdata); + SCULPT_floodfill_free(&flood); + + for (int repeat = 0; repeat < 2; repeat++) { + for (int i = 0; i < totvert; i++) { + float avg = 0.0f; + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, i, ni) { + avg += dists[ni.index]; + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + dists[i] = avg / ni.size; + } + } + + MEM_SAFE_FREE(edge_factor); + + return dists; +} + +/* Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into + * account. */ + +static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + float *dists = MEM_malloc_arrayN(sizeof(float), totvert, "spherical dist"); + for (int i = 0; i < totvert; i++) { + dists[i] = FLT_MAX; + } + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + + for (char symm_it = 0; symm_it <= symm; symm_it++) { + if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { + continue; + } + const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + if (symm_vertex != -1) { + const float *co = SCULPT_vertex_co_get(ss, symm_vertex); + for (int i = 0; i < totvert; i++) { + dists[i] = min_ff(dists[i], len_v3v3(co, SCULPT_vertex_co_get(ss, i))); + } + } + } + + return dists; +} + +/* Boundary: This falloff mode uses the code from sculpt_boundary to initialize the closest mesh + * boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it + * stays parallel to the boundary, increasing the falloff value by 1 on each step. */ +static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const int v) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist"); + BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); + GSQueue *queue = BLI_gsqueue_new(sizeof(int)); + + /* Search and initialize a boundary per symmetry pass, then mark those vertices as visited. */ + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + for (char symm_it = 0; symm_it <= symm; symm_it++) { + if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { + continue; + } + + const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + + SculptBoundary *boundary = SCULPT_boundary_data_init(ob, NULL, symm_vertex, FLT_MAX); + if (!boundary) { + continue; + } + + for (int i = 0; i < boundary->num_vertices; i++) { + BLI_gsqueue_push(queue, &boundary->vertices[i]); + BLI_BITMAP_ENABLE(visited_vertices, boundary->vertices[i]); + } + SCULPT_boundary_data_free(boundary); + } + + /* If there are no boundaries, return a falloff with all values set to 0. */ + if (BLI_gsqueue_is_empty(queue)) { + return dists; + } + + /* Propagate the values from the boundaries to the rest of the mesh. */ + while (!BLI_gsqueue_is_empty(queue)) { + int v_next; + BLI_gsqueue_pop(queue, &v_next); + + SculptVertexNeighborIter ni; + SCULPT_VERTEX_NEIGHBORS_ITER_BEGIN (ss, v_next, ni) { + if (BLI_BITMAP_TEST(visited_vertices, ni.index)) { + continue; + } + dists[ni.index] = dists[v_next] + 1.0f; + BLI_BITMAP_ENABLE(visited_vertices, ni.index); + BLI_gsqueue_push(queue, &ni.index); + } + SCULPT_VERTEX_NEIGHBORS_ITER_END(ni); + } + + BLI_gsqueue_free(queue); + MEM_freeN(visited_vertices); + return dists; +} + +/* Topology diagonals. This falloff is similar to topology, but it also considers the diagonals of + * the base mesh faces when checking a vertex neighbor. For this reason, this is not implement + * using the general floodfill and sculpt neighbors accessors. */ +static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "spherical dist"); + + /* This algorithm uses mesh data (polys and loops), so this falloff type can't be initialized for + * Multires. It also does not make sense to implement it for dyntopo as the result will be the + * same as Topology falloff. */ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return dists; + } + + /* Search and mask as visited the initial vertices using the enabled symmetry passes. */ + BLI_bitmap *visited_vertices = BLI_BITMAP_NEW(totvert, "visited vertices"); + GSQueue *queue = BLI_gsqueue_new(sizeof(int)); + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + for (char symm_it = 0; symm_it <= symm; symm_it++) { + if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { + continue; + } + + const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass(ob, symm_it, v); + + BLI_gsqueue_push(queue, &symm_vertex); + BLI_BITMAP_ENABLE(visited_vertices, symm_vertex); + } + + if (BLI_gsqueue_is_empty(queue)) { + return dists; + } + + /* Propagate the falloff increasing the value by 1 each time a new vertex is visited. */ + Mesh *mesh = ob->data; + while (!BLI_gsqueue_is_empty(queue)) { + int v_next; + BLI_gsqueue_pop(queue, &v_next); + for (int j = 0; j < ss->pmap[v_next].count; j++) { + MPoly *p = &ss->mpoly[ss->pmap[v_next].indices[j]]; + for (int l = 0; l < p->totloop; l++) { + const int neighbor_v = mesh->mloop[p->loopstart + l].v; + if (BLI_BITMAP_TEST(visited_vertices, neighbor_v)) { + continue; + } + dists[neighbor_v] = dists[v_next] + 1.0f; + BLI_BITMAP_ENABLE(visited_vertices, neighbor_v); + BLI_gsqueue_push(queue, &neighbor_v); + } + } + } + + BLI_gsqueue_free(queue); + MEM_freeN(visited_vertices); + return dists; +} + +/* Functions to update the max_falloff value in the ExpandCache. These funcions are called after + * initializing a new falloff to make sure that this value is always updated. */ + +/* Updates the max_falloff value for vertices in a ExpandCache based on the current values of the + * falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. */ +static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss, + ExpandCache *expand_cache) +{ + const int totvert = SCULPT_vertex_count_get(ss); + expand_cache->max_vert_falloff = -FLT_MAX; + for (int i = 0; i < totvert; i++) { + if (expand_cache->vert_falloff[i] == FLT_MAX) { + continue; + } + + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) { + continue; + } + + expand_cache->max_vert_falloff = max_ff(expand_cache->max_vert_falloff, + expand_cache->vert_falloff[i]); + } +} + +/* Updates the max_falloff value for faces in a ExpandCache based on the current values of the + * falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. */ +static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss, + ExpandCache *expand_cache) +{ + const int totface = ss->totfaces; + expand_cache->max_face_falloff = -FLT_MAX; + for (int i = 0; i < totface; i++) { + if (expand_cache->face_falloff[i] == FLT_MAX) { + continue; + } + + if (!sculpt_expand_is_face_in_active_component(ss, expand_cache, i)) { + continue; + } + + expand_cache->max_face_falloff = max_ff(expand_cache->max_face_falloff, + expand_cache->face_falloff[i]); + } +} + +/* Functions to get falloff values for faces from the values from the vertices. This is used for + * expanding Face Sets. Depending on the data type of the SculptSession, this needs to get the per + * face falloff value from the connected vertices of each face or from the grids stored per loops + * for each face. */ +static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss, + Mesh *mesh, + ExpandCache *expand_cache) +{ + + const CCGKey *key = BKE_pbvh_get_grid_key(ss->pbvh); + + for (int p = 0; p < mesh->totpoly; p++) { + MPoly *poly = &mesh->mpoly[p]; + float accum = 0.0f; + for (int l = 0; l < poly->totloop; l++) { + const int grid_loop_index = (poly->loopstart + l) * key->grid_area; + for (int g = 0; g < key->grid_area; g++) { + accum += expand_cache->vert_falloff[grid_loop_index + g]; + } + } + expand_cache->face_falloff[p] = accum / (poly->totloop * key->grid_area); + } +} + +static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expand_cache) +{ + for (int p = 0; p < mesh->totpoly; p++) { + MPoly *poly = &mesh->mpoly[p]; + float accum = 0.0f; + for (int l = 0; l < poly->totloop; l++) { + MLoop *loop = &mesh->mloop[l + poly->loopstart]; + accum += expand_cache->vert_falloff[loop->v]; + } + expand_cache->face_falloff[p] = accum / poly->totloop; + } +} + +/* Main function to update the faces falloff from a already calculated vertex falloff. */ +static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *ss, + Mesh *mesh, + ExpandCache *expand_cache) +{ + BLI_assert(expand_cache->vert_falloff != NULL); + + if (!expand_cache->face_falloff) { + expand_cache->face_falloff = MEM_malloc_arrayN( + mesh->totpoly, sizeof(float), "face falloff factors"); + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + sculpt_expand_vertex_to_faces_falloff(mesh, expand_cache); + } + else if (BKE_pbvh_type(ss->pbvh) == PBVH_GRIDS) { + sculpt_expand_grids_to_faces_falloff(ss, mesh, expand_cache); + } + else { + BLI_assert(false); + } +} + +/* Recursions. These functions will generate new falloff values based on the state of the vertices + * from the current ExpandCache options and falloff values. */ + +/* Geodesic recursion: Initializes falloff values using geodesic distances from the boundary of the + * current vertices state. */ +static void sculpt_expand_geodesics_from_state_boundary(Object *ob, + ExpandCache *expand_cache, + BLI_bitmap *enabled_vertices) +{ + SculptSession *ss = ob->sculpt; + BLI_assert(BKE_pbvh_type(ss->pbvh) == PBVH_FACES); + + GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); + BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); + const int totvert = SCULPT_vertex_count_get(ss); + for (int i = 0; i < totvert; i++) { + if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + continue; + } + BLI_gset_add(initial_vertices, POINTER_FROM_INT(i)); + } + MEM_freeN(boundary_vertices); + + MEM_SAFE_FREE(expand_cache->vert_falloff); + MEM_SAFE_FREE(expand_cache->face_falloff); + + expand_cache->vert_falloff = SCULPT_geodesic_distances_create(ob, initial_vertices, FLT_MAX); + BLI_gset_free(initial_vertices, NULL); +} + +/* Topology recursion: Initializes falloff values using topology steps from the boundary of the + * current vertices state, increasing the value by 1 each time a new vertex is visited. */ +static void sculpt_expand_topology_from_state_boundary(Object *ob, + ExpandCache *expand_cache, + BLI_bitmap *enabled_vertices) +{ + MEM_SAFE_FREE(expand_cache->vert_falloff); + MEM_SAFE_FREE(expand_cache->face_falloff); + + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + float *dists = MEM_calloc_arrayN(sizeof(float), totvert, "topology dist"); + BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled(ss, enabled_vertices, false); + + SculptFloodFill flood; + SCULPT_floodfill_init(ss, &flood); + for (int i = 0; i < totvert; i++) { + if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + continue; + } + SCULPT_floodfill_add_and_skip_initial(&flood, i); + } + MEM_freeN(boundary_vertices); + + ExpandFloodFillData fdata; + fdata.dists = dists; + SCULPT_floodfill_execute(ss, &flood, expand_topology_floodfill_cb, &fdata); + SCULPT_floodfill_free(&flood); + + expand_cache->vert_falloff = dists; +} + +/* Main function to create a recursion step from the current ExpandCache state. */ +static void sculpt_expand_resursion_step_add(Object *ob, + ExpandCache *expand_cache, + const eSculptExpandRecursionType recursion_type) +{ + SculptSession *ss = ob->sculpt; + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return; + } + + BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + + /* Each time a new recursion step is created, reset the distortion strength. This is the expected + * result from the recursion, as otherwise the new falloff will render with undesired distortion + * from the beginning. */ + expand_cache->texture_distortion_strength = 0.0f; + + switch (recursion_type) { + case SCULPT_EXPAND_RECURSION_GEODESICS: + sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); + break; + case SCULPT_EXPAND_RECURSION_TOPOLOGY: + sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_vertices); + break; + } + + sculpt_expand_update_max_vert_falloff_value(ss, expand_cache); + if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) { + sculpt_expand_mesh_face_falloff_from_vertex_falloff(ss, ob->data, expand_cache); + sculpt_expand_update_max_face_falloff_factor(ss, expand_cache); + } + + MEM_freeN(enabled_vertices); +} + +/* Face Set Boundary falloff. */ +/* When internal falloff is set to true, the falloff will fill the active Face Set with a gradient, + * otherwise the active Face Set will be filled with a constant falloff of 0.0f. */ +static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, + ExpandCache *expand_cache, + const int active_face_set, + const bool internal_falloff) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + + BLI_bitmap *enabled_vertices = BLI_BITMAP_NEW(totvert, "enabled vertices"); + for (int i = 0; i < totvert; i++) { + if (!SCULPT_vertex_has_unique_face_set(ss, i)) { + continue; + } + if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + continue; + } + BLI_BITMAP_ENABLE(enabled_vertices, i); + } + + if (BKE_pbvh_type(ss->pbvh) == PBVH_FACES) { + sculpt_expand_geodesics_from_state_boundary(ob, expand_cache, enabled_vertices); + } + else { + sculpt_expand_topology_from_state_boundary(ob, expand_cache, enabled_vertices); + } + + MEM_freeN(enabled_vertices); + + if (internal_falloff) { + for (int i = 0; i < totvert; i++) { + if (!(SCULPT_vertex_has_face_set(ss, i, active_face_set) && + SCULPT_vertex_has_unique_face_set(ss, i))) { + continue; + } + expand_cache->vert_falloff[i] *= -1.0f; + } + + float min_factor = FLT_MAX; + for (int i = 0; i < totvert; i++) { + min_factor = min_ff(expand_cache->vert_falloff[i], min_factor); + } + + const float additional_falloff = fabsf(min_factor); + for (int i = 0; i < totvert; i++) { + expand_cache->vert_falloff[i] += additional_falloff; + } + } + else { + for (int i = 0; i < totvert; i++) { + if (!SCULPT_vertex_has_face_set(ss, i, active_face_set)) { + continue; + } + expand_cache->vert_falloff[i] = 0.0f; + } + } +} + +/* Main function to initialize new falloff values in a ExpandCache given an initial vertex and a + * falloff type. */ +static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( + ExpandCache *expand_cache, + Sculpt *sd, + Object *ob, + const int v, + eSculptExpandFalloffType falloff_type) +{ + MEM_SAFE_FREE(expand_cache->vert_falloff); + expand_cache->falloff_type = falloff_type; + + SculptSession *ss = ob->sculpt; + const bool has_topology_info = BKE_pbvh_type(ss->pbvh) == PBVH_FACES; + + switch (falloff_type) { + case SCULPT_EXPAND_FALLOFF_GEODESIC: + expand_cache->vert_falloff = has_topology_info ? + sculpt_expand_geodesic_falloff_create(sd, ob, v) : + sculpt_expand_spherical_falloff_create(ob, v); + break; + case SCULPT_EXPAND_FALLOFF_TOPOLOGY: + expand_cache->vert_falloff = sculpt_expand_topology_falloff_create(sd, ob, v); + break; + case SCULPT_EXPAND_FALLOFF_TOPOLOGY_DIAGONALS: + expand_cache->vert_falloff = has_topology_info ? + sculpt_expand_diagonals_falloff_create(ob, v) : + sculpt_expand_topology_falloff_create(sd, ob, v); + break; + case SCULPT_EXPAND_FALLOFF_NORMALS: + expand_cache->vert_falloff = sculpt_expand_normal_falloff_create( + sd, ob, v, SCULPT_EXPAND_NORMALS_FALLOFF_EDGE_SENSITIVITY); + break; + case SCULPT_EXPAND_FALLOFF_SPHERICAL: + expand_cache->vert_falloff = sculpt_expand_spherical_falloff_create(ob, v); + break; + case SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY: + expand_cache->vert_falloff = sculpt_expand_boundary_topology_falloff_create(ob, v); + break; + case SCULPT_EXPAND_FALLOFF_BOUNDARY_FACE_SET: + sculpt_expand_initialize_from_face_set_boundary( + ob, expand_cache, expand_cache->initial_active_face_set, true); + break; + case SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET: + sculpt_expand_initialize_from_face_set_boundary( + ob, expand_cache, expand_cache->initial_active_face_set, false); + break; + } + + /* Update max falloff values and propagate to base mesh faces if needed. */ + sculpt_expand_update_max_vert_falloff_value(ss, expand_cache); + if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) { + sculpt_expand_mesh_face_falloff_from_vertex_falloff(ss, ob->data, expand_cache); + sculpt_expand_update_max_face_falloff_factor(ss, expand_cache); + } +} + +/* Adds to the snapping Face Set gset all Face Sets which contain all enabled vertices for the + * current ExpandCache state. This improves the usability of snapping, as already enabled elements + * won't switch their state when toggling snapping with the modal keymap.*/ +static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, + ExpandCache *expand_cache) +{ + if (BKE_pbvh_type(ss->pbvh) != PBVH_FACES) { + return; + } + + /* Make sure this code runs with snapping and invert disabled. This simplifies the code and + * prevents using this function with snapping already enabled. */ + const bool prev_snap_state = expand_cache->snap; + const bool prev_invert_state = expand_cache->invert; + expand_cache->snap = false; + expand_cache->invert = false; + + BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + + const int totface = ss->totfaces; + for (int i = 0; i < totface; i++) { + const int face_set = expand_cache->original_face_sets[i]; + BLI_gset_add(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set)); + } + + for (int p = 0; p < totface; p++) { + MPoly *poly = &ss->mpoly[p]; + bool any_disabled = false; + for (int l = 0; l < poly->totloop; l++) { + MLoop *loop = &ss->mloop[l + poly->loopstart]; + if (!BLI_BITMAP_TEST(enabled_vertices, loop->v)) { + any_disabled = true; + break; + } + } + if (any_disabled) { + const int face_set = expand_cache->original_face_sets[p]; + BLI_gset_remove(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(face_set), NULL); + } + } + + MEM_freeN(enabled_vertices); + expand_cache->snap = prev_snap_state; + expand_cache->invert = prev_invert_state; +} + +/* Functions to free a ExpandCache. */ +static void sculpt_expand_cache_data_free(ExpandCache *expand_cache) +{ + if (expand_cache->snap_enabled_face_sets) { + BLI_gset_free(expand_cache->snap_enabled_face_sets, NULL); + } + MEM_SAFE_FREE(expand_cache->nodes); + MEM_SAFE_FREE(expand_cache->vert_falloff); + MEM_SAFE_FREE(expand_cache->face_falloff); + MEM_SAFE_FREE(expand_cache->original_mask); + MEM_SAFE_FREE(expand_cache->original_face_sets); + MEM_SAFE_FREE(expand_cache->initial_face_sets); + MEM_SAFE_FREE(expand_cache->original_colors); + MEM_SAFE_FREE(expand_cache); +} + +static void sculpt_expand_cache_free(SculptSession *ss) +{ + sculpt_expand_cache_data_free(ss->expand_cache); + /* Needs to be set to NULL as the paint cursor relies on checking this pointer detecting if an + * expand operation is running. */ + ss->expand_cache = NULL; +} + +/* Functions to restore the original state from the ExpandCache when canceling the operator. */ +static void sculpt_expand_restore_face_set_data(SculptSession *ss, ExpandCache *expand_cache) +{ + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + for (int n = 0; n < totnode; n++) { + PBVHNode *node = nodes[n]; + BKE_pbvh_node_mark_redraw(node); + } + MEM_freeN(nodes); + for (int i = 0; i < ss->totfaces; i++) { + ss->face_sets[i] = expand_cache->original_face_sets[i]; + } +} + +static void sculpt_expand_restore_color_data(SculptSession *ss, ExpandCache *expand_cache) +{ + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + for (int n = 0; n < totnode; n++) { + PBVHNode *node = nodes[n]; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + copy_v4_v4(vd.col, expand_cache->original_colors[vd.index]); + } + BKE_pbvh_vertex_iter_end; + BKE_pbvh_node_mark_redraw(node); + } + MEM_freeN(nodes); +} + +static void sculpt_expand_restore_mask_data(SculptSession *ss, ExpandCache *expand_cache) +{ + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + for (int n = 0; n < totnode; n++) { + PBVHNode *node = nodes[n]; + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_UNIQUE) + { + *vd.mask = expand_cache->original_mask[vd.index]; + } + BKE_pbvh_vertex_iter_end; + BKE_pbvh_node_mark_redraw(node); + } + MEM_freeN(nodes); +} + +/* Main function to restore the original state of the data to how it was before starting the expand + * operation. */ +static void sculpt_expand_restore_original_state(bContext *C, + Object *ob, + ExpandCache *expand_cache) +{ + + SculptSession *ss = ob->sculpt; + switch (expand_cache->target) { + case SCULPT_EXPAND_TARGET_MASK: + sculpt_expand_restore_mask_data(ss, expand_cache); + SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + SCULPT_tag_update_overlays(C); + break; + case SCULPT_EXPAND_TARGET_FACE_SETS: + sculpt_expand_restore_face_set_data(ss, expand_cache); + SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + SCULPT_tag_update_overlays(C); + break; + case SCULPT_EXPAND_TARGET_COLORS: + sculpt_expand_restore_color_data(ss, expand_cache); + SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR); + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR); + break; + } +} + +/* Cancel operator callback. */ +static void sculpt_expand_cancel(bContext *C, wmOperator *UNUSED(op)) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + + sculpt_expand_restore_original_state(C, ob, ss->expand_cache); + + SCULPT_undo_push_end(); + sculpt_expand_cache_free(ss); +} + +/* Functions to update the sculpt mesh data. */ + +/* Callback to update mask data per PBVH node. */ +static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + ExpandCache *expand_cache = ss->expand_cache; + + bool any_changed = false; + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) + { + const float initial_mask = *vd.mask; + const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); + + float new_mask; + + if (enabled) { + new_mask = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index); + } + else { + new_mask = 0.0f; + } + + if (expand_cache->preserve) { + new_mask = max_ff(new_mask, expand_cache->original_mask[vd.index]); + } + + if (new_mask == initial_mask) { + continue; + } + + *vd.mask = clamp_f(new_mask, 0.0f, 1.0f); + any_changed = true; + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; + if (any_changed) { + BKE_pbvh_node_mark_update_mask(node); + } +} + +/* Update Face Set data. Not multithreaded per node as nodes don't contain face arrays. */ +static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expand_cache) +{ + const int totface = ss->totfaces; + for (int f = 0; f < totface; f++) { + const bool enabled = sculpt_expand_face_state_get(ss, expand_cache, f); + if (!enabled) { + continue; + } + if (expand_cache->preserve) { + ss->face_sets[f] += expand_cache->next_face_set; + } + else { + ss->face_sets[f] = expand_cache->next_face_set; + } + } + + for (int i = 0; i < expand_cache->totnode; i++) { + BKE_pbvh_node_mark_redraw(ss->expand_cache->nodes[i]); + } +} + +/* Callback to update vertex colors per PBVH node. */ +static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, + const int i, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + SculptThreadedTaskData *data = userdata; + SculptSession *ss = data->ob->sculpt; + PBVHNode *node = data->nodes[i]; + ExpandCache *expand_cache = ss->expand_cache; + + bool any_changed = false; + + PBVHVertexIter vd; + BKE_pbvh_vertex_iter_begin(ss->pbvh, node, vd, PBVH_ITER_ALL) + { + float initial_color[4]; + copy_v4_v4(initial_color, vd.col); + + const bool enabled = sculpt_expand_state_get(ss, expand_cache, vd.index); + float fade; + + if (enabled) { + fade = sculpt_expand_gradient_value_get(ss, expand_cache, vd.index); + } + else { + fade = 0.0f; + } + + fade *= 1.0f - *vd.mask; + fade = clamp_f(fade, 0.0f, 1.0f); + + float final_color[4]; + float final_fill_color[4]; + mul_v4_v4fl(final_fill_color, expand_cache->fill_color, fade); + IMB_blend_color_float(final_color, + expand_cache->original_colors[vd.index], + final_fill_color, + expand_cache->blend_mode); + + if (equals_v4v4(initial_color, final_color)) { + continue; + } + + copy_v4_v4(vd.col, final_color); + any_changed = true; + if (vd.mvert) { + vd.mvert->flag |= ME_VERT_PBVH_UPDATE; + } + } + BKE_pbvh_vertex_iter_end; + if (any_changed) { + BKE_pbvh_node_mark_update_color(node); + } +} + +static void sculpt_expand_flush_updates(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + switch (ss->expand_cache->target) { + case SCULPT_EXPAND_TARGET_MASK: + SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); + break; + case SCULPT_EXPAND_TARGET_FACE_SETS: + SCULPT_flush_update_step(C, SCULPT_UPDATE_MASK); + break; + case SCULPT_EXPAND_TARGET_COLORS: + SCULPT_flush_update_step(C, SCULPT_UPDATE_COLOR); + break; + default: + break; + } +} + +/* Store the original mesh data state in the expand cache. */ +static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_cache) +{ + SculptSession *ss = ob->sculpt; + const int totvert = SCULPT_vertex_count_get(ss); + const int totface = ss->totfaces; + + /* Face Sets are always stored as they are needed for snapping. */ + expand_cache->initial_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "initial face set"); + expand_cache->original_face_sets = MEM_malloc_arrayN(totface, sizeof(int), "original face set"); + for (int i = 0; i < totface; i++) { + expand_cache->initial_face_sets[i] = ss->face_sets[i]; + expand_cache->original_face_sets[i] = ss->face_sets[i]; + } + + if (expand_cache->target == SCULPT_EXPAND_TARGET_MASK) { + expand_cache->original_mask = MEM_malloc_arrayN(totvert, sizeof(float), "initial mask"); + for (int i = 0; i < totvert; i++) { + expand_cache->original_mask[i] = SCULPT_vertex_mask_get(ss, i); + } + } + + if (expand_cache->target == SCULPT_EXPAND_TARGET_COLORS) { + expand_cache->original_colors = MEM_malloc_arrayN(totvert, sizeof(float[4]), "initial colors"); + for (int i = 0; i < totvert; i++) { + copy_v4_v4(expand_cache->original_colors[i], SCULPT_vertex_color_get(ss, i)); + } + } +} + +/* Restore the state of the Face Sets before a new update. */ +static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expand_cache) +{ + const int totfaces = ss->totfaces; + for (int i = 0; i < totfaces; i++) { + ss->face_sets[i] = expand_cache->initial_face_sets[i]; + } +} + +static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int vertex) +{ + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + ExpandCache *expand_cache = ss->expand_cache; + + /* Update the active factor in the cache. */ + if (vertex == SCULPT_EXPAND_VERTEX_NONE) { + /* This means that the cursor is not over the mesh, so a valid active falloff can't be + * determined. In this situations, don't evaluate enabled states and default all vertices in + * connected components to enabled. */ + expand_cache->active_falloff = expand_cache->max_vert_falloff; + expand_cache->all_enabled = true; + } + else { + expand_cache->active_falloff = expand_cache->vert_falloff[vertex]; + expand_cache->all_enabled = false; + } + + if (expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS) { + /* Face sets needs to be restored their initial state on each iteration as the overwrite + * existing data. */ + sculpt_expand_face_sets_restore(ss, expand_cache); + } + + /* Update the mesh sculpt data. */ + SculptThreadedTaskData data = { + .sd = sd, + .ob = ob, + .nodes = expand_cache->nodes, + }; + + TaskParallelSettings settings; + BKE_pbvh_parallel_range_settings(&settings, true, expand_cache->totnode); + + switch (expand_cache->target) { + case SCULPT_EXPAND_TARGET_MASK: + BLI_task_parallel_range( + 0, expand_cache->totnode, &data, sculpt_expand_mask_update_task_cb, &settings); + break; + case SCULPT_EXPAND_TARGET_FACE_SETS: + sculpt_expand_face_sets_update(ss, expand_cache); + break; + case SCULPT_EXPAND_TARGET_COLORS: + BLI_task_parallel_range( + 0, expand_cache->totnode, &data, sculpt_expand_colors_update_task_cb, &settings); + break; + } + + sculpt_expand_flush_updates(C); +} + +/* Updates the SculptSession cursor data and gets the active vertex if the cursor is over the mesh. + */ +static int sculpt_expand_target_vertex_update_and_get(bContext *C, + Object *ob, + const float mouse[2]) +{ + SculptSession *ss = ob->sculpt; + SculptCursorGeometryInfo sgi; + if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { + return SCULPT_active_vertex_get(ss); + } + else { + return SCULPT_EXPAND_VERTEX_NONE; + } +} + +/* Moves the sculpt pivot to the average point of the boundary enabled vertices of the current + * expand state. Take symmetry and active components into account. */ +static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache *expand_cache) +{ + SculptSession *ss = ob->sculpt; + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + const int totvert = SCULPT_vertex_count_get(ss); + + const bool initial_invert_state = expand_cache->invert; + expand_cache->invert = false; + BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); + + /* For boundary topology, position the pivot using only the boundary of the enabled vertices, + * without taking mesh boundary into account. This allows to creat deformations like bending the + * mesh from the boundary of the mask that was just created. */ + const float use_mesh_boundary = expand_cache->falloff_type != + SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; + + BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled( + ss, enabled_vertices, use_mesh_boundary); + + /* Ignore invert state, as this is the expected behaviour in most cases and mask are created in + * inverted state by default. */ + expand_cache->invert = initial_invert_state; + + int total = 0; + float avg[3] = {0.0f}; + + const float *expand_init_co = SCULPT_vertex_co_get(ss, expand_cache->initial_active_vertex); + + for (int i = 0; i < totvert; i++) { + if (!BLI_BITMAP_TEST(boundary_vertices, i)) { + continue; + } + + if (!sculpt_expand_is_vert_in_active_component(ss, expand_cache, i)) { + continue; + } + + const float *vertex_co = SCULPT_vertex_co_get(ss, i); + + if (!SCULPT_check_vertex_pivot_symmetry(vertex_co, expand_init_co, symm)) { + continue; + } + + add_v3_v3(avg, vertex_co); + total++; + } + + MEM_freeN(enabled_vertices); + MEM_freeN(boundary_vertices); + + if (total > 0) { + mul_v3_v3fl(ss->pivot_pos, avg, 1.0f / total); + } + + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob->data); +} + +static void sculpt_expand_finish(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + SCULPT_undo_push_end(); + + /* Tag all nodes to redraw to avoid artifacts after the fast partial updates. */ + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + for (int n = 0; n < totnode; n++) { + BKE_pbvh_node_mark_update_mask(nodes[n]); + } + MEM_freeN(nodes); + + switch (ss->expand_cache->target) { + case SCULPT_EXPAND_TARGET_MASK: + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + break; + case SCULPT_EXPAND_TARGET_FACE_SETS: + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_MASK); + break; + case SCULPT_EXPAND_TARGET_COLORS: + SCULPT_flush_update_done(C, ob, SCULPT_UPDATE_COLOR); + break; + } + + sculpt_expand_cache_free(ss); + ED_workspace_status_text(C, NULL); +} + +/* Finds and stores in the ExpandCache the sculpt connected component index for each symmetry pass + * needed for expand. */ +static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, + ExpandCache *expand_cache, + const int initial_vertex) +{ + SculptSession *ss = ob->sculpt; + for (int i = 0; i < EXPAND_SYMM_AREAS; i++) { + expand_cache->active_connected_components[i] = EXPAND_ACTIVE_COMPONENT_NONE; + } + + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + for (char symm_it = 0; symm_it <= symm; symm_it++) { + if (!SCULPT_is_symmetry_iteration_valid(symm_it, symm)) { + continue; + } + + const int symm_vertex = sculpt_expand_get_vertex_index_for_symmetry_pass( + ob, symm_it, initial_vertex); + + expand_cache->active_connected_components[(int)symm_it] = + ss->vertex_info.connected_component[symm_vertex]; + } +} + +/* Stores the active vertex, Face Set and mouse coordinates in the ExpandCache based on the current + * cursor position. */ +static void sculpt_expand_set_initial_components_for_mouse(bContext *C, + Object *ob, + ExpandCache *expand_cache, + const float mouse[2]) +{ + SculptSession *ss = ob->sculpt; + int initial_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); + if (initial_vertex == SCULPT_EXPAND_VERTEX_NONE) { + /* Cursor not over the mesh, for creating valid initial falloffs, fallback to the last active + * vertex in the sculpt session. */ + initial_vertex = SCULPT_active_vertex_get(ss); + } + copy_v2_v2(ss->expand_cache->initial_mouse, mouse); + expand_cache->initial_active_vertex = initial_vertex; + expand_cache->initial_active_face_set = SCULPT_active_face_set_get(ss); + + if (expand_cache->next_face_set == SCULPT_FACE_SET_NONE) { + /* Only set the next face set once, otherwise this ID will constantly update to a new one each + * time this function is called for using a new initial vertex from a different cursor + * position. */ + if (expand_cache->modify_active_face_set) { + expand_cache->next_face_set = SCULPT_active_face_set_get(ss); + } + else { + expand_cache->next_face_set = ED_sculpt_face_sets_find_next_available_id(ob->data); + } + } + + /* The new mouse position can be over a different connected component, so this needs to be + * updated. */ + sculpt_expand_find_active_connected_components_from_vert(ob, expand_cache, initial_vertex); +} + +/* Displaces the initial mouse coordinates using the new mouse position to get a new active vertex. + * After that, initializes a new falloff of the same type with the new active vertex. */ +static void sculpt_expand_move_propagation_origin(bContext *C, + Object *ob, + const wmEvent *event, + ExpandCache *expand_cache) +{ + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + const float mouse[2] = {event->mval[0], event->mval[1]}; + float move_disp[2]; + sub_v2_v2v2(move_disp, mouse, expand_cache->initial_mouse_move); + + float new_mouse[2]; + add_v2_v2v2(new_mouse, move_disp, expand_cache->original_mouse_move); + + sculpt_expand_set_initial_components_for_mouse(C, ob, expand_cache, new_mouse); + sculpt_expand_falloff_factors_from_vertex_and_symm_create( + expand_cache, + sd, + ob, + expand_cache->initial_active_vertex, + expand_cache->move_preview_falloff_type); +} + +/* Ensures that the SculptSession contains the required data needed for Expand. */ +static void sculpt_expand_ensure_sculptsession_data(Object *ob) +{ + SculptSession *ss = ob->sculpt; + SCULPT_vertex_random_access_ensure(ss); + SCULPT_connected_components_ensure(ob); + SCULPT_boundary_info_ensure(ob); + if (!ss->tex_pool) { + ss->tex_pool = BKE_image_pool_new(); + } +} + +/* Returns the active Face Sets ID from the enabled face or grid in the SculptSession. */ +static int sculpt_expand_active_face_set_id_get(SculptSession *ss, ExpandCache *expand_cache) +{ + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return expand_cache->original_face_sets[ss->active_face_index]; + case PBVH_GRIDS: { + const int face_index = BKE_subdiv_ccg_grid_to_face_index(ss->subdiv_ccg, + ss->active_grid_index); + return expand_cache->original_face_sets[face_index]; + } + case PBVH_BMESH: { + /* Dyntopo does not support Face Set functionality. */ + BLI_assert(false); + } + } + return SCULPT_FACE_SET_NONE; +} + +static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + /* Skips INBETWEEN_MOUSEMOVE events and other events that may cause unnecessary updates. */ + if (!ELEM(event->type, MOUSEMOVE, EVT_MODAL_MAP)) { + return OPERATOR_RUNNING_MODAL; + } + + /* Update SculptSession data. */ + Depsgraph *depsgraph = CTX_data_depsgraph_pointer(C); + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, false); + sculpt_expand_ensure_sculptsession_data(ob); + + /* Update and get the active vertex (and face) from the cursor. */ + const float mouse[2] = {event->mval[0], event->mval[1]}; + const int target_expand_vertex = sculpt_expand_target_vertex_update_and_get(C, ob, mouse); + + /* Handle the modal keymap state changes. */ + ExpandCache *expand_cache = ss->expand_cache; + if (event->type == EVT_MODAL_MAP) { + switch (event->val) { + case SCULPT_EXPAND_MODAL_CANCEL: { + sculpt_expand_cancel(C, op); + return OPERATOR_FINISHED; + } + case SCULPT_EXPAND_MODAL_INVERT: { + expand_cache->invert = !expand_cache->invert; + break; + } + case SCULPT_EXPAND_MODAL_PRESERVE_TOGGLE: { + expand_cache->preserve = !expand_cache->preserve; + break; + } + case SCULPT_EXPAND_MODAL_GRADIENT_TOGGLE: { + expand_cache->falloff_gradient = !expand_cache->falloff_gradient; + break; + } + case SCULPT_EXPAND_MODAL_BRUSH_GRADIENT_TOGGLE: { + expand_cache->brush_gradient = !expand_cache->brush_gradient; + if (expand_cache->brush_gradient) { + expand_cache->falloff_gradient = true; + } + break; + } + case SCULPT_EXPAND_MODAL_SNAP_TOGGLE: { + if (expand_cache->snap) { + expand_cache->snap = false; + if (expand_cache->snap_enabled_face_sets) { + BLI_gset_free(expand_cache->snap_enabled_face_sets, NULL); + expand_cache->snap_enabled_face_sets = NULL; + } + } + else { + expand_cache->snap = true; + if (!expand_cache->snap_enabled_face_sets) { + expand_cache->snap_enabled_face_sets = BLI_gset_int_new("snap face sets"); + } + sculpt_expand_snap_initialize_from_enabled(ss, expand_cache); + } + } break; + case SCULPT_EXPAND_MODAL_MOVE_TOGGLE: { + if (expand_cache->move) { + expand_cache->move = false; + sculpt_expand_falloff_factors_from_vertex_and_symm_create( + expand_cache, + sd, + ob, + expand_cache->initial_active_vertex, + expand_cache->move_original_falloff_type); + break; + } + else { + expand_cache->move = true; + expand_cache->move_original_falloff_type = expand_cache->falloff_type; + copy_v2_v2(expand_cache->initial_mouse_move, mouse); + copy_v2_v2(expand_cache->original_mouse_move, expand_cache->initial_mouse); + if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_GEODESIC && + SCULPT_vertex_count_get(ss) > expand_cache->max_geodesic_move_preview) { + /* Set to spherical falloff for preview in high poly meshes as it is the fastest one. + * In most cases it should match closely the preview from geodesic. */ + expand_cache->move_preview_falloff_type = SCULPT_EXPAND_FALLOFF_SPHERICAL; + } + else { + expand_cache->move_preview_falloff_type = expand_cache->falloff_type; + } + } + break; + } + case SCULPT_EXPAND_MODAL_RECURSION_STEP_GEODESIC: { + sculpt_expand_resursion_step_add(ob, expand_cache, SCULPT_EXPAND_RECURSION_GEODESICS); + break; + } + case SCULPT_EXPAND_MODAL_RECURSION_STEP_TOPOLOGY: { + sculpt_expand_resursion_step_add(ob, expand_cache, SCULPT_EXPAND_RECURSION_TOPOLOGY); + break; + } + case SCULPT_EXPAND_MODAL_CONFIRM: { + sculpt_expand_update_for_vertex(C, ob, target_expand_vertex); + + if (expand_cache->reposition_pivot) { + sculpt_expand_reposition_pivot(C, ob, expand_cache); + } + + sculpt_expand_finish(C); + return OPERATOR_FINISHED; + } + case SCULPT_EXPAND_MODAL_FALLOFF_GEODESIC: { + sculpt_expand_falloff_factors_from_vertex_and_symm_create( + expand_cache, + sd, + ob, + expand_cache->initial_active_vertex, + SCULPT_EXPAND_FALLOFF_GEODESIC); + break; + } + case SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY: { + sculpt_expand_falloff_factors_from_vertex_and_symm_create( + expand_cache, + sd, + ob, + expand_cache->initial_active_vertex, + SCULPT_EXPAND_FALLOFF_TOPOLOGY); + break; + } + case SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY_DIAGONALS: { + sculpt_expand_falloff_factors_from_vertex_and_symm_create( + expand_cache, + sd, + ob, + expand_cache->initial_active_vertex, + SCULPT_EXPAND_FALLOFF_TOPOLOGY_DIAGONALS); + break; + } + case SCULPT_EXPAND_MODAL_FALLOFF_SPHERICAL: { + sculpt_expand_falloff_factors_from_vertex_and_symm_create( + expand_cache, + sd, + ob, + expand_cache->initial_active_vertex, + SCULPT_EXPAND_FALLOFF_SPHERICAL); + break; + } + case SCULPT_EXPAND_MODAL_LOOP_COUNT_INCREASE: { + expand_cache->loop_count += 1; + break; + } + case SCULPT_EXPAND_MODAL_LOOP_COUNT_DECREASE: { + expand_cache->loop_count -= 1; + expand_cache->loop_count = max_ii(expand_cache->loop_count, 1); + break; + } + case SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_INCREASE: { + if (expand_cache->texture_distortion_strength == 0.0f) { + if (expand_cache->brush->mtex.tex == NULL) { + BKE_report(op->reports, + RPT_WARNING, + "Active brush does not contain any texture to distort the expand boundary"); + break; + } + if (expand_cache->brush->mtex.brush_map_mode != MTEX_MAP_MODE_3D) { + BKE_report(op->reports, + RPT_WARNING, + "Texture mapping not set to 3D, results may be unpredictable"); + } + } + expand_cache->texture_distortion_strength += SCULPT_EXPAND_TEXTURE_DISTORTION_STEP; + break; + } + case SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_DECREASE: { + expand_cache->texture_distortion_strength -= SCULPT_EXPAND_TEXTURE_DISTORTION_STEP; + expand_cache->texture_distortion_strength = max_ff( + expand_cache->texture_distortion_strength, 0.0f); + break; + } + } + } + + /* Handle expand origin movement if enabled. */ + if (expand_cache->move) { + sculpt_expand_move_propagation_origin(C, ob, event, expand_cache); + } + + /* Add new Face Sets IDs to the snapping gset if enabled. */ + if (expand_cache->snap) { + const int active_face_set_id = sculpt_expand_active_face_set_id_get(ss, expand_cache); + if (!BLI_gset_haskey(expand_cache->snap_enabled_face_sets, + POINTER_FROM_INT(active_face_set_id))) { + BLI_gset_add(expand_cache->snap_enabled_face_sets, POINTER_FROM_INT(active_face_set_id)); + } + } + + /* Update the sculpt data with the current state of the ExpandCache. */ + sculpt_expand_update_for_vertex(C, ob, target_expand_vertex); + + return OPERATOR_RUNNING_MODAL; +} + +/* Deletes the delete_id Face Set ID from the mesh Face Sets and stores the result in r_face_set. + * The faces that were using the delete_id Face Set are filled using the content from their + * neighbors. */ +static void sculpt_expand_delete_face_set_id( + int *r_face_sets, Mesh *mesh, MeshElemMap *pmap, const int totface, const int delete_id) +{ + /* Check that all the face sets IDs in the mesh are not equal to delete_id befor attempting to + * delete it. */ + bool all_same_id = true; + for (int i = 0; i < totface; i++) { + if (r_face_sets[i] != delete_id) { + all_same_id = false; + break; + } + } + if (all_same_id) { + return; + } + + BLI_LINKSTACK_DECLARE(queue, void *); + BLI_LINKSTACK_DECLARE(queue_next, void *); + + BLI_LINKSTACK_INIT(queue); + BLI_LINKSTACK_INIT(queue_next); + + for (int i = 0; i < totface; i++) { + if (r_face_sets[i] == delete_id) { + BLI_LINKSTACK_PUSH(queue, POINTER_FROM_INT(i)); + } + } + + while (BLI_LINKSTACK_SIZE(queue)) { + while (BLI_LINKSTACK_SIZE(queue)) { + const int f_index = POINTER_AS_INT(BLI_LINKSTACK_POP(queue)); + int other_id = delete_id; + const MPoly *c_poly = &mesh->mpoly[f_index]; + for (int l = 0; l < c_poly->totloop; l++) { + const MLoop *c_loop = &mesh->mloop[c_poly->loopstart + l]; + const MeshElemMap *vert_map = &pmap[c_loop->v]; + for (int i = 0; i < vert_map->count; i++) { + + const int neighbor_face_index = vert_map->indices[i]; + if (r_face_sets[neighbor_face_index] != delete_id) { + other_id = r_face_sets[neighbor_face_index]; + } + } + } + + if (other_id != delete_id) { + r_face_sets[f_index] = other_id; + } + else { + BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(f_index)); + } + } + + BLI_LINKSTACK_SWAP(queue, queue_next); + } + + BLI_LINKSTACK_FREE(queue); + BLI_LINKSTACK_FREE(queue_next); +} + +static void sculpt_expand_cache_initial_config_set(bContext *C, + wmOperator *op, + ExpandCache *expand_cache) +{ + /* RNA properties. */ + expand_cache->invert = RNA_boolean_get(op->ptr, "invert"); + expand_cache->preserve = RNA_boolean_get(op->ptr, "use_mask_preserve"); + expand_cache->falloff_gradient = RNA_boolean_get(op->ptr, "use_falloff_gradient"); + expand_cache->target = RNA_enum_get(op->ptr, "target"); + expand_cache->modify_active_face_set = RNA_boolean_get(op->ptr, "use_modify_active"); + expand_cache->reposition_pivot = RNA_boolean_get(op->ptr, "use_reposition_pivot"); + expand_cache->max_geodesic_move_preview = RNA_int_get(op->ptr, "max_geodesic_move_preview"); + + /* These can be exposed in RNA if needed. */ + expand_cache->loop_count = 1; + expand_cache->brush_gradient = false; + + /* Texture and color data from the active Brush. */ + Object *ob = CTX_data_active_object(C); + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + SculptSession *ss = ob->sculpt; + expand_cache->brush = BKE_paint_brush(&sd->paint); + BKE_curvemapping_init(expand_cache->brush->curve); + copy_v4_fl(expand_cache->fill_color, 1.0f); + copy_v3_v3(expand_cache->fill_color, BKE_brush_color_get(ss->scene, expand_cache->brush)); + IMB_colormanagement_srgb_to_scene_linear_v3(expand_cache->fill_color); + + expand_cache->scene = CTX_data_scene(C); + expand_cache->mtex = &expand_cache->brush->mtex; + expand_cache->texture_distortion_strength = 0.0f; + expand_cache->blend_mode = expand_cache->brush->blend; +} + +/* Does the undo sculpt push for the affected target data of the ExpandCache. */ +static void sculpt_expand_undo_push(Object *ob, ExpandCache *expand_cache) +{ + SculptSession *ss = ob->sculpt; + PBVHNode **nodes; + int totnode; + BKE_pbvh_search_gather(ss->pbvh, NULL, NULL, &nodes, &totnode); + + switch (expand_cache->target) { + case SCULPT_EXPAND_TARGET_MASK: + for (int i = 0; i < totnode; i++) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_MASK); + } + break; + case SCULPT_EXPAND_TARGET_FACE_SETS: + SCULPT_undo_push_node(ob, nodes[0], SCULPT_UNDO_FACE_SETS); + break; + case SCULPT_EXPAND_TARGET_COLORS: + for (int i = 0; i < totnode; i++) { + SCULPT_undo_push_node(ob, nodes[i], SCULPT_UNDO_COLOR); + } + break; + } + + MEM_freeN(nodes); +} + +static int sculpt_expand_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + Object *ob = CTX_data_active_object(C); + SculptSession *ss = ob->sculpt; + Sculpt *sd = CTX_data_tool_settings(C)->sculpt; + + /* Create and configure the Expand Cache. */ + ss->expand_cache = MEM_callocN(sizeof(ExpandCache), "expand cache"); + sculpt_expand_cache_initial_config_set(C, op, ss->expand_cache); + + /* Update object. */ + const bool needs_colors = ss->expand_cache->target == SCULPT_EXPAND_TARGET_COLORS; + + if (needs_colors) { + /* CTX_data_ensure_evaluated_depsgraph should be used at the end to include the updates of + * earlier steps modifying the data. */ + BKE_sculpt_color_layer_create_if_needed(ob); + depsgraph = CTX_data_ensure_evaluated_depsgraph(C); + } + + BKE_sculpt_update_object_for_edit(depsgraph, ob, true, true, needs_colors); + + /* Do nothing when the mesh has 0 vertices. */ + const int totvert = SCULPT_vertex_count_get(ss); + if (totvert == 0) { + sculpt_expand_cache_free(ss); + return OPERATOR_CANCELLED; + } + + /* Face Set operations are not supported in dyntopo. */ + if (ss->expand_cache->target == SCULPT_EXPAND_TARGET_FACE_SETS && + BKE_pbvh_type(ss->pbvh) == PBVH_BMESH) { + sculpt_expand_cache_free(ss); + return OPERATOR_CANCELLED; + } + + sculpt_expand_ensure_sculptsession_data(ob); + + /* Initialize undo. */ + SCULPT_undo_push_begin(ob, "expand"); + sculpt_expand_undo_push(ob, ss->expand_cache); + + /* Set the initial element for expand from the event position. */ + const float mouse[2] = {event->mval[0], event->mval[1]}; + sculpt_expand_set_initial_components_for_mouse(C, ob, ss->expand_cache, mouse); + + /* Cache PBVH nodes. */ + BKE_pbvh_search_gather( + ss->pbvh, NULL, NULL, &ss->expand_cache->nodes, &ss->expand_cache->totnode); + + /* Store initial state. */ + sculpt_expand_original_state_store(ob, ss->expand_cache); + + if (ss->expand_cache->modify_active_face_set) { + sculpt_expand_delete_face_set_id(ss->expand_cache->initial_face_sets, + ob->data, + ss->pmap, + ss->totfaces, + ss->expand_cache->next_face_set); + } + + /* Initialize the falloff. */ + eSculptExpandFalloffType falloff_type = RNA_enum_get(op->ptr, "falloff_type"); + + /* When starting from a boundary vertex, set the initial falloff to boundary. */ + if (SCULPT_vertex_is_boundary(ss, ss->expand_cache->initial_active_vertex)) { + falloff_type = SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; + } + + sculpt_expand_falloff_factors_from_vertex_and_symm_create( + ss->expand_cache, sd, ob, ss->expand_cache->initial_active_vertex, falloff_type); + + /* Initial mesh data update, resets all target data in the sculpt mesh. */ + sculpt_expand_update_for_vertex(C, ob, ss->expand_cache->initial_active_vertex); + + WM_event_add_modal_handler(C, op); + return OPERATOR_RUNNING_MODAL; +} + +void sculpt_expand_modal_keymap(wmKeyConfig *keyconf) +{ + static const EnumPropertyItem modal_items[] = { + {SCULPT_EXPAND_MODAL_CONFIRM, "CONFIRM", 0, "Confirm", ""}, + {SCULPT_EXPAND_MODAL_CANCEL, "CANCEL", 0, "Cancel", ""}, + {SCULPT_EXPAND_MODAL_INVERT, "INVERT", 0, "Invert", ""}, + {SCULPT_EXPAND_MODAL_PRESERVE_TOGGLE, "PRESERVE", 0, "Toggle Preserve State", ""}, + {SCULPT_EXPAND_MODAL_GRADIENT_TOGGLE, "GRADIENT", 0, "Toggle Gradient", ""}, + {SCULPT_EXPAND_MODAL_RECURSION_STEP_GEODESIC, + "RECURSION_STEP_GEODESIC", + 0, + "Geodesic recursion step", + ""}, + {SCULPT_EXPAND_MODAL_RECURSION_STEP_TOPOLOGY, + "RECURSION_STEP_TOPOLOGY", + 0, + "Topology recursion Step", + ""}, + {SCULPT_EXPAND_MODAL_MOVE_TOGGLE, "MOVE_TOGGLE", 0, "Move Origin", ""}, + {SCULPT_EXPAND_MODAL_FALLOFF_GEODESIC, "FALLOFF_GEODESICS", 0, "Geodesic Falloff", ""}, + {SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY, "FALLOFF_TOPOLOGY", 0, "Topology Falloff", ""}, + {SCULPT_EXPAND_MODAL_FALLOFF_TOPOLOGY_DIAGONALS, + "FALLOFF_TOPOLOGY_DIAGONALS", + 0, + "Diagonals Falloff", + ""}, + {SCULPT_EXPAND_MODAL_FALLOFF_SPHERICAL, "FALLOFF_SPHERICAL", 0, "Spherical Falloff", ""}, + {SCULPT_EXPAND_MODAL_SNAP_TOGGLE, "SNAP_TOGGLE", 0, "Snap expand to Face Sets", ""}, + {SCULPT_EXPAND_MODAL_LOOP_COUNT_INCREASE, + "LOOP_COUNT_INCREASE", + 0, + "Loop Count Increase", + ""}, + {SCULPT_EXPAND_MODAL_LOOP_COUNT_DECREASE, + "LOOP_COUNT_DECREASE", + 0, + "Loop Count Decrease", + ""}, + {SCULPT_EXPAND_MODAL_BRUSH_GRADIENT_TOGGLE, + "BRUSH_GRADIENT_TOGGLE", + 0, + "Toggle Brush Gradient", + ""}, + {SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_INCREASE, + "TEXTURE_DISTORTION_INCREASE", + 0, + "Texture Distortion Increase", + ""}, + {SCULPT_EXPAND_MODAL_TEXTURE_DISTORTION_DECREASE, + "TEXTURE_DISTORTION_DECREASE", + 0, + "Texture Distortion Decrease", + ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static const char *name = "Sculpt Expand Modal"; + wmKeyMap *keymap = WM_modalkeymap_find(keyconf, name); + + /* This function is called for each spacetype, only needs to add map once. */ + if (keymap && keymap->modal_items) { + return; + } + + keymap = WM_modalkeymap_ensure(keyconf, name, modal_items); + WM_modalkeymap_assign(keymap, "SCULPT_OT_expand"); +} + +void SCULPT_OT_expand(wmOperatorType *ot) +{ + /* Identifiers. */ + ot->name = "Expand"; + ot->idname = "SCULPT_OT_expand"; + ot->description = "Generic sculpt expand operator"; + + /* API callbacks. */ + ot->invoke = sculpt_expand_invoke; + ot->modal = sculpt_expand_modal; + ot->cancel = sculpt_expand_cancel; + ot->poll = SCULPT_mode_poll; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + static EnumPropertyItem prop_sculpt_expand_falloff_type_items[] = { + {SCULPT_EXPAND_FALLOFF_GEODESIC, "GEODESIC", 0, "Geodesic", ""}, + {SCULPT_EXPAND_FALLOFF_TOPOLOGY, "TOPOLOGY", 0, "Topology", ""}, + {SCULPT_EXPAND_FALLOFF_TOPOLOGY_DIAGONALS, + "TOPOLOGY_DIAGONALS", + 0, + "Topology Diagonals", + ""}, + {SCULPT_EXPAND_FALLOFF_NORMALS, "NORMALS", 0, "Normals", ""}, + {SCULPT_EXPAND_FALLOFF_SPHERICAL, "SPHERICAL", 0, "Spherical", ""}, + {SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY, "BOUNDARY_TOPOLOGY", 0, "Boundary Topology", ""}, + {SCULPT_EXPAND_FALLOFF_BOUNDARY_FACE_SET, "BOUNDARY_FACE_SET", 0, "Boundary Face Set", ""}, + {SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET, "ACTIVE_FACE_SET", 0, "Active Face Set", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + static EnumPropertyItem prop_sculpt_expand_target_type_items[] = { + {SCULPT_EXPAND_TARGET_MASK, "MASK", 0, "Mask", ""}, + {SCULPT_EXPAND_TARGET_FACE_SETS, "FACE_SETS", 0, "Face Sets", ""}, + {SCULPT_EXPAND_TARGET_COLORS, "COLOR", 0, "Color", ""}, + {0, NULL, 0, NULL, NULL}, + }; + + RNA_def_enum(ot->srna, + "target", + prop_sculpt_expand_target_type_items, + SCULPT_EXPAND_TARGET_MASK, + "Data Target", + "Data that is going to be modified in the expand operation"); + + RNA_def_enum(ot->srna, + "falloff_type", + prop_sculpt_expand_falloff_type_items, + SCULPT_EXPAND_FALLOFF_GEODESIC, + "Falloff Type", + "Initial falloff of the expand operation"); + + ot->prop = RNA_def_boolean( + ot->srna, "invert", false, "Invert", "Invert the expand active elements"); + ot->prop = RNA_def_boolean(ot->srna, + "use_mask_preserve", + false, + "Preserve Previous", + "Preserve the previous state of the target data"); + ot->prop = RNA_def_boolean(ot->srna, + "use_falloff_gradient", + false, + "Falloff Gradient", + "Expand Using a linear falloff"); + + ot->prop = RNA_def_boolean(ot->srna, + "use_modify_active", + false, + "Modify Active", + "Modify the active Face Set instead of creating a new one"); + + ot->prop = RNA_def_boolean( + ot->srna, + "use_reposition_pivot", + true, + "Reposition Pivot", + "Reposition the sculpt transform pivot to the boundary of the expand active area"); + + ot->prop = RNA_def_int(ot->srna, + "max_geodesic_move_preview", + 10000, + 0, + INT_MAX, + "Max Vertex Count for Geodesic Move Preview", + "Maximum number of vertices in the mesh for using geodesic falloff when " + "moving the origin of expand. If the total number of vertices is greater " + "than this value, the falloff will be set to spherical when moving", + 0, + 1000000); +} diff --git a/source/blender/editors/sculpt_paint/sculpt_geodesic.c b/source/blender/editors/sculpt_paint/sculpt_geodesic.c new file mode 100644 index 00000000000..d86d0938300 --- /dev/null +++ b/source/blender/editors/sculpt_paint/sculpt_geodesic.c @@ -0,0 +1,360 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation. + * All rights reserved. + */ + +/** \file + * \ingroup edsculpt + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_linklist_stack.h" +#include "BLI_math.h" +#include "BLI_task.h" + +#include "BLT_translation.h" + +#include "DNA_brush_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" + +#include "BKE_brush.h" +#include "BKE_ccg.h" +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_mesh.h" +#include "BKE_mesh_mapping.h" +#include "BKE_multires.h" +#include "BKE_node.h" +#include "BKE_object.h" +#include "BKE_paint.h" +#include "BKE_pbvh.h" +#include "BKE_scene.h" +#include "BKE_subdiv_ccg.h" + +#include "DEG_depsgraph.h" + +#include "WM_api.h" +#include "WM_message.h" +#include "WM_toolsystem.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "ED_object.h" +#include "ED_screen.h" +#include "ED_sculpt.h" +#include "ED_view3d.h" +#include "paint_intern.h" +#include "sculpt_intern.h" + +#include "IMB_colormanagement.h" +#include "IMB_imbuf.h" + +#include "bmesh.h" + +#include +#include +#define SCULPT_GEODESIC_VERTEX_NONE -1 + +/* Propagate distance from v1 and v2 to v0. */ +static bool sculpt_geodesic_mesh_test_dist_add( + MVert *mvert, const int v0, const int v1, const int v2, float *dists, GSet *initial_vertices) +{ + if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(v0))) { + return false; + } + + BLI_assert(dists[v1] != FLT_MAX); + if (dists[v0] <= dists[v1]) { + return false; + } + + float dist0; + if (v2 != SCULPT_GEODESIC_VERTEX_NONE) { + BLI_assert(dists[v2] != FLT_MAX); + if (dists[v0] <= dists[v2]) { + return false; + } + dist0 = geodesic_distance_propagate_across_triangle( + mvert[v0].co, mvert[v1].co, mvert[v2].co, dists[v1], dists[v2]); + } + else { + float vec[3]; + sub_v3_v3v3(vec, mvert[v1].co, mvert[v0].co); + dist0 = dists[v1] + len_v3(vec); + } + + if (dist0 < dists[v0]) { + dists[v0] = dist0; + return true; + } + + return false; +} + +static float *SCULPT_geodesic_mesh_create(Object *ob, + GSet *initial_vertices, + const float limit_radius) +{ + SculptSession *ss = ob->sculpt; + Mesh *mesh = BKE_object_get_original_mesh(ob); + + const int totvert = mesh->totvert; + const int totedge = mesh->totedge; + + const float limit_radius_sq = limit_radius * limit_radius; + + MEdge *edges = mesh->medge; + MVert *verts = SCULPT_mesh_deformed_mverts_get(ss); + + float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances"); + BLI_bitmap *edge_tag = BLI_BITMAP_NEW(totedge, "edge tag"); + + if (!ss->epmap) { + BKE_mesh_edge_poly_map_create(&ss->epmap, + &ss->epmap_mem, + mesh->medge, + mesh->totedge, + mesh->mpoly, + mesh->totpoly, + mesh->mloop, + mesh->totloop); + } + if (!ss->vemap) { + BKE_mesh_vert_edge_map_create( + &ss->vemap, &ss->vemap_mem, mesh->medge, mesh->totvert, mesh->totedge); + } + + /* Both contain edge indices encoded as *void. */ + BLI_LINKSTACK_DECLARE(queue, void *); + BLI_LINKSTACK_DECLARE(queue_next, void *); + + BLI_LINKSTACK_INIT(queue); + BLI_LINKSTACK_INIT(queue_next); + + for (int i = 0; i < totvert; i++) { + if (BLI_gset_haskey(initial_vertices, POINTER_FROM_INT(i))) { + dists[i] = 0.0f; + } + else { + dists[i] = FLT_MAX; + } + } + + /* Masks vertices that are further than limit radius from an initial vertex. As there is no need + * to define a distance to them the algorithm can stop earlier by skipping them. */ + BLI_bitmap *affected_vertex = BLI_BITMAP_NEW(totvert, "affected vertex"); + GSetIterator gs_iter; + + if (limit_radius == FLT_MAX) { + /* In this case, no need to loop through all initial vertices to check distances as they are + * all going to be affected. */ + BLI_bitmap_set_all(affected_vertex, true, totvert); + } + else { + /* This is an O(n^2) loop used to limit the geodesic distance calculation to a radius. When + * this optimization is needed, it is expected for the tool to request the distance to a low + * number of vertices (usually just 1 or 2). */ + GSET_ITER (gs_iter, initial_vertices) { + const int v = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); + float *v_co = verts[v].co; + for (int i = 0; i < totvert; i++) { + if (len_squared_v3v3(v_co, verts[i].co) <= limit_radius_sq) { + BLI_BITMAP_ENABLE(affected_vertex, i); + } + } + } + } + + /* Add edges adjacent to an initial vertex to the queue. */ + for (int i = 0; i < totedge; i++) { + const int v1 = edges[i].v1; + const int v2 = edges[i].v2; + if (!BLI_BITMAP_TEST(affected_vertex, v1) && !BLI_BITMAP_TEST(affected_vertex, v2)) { + continue; + } + if (dists[v1] != FLT_MAX || dists[v2] != FLT_MAX) { + BLI_LINKSTACK_PUSH(queue, POINTER_FROM_INT(i)); + } + } + + do { + while (BLI_LINKSTACK_SIZE(queue)) { + const int e = POINTER_AS_INT(BLI_LINKSTACK_POP(queue)); + int v1 = edges[e].v1; + int v2 = edges[e].v2; + + if (dists[v1] == FLT_MAX || dists[v2] == FLT_MAX) { + if (dists[v1] > dists[v2]) { + SWAP(int, v1, v2); + } + sculpt_geodesic_mesh_test_dist_add( + verts, v2, v1, SCULPT_GEODESIC_VERTEX_NONE, dists, initial_vertices); + } + + if (ss->epmap[e].count != 0) { + for (int poly_map_index = 0; poly_map_index < ss->epmap[e].count; poly_map_index++) { + const int poly = ss->epmap[e].indices[poly_map_index]; + if (ss->face_sets[poly] <= 0) { + continue; + } + const MPoly *mpoly = &mesh->mpoly[poly]; + + for (int loop_index = 0; loop_index < mpoly->totloop; loop_index++) { + const MLoop *mloop = &mesh->mloop[loop_index + mpoly->loopstart]; + const int v_other = mloop->v; + if (ELEM(v_other, v1, v2)) { + continue; + } + if (sculpt_geodesic_mesh_test_dist_add( + verts, v_other, v1, v2, dists, initial_vertices)) { + for (int edge_map_index = 0; edge_map_index < ss->vemap[v_other].count; + edge_map_index++) { + const int e_other = ss->vemap[v_other].indices[edge_map_index]; + int ev_other; + if (edges[e_other].v1 == (uint)v_other) { + ev_other = edges[e_other].v2; + } + else { + ev_other = edges[e_other].v1; + } + + if (e_other != e && !BLI_BITMAP_TEST(edge_tag, e_other) && + (ss->epmap[e_other].count == 0 || dists[ev_other] != FLT_MAX)) { + if (BLI_BITMAP_TEST(affected_vertex, v_other) || + BLI_BITMAP_TEST(affected_vertex, ev_other)) { + BLI_BITMAP_ENABLE(edge_tag, e_other); + BLI_LINKSTACK_PUSH(queue_next, POINTER_FROM_INT(e_other)); + } + } + } + } + } + } + } + } + + for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) { + const int e = POINTER_AS_INT(lnk->link); + BLI_BITMAP_DISABLE(edge_tag, e); + } + + BLI_LINKSTACK_SWAP(queue, queue_next); + + } while (BLI_LINKSTACK_SIZE(queue)); + + BLI_LINKSTACK_FREE(queue); + BLI_LINKSTACK_FREE(queue_next); + MEM_SAFE_FREE(edge_tag); + MEM_SAFE_FREE(affected_vertex); + + return dists; +} + +/* For sculpt mesh data that does not support a geodesic distances algorithm, fallback to the + * distance to each vertex. In this case, only one of the initial vertices will be used to + * calculate the distance. */ +static float *SCULPT_geodesic_fallback_create(Object *ob, GSet *initial_vertices) +{ + + SculptSession *ss = ob->sculpt; + Mesh *mesh = BKE_object_get_original_mesh(ob); + const int totvert = mesh->totvert; + float *dists = MEM_malloc_arrayN(totvert, sizeof(float), "distances"); + int first_affected = SCULPT_GEODESIC_VERTEX_NONE; + GSetIterator gs_iter; + GSET_ITER (gs_iter, initial_vertices) { + first_affected = POINTER_AS_INT(BLI_gsetIterator_getKey(&gs_iter)); + break; + } + + if (first_affected == SCULPT_GEODESIC_VERTEX_NONE) { + for (int i = 0; i < totvert; i++) { + dists[i] = FLT_MAX; + } + return dists; + } + + const float *first_affected_co = SCULPT_vertex_co_get(ss, first_affected); + for (int i = 0; i < totvert; i++) { + dists[i] = len_v3v3(first_affected_co, SCULPT_vertex_co_get(ss, i)); + } + + return dists; +} + +float *SCULPT_geodesic_distances_create(Object *ob, + GSet *initial_vertices, + const float limit_radius) +{ + SculptSession *ss = ob->sculpt; + switch (BKE_pbvh_type(ss->pbvh)) { + case PBVH_FACES: + return SCULPT_geodesic_mesh_create(ob, initial_vertices, limit_radius); + case PBVH_BMESH: + case PBVH_GRIDS: + return SCULPT_geodesic_fallback_create(ob, initial_vertices); + } + BLI_assert(false); + return NULL; +} + +float *SCULPT_geodesic_from_vertex_and_symm(Sculpt *sd, + Object *ob, + const int vertex, + const float limit_radius) +{ + SculptSession *ss = ob->sculpt; + GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); + + const char symm = SCULPT_mesh_symmetry_xyz_get(ob); + for (char i = 0; i <= symm; ++i) { + if (SCULPT_is_symmetry_iteration_valid(i, symm)) { + int v = -1; + if (i == 0) { + v = vertex; + } + else { + float location[3]; + flip_v3_v3(location, SCULPT_vertex_co_get(ss, vertex), i); + v = SCULPT_nearest_vertex_get(sd, ob, location, FLT_MAX, false); + } + if (v != -1) { + BLI_gset_add(initial_vertices, POINTER_FROM_INT(v)); + } + } + } + + float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); + BLI_gset_free(initial_vertices, NULL); + return dists; +} + +float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius) +{ + GSet *initial_vertices = BLI_gset_int_new("initial_vertices"); + BLI_gset_add(initial_vertices, POINTER_FROM_INT(vertex)); + float *dists = SCULPT_geodesic_distances_create(ob, initial_vertices, limit_radius); + BLI_gset_free(initial_vertices, NULL); + return dists; +} diff --git a/source/blender/editors/sculpt_paint/sculpt_intern.h b/source/blender/editors/sculpt_paint/sculpt_intern.h index f90cf366ed9..19c4eda7593 100644 --- a/source/blender/editors/sculpt_paint/sculpt_intern.h +++ b/source/blender/editors/sculpt_paint/sculpt_intern.h @@ -187,6 +187,8 @@ void SCULPT_boundary_info_ensure(Object *object); /* Boundary Info needs to be initialized in order to use this function. */ bool SCULPT_vertex_is_boundary(const SculptSession *ss, const int index); +void SCULPT_connected_components_ensure(Object *ob); + /* Sculpt Visibility API */ void SCULPT_vertex_visible_set(SculptSession *ss, int index, bool visible); @@ -300,6 +302,7 @@ void SCULPT_floodfill_add_initial_with_symmetry(struct Sculpt *sd, int index, float radius); void SCULPT_floodfill_add_initial(SculptFloodFill *flood, int index); +void SCULPT_floodfill_add_and_skip_initial(SculptFloodFill *flood, int index); void SCULPT_floodfill_execute( struct SculptSession *ss, SculptFloodFill *flood, @@ -361,6 +364,21 @@ float *SCULPT_boundary_automasking_init(Object *ob, int propagation_steps, float *automask_factor); +/* Geodesic distances. */ + +/* Returns an array indexed by vertex index containing the geodesic distance to the closest vertex +in the initial vertex set. The caller is responsible for freeing the array. +Geodesic distances will only work when used with PBVH_FACES, for other types of PBVH it will +fallback to euclidean distances to one of the initial vertices in the set. */ +float *SCULPT_geodesic_distances_create(struct Object *ob, + struct GSet *initial_vertices, + const float limit_radius); +float *SCULPT_geodesic_from_vertex_and_symm(struct Sculpt *sd, + struct Object *ob, + const int vertex, + const float limit_radius); +float *SCULPT_geodesic_from_vertex(Object *ob, const int vertex, const float limit_radius); + /* Filters. */ void SCULPT_filter_cache_init(struct bContext *C, Object *ob, Sculpt *sd, const int undo_type); void SCULPT_filter_cache_free(SculptSession *ss); @@ -1066,6 +1084,155 @@ void SCULPT_filter_to_orientation_space(float r_v[3], struct FilterCache *filter void SCULPT_filter_to_object_space(float r_v[3], struct FilterCache *filter_cache); void SCULPT_filter_zero_disabled_axis_components(float r_v[3], struct FilterCache *filter_cache); +/* Sculpt Expand. */ +typedef enum eSculptExpandFalloffType { + SCULPT_EXPAND_FALLOFF_GEODESIC, + SCULPT_EXPAND_FALLOFF_TOPOLOGY, + SCULPT_EXPAND_FALLOFF_TOPOLOGY_DIAGONALS, + SCULPT_EXPAND_FALLOFF_NORMALS, + SCULPT_EXPAND_FALLOFF_SPHERICAL, + SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY, + SCULPT_EXPAND_FALLOFF_BOUNDARY_FACE_SET, + SCULPT_EXPAND_FALLOFF_ACTIVE_FACE_SET, +} eSculptExpandFalloffType; + +typedef enum eSculptExpandTargetType { + SCULPT_EXPAND_TARGET_MASK, + SCULPT_EXPAND_TARGET_FACE_SETS, + SCULPT_EXPAND_TARGET_COLORS, +} eSculptExpandTargetType; + +typedef enum eSculptExpandRecursionType { + SCULPT_EXPAND_RECURSION_TOPOLOGY, + SCULPT_EXPAND_RECURSION_GEODESICS, +} eSculptExpandRecursionType; + +#define EXPAND_SYMM_AREAS 8 + +typedef struct ExpandCache { + /* Target data elements that the expand operation will affect. */ + eSculptExpandTargetType target; + + /* Falloff data. */ + eSculptExpandFalloffType falloff_type; + + /* Indexed by vertex index, precalculated falloff value of that vertex (without any falloff + * editing modification applied). */ + float *vert_falloff; + /* Max falloff value in *vert_falloff. */ + float max_vert_falloff; + + /* Indexed by base mesh poly index, precalculated falloff value of that face. These values are + * calculated from the per vertex falloff (*vert_falloff) when needed. */ + float *face_falloff; + float max_face_falloff; + + /* Falloff value of the active element (vertex or base mesh face) that Expand will expand to. */ + float active_falloff; + + /* When set to true, expand skips all falloff computations and considers all elements as enabled. + */ + bool all_enabled; + + /* Initial mouse and cursor data from where the current falloff started. This data can be changed + * during the execution of Expand by moving the origin. */ + float initial_mouse_move[2]; + float initial_mouse[2]; + int initial_active_vertex; + int initial_active_face_set; + + /* Maximum number of vertices allowed in the SculptSession for previewing the falloff using + * geodesic distances. */ + int max_geodesic_move_preview; + + /* Original falloff type before starting the move operation. */ + eSculptExpandFalloffType move_original_falloff_type; + /* Falloff type using when moving the origin for preview. */ + eSculptExpandFalloffType move_preview_falloff_type; + + /* Face set ID that is going to be used when creating a new Face Set. */ + int next_face_set; + + /* Face Set ID of the Face set selected for editing. */ + int update_face_set; + + /* Mouse position since the last time the origin was moved. Used for reference when moving the + * initial position of Expand. */ + float original_mouse_move[2]; + + /* Active components checks. */ + /* Indexed by symmetry pass index, contains the connected component ID found in + * SculptSession->vertex_info.connected_component. Other connected components not found in this + * array will be ignored by Expand. */ + int active_connected_components[EXPAND_SYMM_AREAS]; + + /* Snapping. */ + /* GSet containing all Face Sets IDs that Expand will use to snap the new data. */ + GSet *snap_enabled_face_sets; + + /* Texture distortion data. */ + Brush *brush; + struct Scene *scene; + struct MTex *mtex; + + /* Controls how much texture distortion will be applied to the current falloff */ + float texture_distortion_strength; + + /* Cached PBVH nodes. This allows to skip gathering all nodes from the PBVH each time expand + * needs to update the state of the elements. */ + PBVHNode **nodes; + int totnode; + + /* Expand state options. */ + + /* Number of loops (times that the falloff is going to be repeated). */ + int loop_count; + + /* Invert the falloff result. */ + bool invert; + + /* When set to true, preserves the previous state of the data and adds the new one on top. */ + bool preserve; + + /* When set to true, the mask or colors will be applied as a gradient. */ + bool falloff_gradient; + + /* When set to true, Expand will use the Brush falloff curve data to shape the gradient. */ + bool brush_gradient; + + /* When set to true, Expand will move the origin (initial active vertex and cursor position) + * instead of updating the active vertex and active falloff. */ + bool move; + + /* When set to true, Expand will snap the new data to the Face Sets IDs found in + * *original_face_sets. */ + bool snap; + + /* When set to true, Expand will use the current Face Set ID to modify an existing Face Set + * instead of creating a new one. */ + bool modify_active_face_set; + + /* When set to true, Expand will reposition the sculpt pivot to the boundary of the expand result + * after finishing the operation. */ + bool reposition_pivot; + + /* Color target data type related data. */ + float fill_color[4]; + short blend_mode; + + /* Face Sets at the first step of the expand operation, before starting modifying the active + * vertex and active falloff. These are not the original Face Sets of the sculpt before starting + * the operator as they could have been modified by Expand when initializing the operator and + * before starting changing the active vertex. These Face Sets are used for restoring and + * checking the Face Sets state while the Expand operation modal runs. */ + int *initial_face_sets; + + /* Original data of the sculpt as it was before running the Expand operator. */ + float *original_mask; + int *original_face_sets; + float (*original_colors)[4]; +} ExpandCache; + typedef struct FilterCache { bool enabled_axis[3]; bool enabled_force_axis[3]; @@ -1150,6 +1317,10 @@ bool SCULPT_get_redraw_rect(struct ARegion *region, /* Operators. */ +/* Expand. */ +void SCULPT_OT_expand(struct wmOperatorType *ot); +void sculpt_expand_modal_keymap(struct wmKeyConfig *keyconf); + /* Gestures. */ void SCULPT_OT_face_set_lasso_gesture(struct wmOperatorType *ot); void SCULPT_OT_face_set_box_gesture(struct wmOperatorType *ot); -- cgit v1.2.3 From 1a8aee0a7cec0c2718932fdeece9dc071689f928 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Mar 2021 11:42:05 -0600 Subject: UI: Expose an "is first search" boolean to search button callbacks Currently when you open an RNA collection search button, like a vertex group selector, the search filter isn't applied until you start typing, in order to display every option at the start. Otherwise they wouldn't be visible, since the search filter would run for the current text. Currently this check happens in one place, but it relies on the `changed` value of `uiBut`. This is fine in the interface directory, but anywhere else it would require exposing `uiBut.changed`, which is probably too low-level to expose. The solution is adding an `is_first` argument to the search callbacks, which is nice for a few reasons: - They work at a higher level of abstraction, meaning they don't have to worry about how exactly to tell if this is the first search. - It makes it easier to do special behavior when the search menu is first opened. - Then, obviously, it makes that state accessible without including `interface_intern.h`. Needed for attribute search: T85658 Differential Revision: https://developer.blender.org/D10528 --- source/blender/editors/include/UI_interface.h | 7 ++++++- source/blender/editors/interface/interface.c | 11 ++++++++++- source/blender/editors/interface/interface_handlers.c | 6 +++++- source/blender/editors/interface/interface_intern.h | 9 ++++++++- .../blender/editors/interface/interface_region_search.c | 17 ++++++++++------- .../editors/interface/interface_template_search_menu.c | 3 ++- .../interface/interface_template_search_operator.c | 3 ++- source/blender/editors/interface/interface_templates.c | 8 +++++--- source/blender/editors/interface/interface_utils.c | 5 +++-- source/blender/editors/space_node/node_select.c | 3 ++- source/blender/editors/space_outliner/outliner_tools.c | 3 ++- 11 files changed, 55 insertions(+), 20 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 81641239c6a..09e1a269ae1 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -499,10 +499,14 @@ typedef int (*uiButCompleteFunc)(struct bContext *C, char *str, void *arg); typedef struct ARegion *(*uiButSearchCreateFn)(struct bContext *C, struct ARegion *butregion, struct uiButSearch *search_but); +/* `is_first` is typically used to ignore search filtering when the menu is first opened in order + * to display the full list of options. The value will be false after the button's text is edited + * (for every call except the first). */ typedef void (*uiButSearchUpdateFn)(const struct bContext *C, void *arg, const char *str, - uiSearchItems *items); + uiSearchItems *items, + const bool is_first); typedef void (*uiButSearchArgFreeFn)(void *arg); typedef bool (*uiButSearchContextMenuFn)(struct bContext *C, void *arg, @@ -1602,6 +1606,7 @@ void UI_but_func_search_set(uiBut *but, void UI_but_func_search_set_context_menu(uiBut *but, uiButSearchContextMenuFn context_menu_fn); void UI_but_func_search_set_tooltip(uiBut *but, uiButSearchTooltipFn tooltip_fn); void UI_but_func_search_set_sep_string(uiBut *but, const char *search_sep_string); +void UI_but_func_search_set_all_strings_valid(uiBut *but, const bool value); /* height in pixels, it's using hardcoded values still */ int UI_searchbox_size_y(void); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index 6e25ec9d275..ed2984e1a86 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -6661,11 +6661,20 @@ void UI_but_func_search_set_tooltip(uiBut *but, uiButSearchTooltipFn tooltip_fn) but_search->item_tooltip_fn = tooltip_fn; } +void UI_but_func_search_set_all_strings_valid(uiBut *but, const bool value) +{ + uiButSearch *but_search = (uiButSearch *)but; + BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); + + but_search->all_strings_valid = value; +} + /* Callbacks for operator search button. */ static void operator_enum_search_update_fn(const struct bContext *C, void *but, const char *str, - uiSearchItems *items) + uiSearchItems *items, + const bool UNUSED(is_first)) { wmOperatorType *ot = ((uiBut *)but)->optype; PropertyRNA *prop = ot->prop; diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 5de330d7136..40202250d9f 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3408,8 +3408,12 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) if (data->searchbox) { if (data->cancel == false) { + BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); + uiButSearch *but_search = (uiButSearch *)but; + if ((ui_searchbox_apply(but, data->searchbox) == false) && - (ui_searchbox_find_index(data->searchbox, but->editstr) == -1)) { + (ui_searchbox_find_index(data->searchbox, but->editstr) == -1) && + !but_search->all_strings_valid) { data->cancel = true; /* ensure menu (popup) too is closed! */ diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 7e931eae749..9d0184ce487 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -320,6 +320,12 @@ typedef struct uiButSearch { struct PointerRNA rnasearchpoin; struct PropertyRNA *rnasearchprop; + + /** + * The search box only provides suggestions, it does not force + * the string to match one of the search items when applying. + */ + bool all_strings_valid; } uiButSearch; /** Derived struct for #UI_BTYPE_DECORATOR */ @@ -1197,7 +1203,8 @@ typedef struct uiRNACollectionSearch { void ui_rna_collection_search_update_fn(const struct bContext *C, void *arg, const char *str, - uiSearchItems *items); + uiSearchItems *items, + const bool is_first); /* interface_ops.c */ bool ui_jump_to_target_button_poll(struct bContext *C); diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 2c07f5c3c03..9297da5307a 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -468,7 +468,8 @@ static void ui_searchbox_update_fn(bContext *C, wmWindow *win = CTX_wm_window(C); WM_tooltip_clear(C, win); } - search_but->items_update_fn(C, search_but->arg, str, items); + const bool is_first_search = !search_but->but.changed; + search_but->items_update_fn(C, search_but->arg, str, items, is_first_search); } /* region is the search box itself */ @@ -1052,14 +1053,16 @@ void ui_but_search_refresh(uiButSearch *search_but) ui_searchbox_update_fn(but->block->evil_C, search_but, but->drawstr, items); - /* Only red-alert when we are sure of it, this can miss cases when >10 matches. */ - if (items->totitem == 0) { - UI_but_flag_enable(but, UI_BUT_REDALERT); - } - else if (items->more == 0) { - if (UI_search_items_find_index(items, but->drawstr) == -1) { + if (!search_but->all_strings_valid) { + /* Only red-alert when we are sure of it, this can miss cases when >10 matches. */ + if (items->totitem == 0) { UI_but_flag_enable(but, UI_BUT_REDALERT); } + else if (items->more == 0) { + if (UI_search_items_find_index(items, but->drawstr) == -1) { + UI_but_flag_enable(but, UI_BUT_REDALERT); + } + } } for (x1 = 0; x1 < items->maxitem; x1++) { diff --git a/source/blender/editors/interface/interface_template_search_menu.c b/source/blender/editors/interface/interface_template_search_menu.c index 25cf2e12377..e1f8f63dcbf 100644 --- a/source/blender/editors/interface/interface_template_search_menu.c +++ b/source/blender/editors/interface/interface_template_search_menu.c @@ -990,7 +990,8 @@ static void menu_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) static void menu_search_update_fn(const bContext *UNUSED(C), void *arg, const char *str, - uiSearchItems *items) + uiSearchItems *items, + const bool UNUSED(is_first)) { struct MenuSearch_Data *data = arg; diff --git a/source/blender/editors/interface/interface_template_search_operator.c b/source/blender/editors/interface/interface_template_search_operator.c index ff0f9a2e5cd..2c83f184ff0 100644 --- a/source/blender/editors/interface/interface_template_search_operator.c +++ b/source/blender/editors/interface/interface_template_search_operator.c @@ -59,7 +59,8 @@ static void operator_search_exec_fn(bContext *C, void *UNUSED(arg1), void *arg2) static void operator_search_update_fn(const bContext *C, void *UNUSED(arg), const char *str, - uiSearchItems *items) + uiSearchItems *items, + const bool UNUSED(is_first)) { GHashIterator iter; diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index 67446ca681f..eadbe618899 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -393,7 +393,8 @@ static bool id_search_add(const bContext *C, TemplateID *template_ui, uiSearchIt static void id_search_cb(const bContext *C, void *arg_template, const char *str, - uiSearchItems *items) + uiSearchItems *items, + const bool UNUSED(is_first)) { TemplateID *template_ui = (TemplateID *)arg_template; ListBase *lb = template_ui->idlb; @@ -464,7 +465,8 @@ static void id_search_cb_tagged(const bContext *C, static void id_search_cb_objects_from_scene(const bContext *C, void *arg_template, const char *str, - uiSearchItems *items) + uiSearchItems *items, + const bool UNUSED(is_first)) { TemplateID *template_ui = (TemplateID *)arg_template; ListBase *lb = template_ui->idlb; @@ -518,7 +520,7 @@ static uiBlock *id_search_menu(bContext *C, ARegion *region, void *arg_litem) static TemplateID template_ui; PointerRNA active_item_ptr; void (*id_search_update_fn)( - const bContext *, void *, const char *, uiSearchItems *) = id_search_cb; + const bContext *, void *, const char *, uiSearchItems *, const bool) = id_search_cb; /* arg_litem is malloced, can be freed by parent button */ template_ui = *((TemplateID *)arg_litem); diff --git a/source/blender/editors/interface/interface_utils.c b/source/blender/editors/interface/interface_utils.c index 5311bb57da9..877800c1ba2 100644 --- a/source/blender/editors/interface/interface_utils.c +++ b/source/blender/editors/interface/interface_utils.c @@ -405,7 +405,8 @@ static bool add_collection_search_item(CollItemSearch *cis, void ui_rna_collection_search_update_fn(const struct bContext *C, void *arg, const char *str, - uiSearchItems *items) + uiSearchItems *items, + const bool is_first) { uiRNACollectionSearch *data = arg; const int flag = RNA_property_flag(data->target_prop); @@ -415,7 +416,7 @@ void ui_rna_collection_search_update_fn(const struct bContext *C, * match the RNA name exactly. So only for pointer properties, the name can be modified to add * further UI hints. */ const bool requires_exact_data_name = !is_ptr_target; - const bool skip_filter = data->search_but && !data->search_but->changed; + const bool skip_filter = is_first; char name_buf[UI_MAX_DRAW_STR]; char *name; bool has_id_icon = false; diff --git a/source/blender/editors/space_node/node_select.c b/source/blender/editors/space_node/node_select.c index 58d22c2864f..704b7350bb9 100644 --- a/source/blender/editors/space_node/node_select.c +++ b/source/blender/editors/space_node/node_select.c @@ -1178,7 +1178,8 @@ static void node_find_create_label(const bNode *node, char *str, int maxlen) static void node_find_update_fn(const struct bContext *C, void *UNUSED(arg), const char *str, - uiSearchItems *items) + uiSearchItems *items, + const bool UNUSED(is_first)) { SpaceNode *snode = CTX_wm_space_node(C); diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index 8726fd768d4..b735064cfef 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -554,7 +554,8 @@ static void merged_element_search_fn_recursive( static void merged_element_search_update_fn(const bContext *UNUSED(C), void *data, const char *str, - uiSearchItems *items) + uiSearchItems *items, + const bool UNUSED(is_first)) { MergedSearchData *search_data = (MergedSearchData *)data; TreeElement *parent = search_data->parent_element; -- cgit v1.2.3 From 6471cf6bc8153eefc2cafe049033e5f3d0952a8a Mon Sep 17 00:00:00 2001 From: William Reynish Date: Tue, 2 Mar 2021 11:58:25 -0600 Subject: UI: Clean up labels and descriptions: "Draw" to "Display" In Blender, we used to use the term 'draw' to refer to information displayed to the user. For version 2.80, it was decided to change these instances to 'display' instead. This was to avoid the ambiguity between end-user drawing tools and display options. From the Oxford English Dictionary: - Draw: produce (a picture or diagram) by making lines and marks on paper with a pencil, pen, etc. - Display: show (data or an image) on a computer, television, or other screen. Therefore, we should use draw when referring to drawing tools for making marks, but use display when referring to information shown/displayed to the user. From a user POV, the computer displays certain information, whereas the user draws a mark. Apparently this change was not implemented consistently, so this patch changes all remaining relevant instances of "draw". Differential Revision: https://developer.blender.org/D10551 --- source/blender/editors/gpencil/gpencil_data.c | 2 +- source/blender/editors/space_outliner/outliner_collections.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index c76c2e55d2b..039bc50fcc9 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -1676,7 +1676,7 @@ void GPENCIL_OT_stroke_arrange(wmOperatorType *ot) /* identifiers */ ot->name = "Arrange Stroke"; ot->idname = "GPENCIL_OT_stroke_arrange"; - ot->description = "Arrange selected strokes up/down in the drawing order of the active layer"; + ot->description = "Arrange selected strokes up/down in the display order of the active layer"; /* callbacks */ ot->exec = gpencil_stroke_arrange_exec; diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index 0afc26e0d8a..ef5733fe375 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -1367,7 +1367,7 @@ void OUTLINER_OT_collection_enable(wmOperatorType *ot) /* identifiers */ ot->name = "Enable Collection"; ot->idname = "OUTLINER_OT_collection_enable"; - ot->description = "Enable viewport drawing in the view layers"; + ot->description = "Enable viewport display in the view layers"; /* api callbacks */ ot->exec = collection_flag_exec; @@ -1382,7 +1382,7 @@ void OUTLINER_OT_collection_disable(wmOperatorType *ot) /* identifiers */ ot->name = "Disable Collection"; ot->idname = "OUTLINER_OT_collection_disable"; - ot->description = "Disable viewport drawing in the view layers"; + ot->description = "Disable viewport display in the view layers"; /* api callbacks */ ot->exec = collection_flag_exec; -- cgit v1.2.3 From a344f203467940cbb8fe6dde001fba5c4517619d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Mar 2021 12:43:27 -0600 Subject: UI: Rename search button variable I landed D10527 in rB1a8aee0a7cec accidentally, and the version there was missing a name change discussed in review. This commit just renames the boolean variable controlling the special behavior for attribute search. Original message meant for this change: For geometry nodes we will use search buttons to display a list of attributes available the last time the node tree was executed (D10519). Because this list is just a hint, we need to be able to enter any string, not just strings from the search items. This patch adds a boolean option to string buttons to enable this. The change is quite simple, changes to behavior are only required in two places. The type-specific button struct changes help a lot here. Differential Revision: https://developer.blender.org/D10527 --- source/blender/editors/include/UI_interface.h | 2 +- source/blender/editors/interface/interface.c | 4 ++-- source/blender/editors/interface/interface_handlers.c | 2 +- source/blender/editors/interface/interface_intern.h | 2 +- source/blender/editors/interface/interface_region_search.c | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/UI_interface.h b/source/blender/editors/include/UI_interface.h index 09e1a269ae1..5620d39ab16 100644 --- a/source/blender/editors/include/UI_interface.h +++ b/source/blender/editors/include/UI_interface.h @@ -1606,7 +1606,7 @@ void UI_but_func_search_set(uiBut *but, void UI_but_func_search_set_context_menu(uiBut *but, uiButSearchContextMenuFn context_menu_fn); void UI_but_func_search_set_tooltip(uiBut *but, uiButSearchTooltipFn tooltip_fn); void UI_but_func_search_set_sep_string(uiBut *but, const char *search_sep_string); -void UI_but_func_search_set_all_strings_valid(uiBut *but, const bool value); +void UI_but_func_search_set_results_are_suggestions(uiBut *but, const bool value); /* height in pixels, it's using hardcoded values still */ int UI_searchbox_size_y(void); diff --git a/source/blender/editors/interface/interface.c b/source/blender/editors/interface/interface.c index ed2984e1a86..c7f5385eac3 100644 --- a/source/blender/editors/interface/interface.c +++ b/source/blender/editors/interface/interface.c @@ -6661,12 +6661,12 @@ void UI_but_func_search_set_tooltip(uiBut *but, uiButSearchTooltipFn tooltip_fn) but_search->item_tooltip_fn = tooltip_fn; } -void UI_but_func_search_set_all_strings_valid(uiBut *but, const bool value) +void UI_but_func_search_set_results_are_suggestions(uiBut *but, const bool value) { uiButSearch *but_search = (uiButSearch *)but; BLI_assert(but->type == UI_BTYPE_SEARCH_MENU); - but_search->all_strings_valid = value; + but_search->results_are_suggestions = value; } /* Callbacks for operator search button. */ diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 40202250d9f..75ee3300e63 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -3413,7 +3413,7 @@ static void ui_textedit_end(bContext *C, uiBut *but, uiHandleButtonData *data) if ((ui_searchbox_apply(but, data->searchbox) == false) && (ui_searchbox_find_index(data->searchbox, but->editstr) == -1) && - !but_search->all_strings_valid) { + !but_search->results_are_suggestions) { data->cancel = true; /* ensure menu (popup) too is closed! */ diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index 9d0184ce487..c39e2b3ff8a 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -325,7 +325,7 @@ typedef struct uiButSearch { * The search box only provides suggestions, it does not force * the string to match one of the search items when applying. */ - bool all_strings_valid; + bool results_are_suggestions; } uiButSearch; /** Derived struct for #UI_BTYPE_DECORATOR */ diff --git a/source/blender/editors/interface/interface_region_search.c b/source/blender/editors/interface/interface_region_search.c index 9297da5307a..12044863b8c 100644 --- a/source/blender/editors/interface/interface_region_search.c +++ b/source/blender/editors/interface/interface_region_search.c @@ -1053,7 +1053,7 @@ void ui_but_search_refresh(uiButSearch *search_but) ui_searchbox_update_fn(but->block->evil_C, search_but, but->drawstr, items); - if (!search_but->all_strings_valid) { + if (!search_but->results_are_suggestions) { /* Only red-alert when we are sure of it, this can miss cases when >10 matches. */ if (items->totitem == 0) { UI_but_flag_enable(but, UI_BUT_REDALERT); -- cgit v1.2.3 From 85421c4fab029ccb0747338715b95910e82229f9 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Mar 2021 13:01:33 -0600 Subject: Geometry Nodes: Attribute search drop-down This commit adds a search for existing attributes when you click on an attribute field. This is useful because otherwise you have to remember which attributes should be available at each node in the tree. The fundamental complication is that this information is not accessible statically. So the search data is only a cache from the previous node tree evaluation. The information is added with `BKE_nodetree_attribute_hint_add`, currently for every input geometry socket for a single node. This is only an initial implementation, and later versions will expose the data type and domain of the attributes. Differential Revision: https://developer.blender.org/D10519 --- source/blender/editors/space_node/CMakeLists.txt | 1 + source/blender/editors/space_node/drawnode.c | 10 +- .../space_node/node_geometry_attribute_search.cc | 151 +++++++++++++++++++++ source/blender/editors/space_node/node_intern.h | 8 ++ 4 files changed, 169 insertions(+), 1 deletion(-) create mode 100644 source/blender/editors/space_node/node_geometry_attribute_search.cc (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/CMakeLists.txt b/source/blender/editors/space_node/CMakeLists.txt index c640b076ba4..bc043a4e665 100644 --- a/source/blender/editors/space_node/CMakeLists.txt +++ b/source/blender/editors/space_node/CMakeLists.txt @@ -42,6 +42,7 @@ set(SRC node_buttons.c node_draw.cc node_edit.c + node_geometry_attribute_search.cc node_gizmo.c node_group.c node_ops.c diff --git a/source/blender/editors/space_node/drawnode.c b/source/blender/editors/space_node/drawnode.c index 82a1cd818c9..977c2053187 100644 --- a/source/blender/editors/space_node/drawnode.c +++ b/source/blender/editors/space_node/drawnode.c @@ -3389,7 +3389,15 @@ static void std_node_socket_draw( case SOCK_STRING: { uiLayout *row = uiLayoutSplit(layout, 0.5f, false); uiItemL(row, text, 0); - uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); + + const bNodeTree *node_tree = (const bNodeTree *)node_ptr->owner_id; + if (node_tree->type == NTREE_GEOMETRY) { + node_geometry_add_attribute_search_button(node_tree, node, ptr, row); + } + else { + uiItemR(row, ptr, "default_value", DEFAULT_FLAGS, "", 0); + } + break; } case SOCK_OBJECT: { diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc new file mode 100644 index 00000000000..41f04dad221 --- /dev/null +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -0,0 +1,151 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include "BLI_index_range.hh" +#include "BLI_listbase.h" +#include "BLI_map.hh" +#include "BLI_set.hh" +#include "BLI_string_ref.hh" +#include "BLI_string_search.h" + +#include "DNA_modifier_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_node_ui_storage.hh" +#include "BKE_object.h" + +#include "UI_interface.h" +#include "UI_resources.h" + +#include "node_intern.h" + +using blender::IndexRange; +using blender::Map; +using blender::Set; +using blender::StringRef; + +struct AttributeSearchData { + const bNodeTree &node_tree; + const bNode &node; + + uiBut *search_button; + + /* Used to keep track of a button pointer over multiple redraws. Since the UI code + * may reallocate the button, without this we might end up with a dangling pointer. */ + uiButStore *button_store; + uiBlock *button_store_block; +}; + +static void attribute_search_update_fn( + const bContext *C, void *arg, const char *str, uiSearchItems *items, const bool is_first) +{ + AttributeSearchData *data = static_cast(arg); + const NodeUIStorage *ui_storage = BKE_node_tree_ui_storage_get_from_context( + C, data->node_tree, data->node); + if (ui_storage == nullptr) { + return; + } + + const Set &attribute_name_hints = ui_storage->attribute_name_hints; + + if (str[0] != '\0' && !attribute_name_hints.contains_as(StringRef(str))) { + /* Any string may be valid, so add the current search string with the hints. */ + UI_search_item_add(items, str, (void *)str, ICON_ADD, 0, 0); + } + + /* Skip the filter when the menu is first opened, so all of the items are visible. */ + if (is_first) { + for (const std::string &attribute_name : attribute_name_hints) { + /* Just use the pointer to the name string as the search data, + * since it's not used anyway but we need a pointer. */ + UI_search_item_add(items, attribute_name.c_str(), (void *)&attribute_name, ICON_NONE, 0, 0); + } + return; + } + + StringSearch *search = BLI_string_search_new(); + for (const std::string &attribute_name : attribute_name_hints) { + BLI_string_search_add(search, attribute_name.c_str(), (void *)&attribute_name); + } + + std::string **filtered_items; + const int filtered_amount = BLI_string_search_query(search, str, (void ***)&filtered_items); + + for (const int i : IndexRange(filtered_amount)) { + std::string *item = filtered_items[i]; + if (!UI_search_item_add(items, item->c_str(), item, ICON_NONE, 0, 0)) { + break; + } + } + + MEM_freeN(filtered_items); + BLI_string_search_free(search); +} + +static void attribute_search_free_fn(void *arg) +{ + AttributeSearchData *data = static_cast(arg); + + UI_butstore_free(data->button_store_block, data->button_store); + delete data; +} + +void node_geometry_add_attribute_search_button(const bNodeTree *node_tree, + const bNode *node, + PointerRNA *socket_ptr, + uiLayout *layout) +{ + uiBlock *block = uiLayoutGetBlock(layout); + uiBut *but = uiDefIconTextButR(block, + UI_BTYPE_SEARCH_MENU, + 0, + ICON_NONE, + "", + 0, + 0, + 10 * UI_UNIT_X, /* Dummy value, replaced by layout system. */ + UI_UNIT_Y, + socket_ptr, + "default_value", + 0, + 0.0f, + 0.0f, + 0.0f, + 0.0f, + ""); + + AttributeSearchData *data = new AttributeSearchData{ + *node_tree, + *node, + but, + UI_butstore_create(block), + block, + }; + + UI_butstore_register(data->button_store, &data->search_button); + + UI_but_func_search_set_results_are_suggestions(but, true); + UI_but_func_search_set(but, + nullptr, + attribute_search_update_fn, + static_cast(data), + attribute_search_free_fn, + nullptr, + nullptr); +} diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 5973d59e68f..972e6cab123 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -38,9 +38,11 @@ struct bContext; struct bNode; struct bNodeLink; struct bNodeSocket; +struct uiBut; struct wmGizmoGroupType; struct wmKeyConfig; struct wmWindow; +struct uiBlock; #ifdef __cplusplus extern "C" { @@ -289,6 +291,12 @@ void NODE_GGT_backdrop_corner_pin(struct wmGizmoGroupType *gzgt); void NODE_OT_cryptomatte_layer_add(struct wmOperatorType *ot); void NODE_OT_cryptomatte_layer_remove(struct wmOperatorType *ot); +/* node_geometry_attribute_search.cc */ +void node_geometry_add_attribute_search_button(const struct bNodeTree *node_tree, + const struct bNode *node, + struct PointerRNA *socket_ptr, + struct uiLayout *layout); + extern const char *node_context_dir[]; /* XXXXXX */ -- cgit v1.2.3 From 3eb8307160e327f64593d218125d02289285aa17 Mon Sep 17 00:00:00 2001 From: Patrick Busch Date: Tue, 2 Mar 2021 13:13:36 -0600 Subject: Python API: Expose CurveMapping Reset View function The Python API for the curve mapping widget offers the `update` function, but no way to reset the view to the clipping rectangle. This commit adds a blenkernel function for this operation, and exposes it to the CurvMapping RNA API. This allows addons to display a more user-friendly view of the data in this widget. Differential Revision: https://developer.blender.org/D10561 --- source/blender/editors/interface/interface_templates.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_templates.c b/source/blender/editors/interface/interface_templates.c index eadbe618899..c5e67e0334e 100644 --- a/source/blender/editors/interface/interface_templates.c +++ b/source/blender/editors/interface/interface_templates.c @@ -3987,7 +3987,7 @@ static void curvemap_tools_dofunc(bContext *C, void *cumap_v, int event) BKE_curvemapping_changed(cumap, false); break; case UICURVE_FUNC_RESET_VIEW: - cumap->curr = cumap->clipr; + BKE_curvemapping_reset_view(cumap); break; case UICURVE_FUNC_HANDLE_VECTOR: /* set vector */ BKE_curvemap_handle_set(cuma, HD_VECT); -- cgit v1.2.3 From 6aec6568a0a3756f0b974ea3c8a00a1fb39a354b Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 3 Mar 2021 06:14:01 +1100 Subject: Cleanup: spelling, minor corrections Also use doxygen comments for sculpt functions. --- .../blender/editors/armature/armature_relations.c | 9 +- .../blender/editors/sculpt_paint/sculpt_expand.c | 281 ++++++++++++++------- source/blender/editors/uvedit/uvedit_select.c | 5 +- 3 files changed, 193 insertions(+), 102 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index bb5bcd4083e..4dbe448c4ec 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -552,9 +552,12 @@ static void separated_armature_fix_links(Main *bmain, Object *origArm, Object *n } } -/* Helper function for armature separating - remove certain bones from the given armature - * sel: remove selected bones from the armature, otherwise the unselected bones are removed - * (ob is not in edit-mode) +/** + * Helper function for armature separating - remove certain bones from the given armature. + * + * \param ob: Armature object (must not be is not in edit-mode). + * \param is_select: remove selected bones from the armature, + * otherwise the unselected bones are removed. */ static void separate_armature_bones(Main *bmain, Object *ob, const bool is_select) { diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index aaa4bed0d2e..53f2821eb76 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -86,37 +86,44 @@ * are only valid on parts of the mesh that are in the same connected component as the given * initial vertices. If needed, these falloff values are propagated from vertex or grids into the * base mesh faces. + * * - On each modal callback, the operator gets the active vertex and face and gets its falloff - * value from its precalculated falloff. This is now the active falloff value. + * value from its precalculated falloff. This is now the active falloff value. * - Using the active falloff value and the settings of the expand operation (which can be modified - * during execution using the modal keymap), the operator loops over all elements in the mesh to - * check if they are enabled of not. + * during execution using the modal key-map), the operator loops over all elements in the mesh to + * check if they are enabled of not. * - Based on each element state after evaluating the settings, the desired mesh data (mask, face - * sets, colors...) is updated. + * sets, colors...) is updated. */ -/* Used for defining an invalid vertex state (for example, when the cursor is not over the mesh). +/** + * Used for defining an invalid vertex state (for example, when the cursor is not over the mesh). */ #define SCULPT_EXPAND_VERTEX_NONE -1 -/* Used for defining an uninitialized active component index for an unused symmetry pass. */ - +/** Used for defining an uninitialized active component index for an unused symmetry pass. */ #define EXPAND_ACTIVE_COMPONENT_NONE -1 -/* Defines how much each time the texture distortion is increased/decreased when using the modal - * keymap. */ +/** + * Defines how much each time the texture distortion is increased/decreased + * when using the modal key-map. + */ #define SCULPT_EXPAND_TEXTURE_DISTORTION_STEP 0.01f -/* This threshold offsets the required falloff value to start a new loop. This is needed because in +/** + * This threshold offsets the required falloff value to start a new loop. This is needed because in * some situations, vertices which have the same falloff value as max_falloff will start a new - * loop, which is undesired. */ + * loop, which is undesired. + */ #define SCULPT_EXPAND_LOOP_THRESHOLD 0.00001f -/* Defines how much changes in curvature in the mesh affect the falloff shape when using normal +/** + * Defines how much changes in curvature in the mesh affect the falloff shape when using normal * falloff. This default was found experimentally and it works well in most cases, but can be - * exposed for tweaking if needed. */ + * exposed for tweaking if needed. + */ #define SCULPT_EXPAND_NORMALS_FALLOFF_EDGE_SENSITIVITY 300 -/* Expand Modal Keymap. */ +/* Expand Modal Key-map. */ enum { SCULPT_EXPAND_MODAL_CONFIRM = 1, SCULPT_EXPAND_MODAL_CANCEL, @@ -143,8 +150,10 @@ enum { * functions for getting the state of an element return true it means that data associated to that * element will be modified by expand. */ -/* Returns true if the vertex is in a connected component with correctly initialized falloff - * values. */ +/** + * Returns true if the vertex is in a connected component with correctly initialized falloff + * values. + */ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss, ExpandCache *expand_cache, const int v) @@ -157,7 +166,8 @@ static bool sculpt_expand_is_vert_in_active_component(SculptSession *ss, return false; } -/* Returns true if the face is in a connected component with correctly initialized falloff values. +/** + * Returns true if the face is in a connected component with correctly initialized falloff values. */ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, ExpandCache *expand_cache, @@ -167,8 +177,10 @@ static bool sculpt_expand_is_face_in_active_component(SculptSession *ss, return sculpt_expand_is_vert_in_active_component(ss, expand_cache, loop->v); } -/* Returns the falloff value of a vertex. This function includes texture distortion, which is not - * precomputed into the initial falloff values. */ +/** + * Returns the falloff value of a vertex. This function includes texture distortion, which is not + * precomputed into the initial falloff values. + */ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, ExpandCache *expand_cache, const int v) @@ -191,8 +203,10 @@ static float sculpt_expand_falloff_value_vertex_get(SculptSession *ss, return expand_cache->vert_falloff[v] + distortion; } -/* Returns the maximum valid falloff value stored in the falloff array, taking the maximum possible - * texture distortion into account. */ +/** + * Returns the maximum valid falloff value stored in the falloff array, taking the maximum possible + * texture distortion into account. + */ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache) { if (expand_cache->texture_distortion_strength == 0.0f) { @@ -207,8 +221,9 @@ static float sculpt_expand_max_vertex_falloff_get(ExpandCache *expand_cache) (0.5f * expand_cache->texture_distortion_strength * expand_cache->max_vert_falloff); } -/* Main function to get the state of a vertex for the current state and settings of a ExpandCache. - * Retruns true when the target data should be modified by expand. +/** + * Main function to get the state of a vertex for the current state and settings of a #ExpandCache. + * Returns true when the target data should be modified by expand. */ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache, const int v) { @@ -252,8 +267,10 @@ static bool sculpt_expand_state_get(SculptSession *ss, ExpandCache *expand_cache return enabled; } -/* Main function to get the state of a face for the current state and settings of a ExpandCache. - * Returs true when the traget data should be modified by expand. */ +/** + * Main function to get the state of a face for the current state and settings of a #ExpandCache. + * Returns true when the target data should be modified by expand. + */ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_cache, const int f) { if (ss->face_sets[f] <= 0) { @@ -296,8 +313,10 @@ static bool sculpt_expand_face_state_get(SculptSession *ss, ExpandCache *expand_ return enabled; } -/* For target modes that support gradients (such as sculpt masks or colors), this function returns - * the corresponding gradient value for an enabled vertex. */ +/** + * For target modes that support gradients (such as sculpt masks or colors), this function returns + * the corresponding gradient value for an enabled vertex. + */ static float sculpt_expand_gradient_value_get(SculptSession *ss, ExpandCache *expand_cache, const int v) @@ -335,8 +354,10 @@ static float sculpt_expand_gradient_value_get(SculptSession *ss, /* Utility functions for getting all vertices state during expand. */ -/* Returns a bitmap indexed by vertex index which contains if the vertex was enabled or not for a - * give expand_cache state. */ +/** + * Returns a bitmap indexed by vertex index which contains if the vertex was enabled or not for a + * give expand_cache state. + */ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCache *expand_cache) { const int totvert = SCULPT_vertex_count_get(ss); @@ -348,9 +369,11 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa return enabled_vertices; } -/* Returns a bitmap indexed by vertex index which contains if the vertex is in the boundary of the +/** + * Returns a bitmap indexed by vertex index which contains if the vertex is in the boundary of the * enabled vertices. This is defined as vertices that are enabled and at least have one connected - * vertex that is not enabled. */ + * vertex that is not enabled. + */ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, BLI_bitmap *enabled_vertices, const bool use_mesh_boundary) @@ -383,8 +406,10 @@ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, /* Functions implementing different algorithms for initializing falloff values. */ -/* Utility function to get the closet vertex after flipping an original vertex possition based on - * an symmetry pass iteration index. */ +/** + * Utility function to get the closet vertex after flipping an original vertex position based on + * an symmetry pass iteration index. + */ static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, const char symm_it, const int original_vertex) @@ -402,15 +427,19 @@ static int sculpt_expand_get_vertex_index_for_symmetry_pass(Object *ob, return symm_vertex; } -/* Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking - * symmetry into account. */ +/** + * Geodesic: Initializes the falloff with geodesic distances from the given active vertex, taking + * symmetry into account. + */ static float *sculpt_expand_geodesic_falloff_create(Sculpt *sd, Object *ob, const int v) { return SCULPT_geodesic_from_vertex_and_symm(sd, ob, v, FLT_MAX); } -/* Topology: Initializes the falloff using a floodfill operation, increasing the falloff value by 1 - * when visiting a new vertex. */ +/** + * Topology: Initializes the falloff using a flood-fill operation, + * increasing the falloff value by 1 when visiting a new vertex. + */ typedef struct ExpandFloodFillData { float original_normal[3]; float edge_sensitivity; @@ -451,10 +480,11 @@ static float *sculpt_expand_topology_falloff_create(Sculpt *sd, Object *ob, cons return dists; } -/* Normals: Floodfills the mesh and reduces the falloff depending on the normal difference between - * each vertex and the previous one. This creates falloff pattens that follow and snap to the hard - * edges of the object. */ - +/** + * Normals: Flood-fills the mesh and reduces the falloff depending on the normal difference between + * each vertex and the previous one. + * This creates falloff patterns that follow and snap to the hard edges of the object. + */ static bool mask_expand_normal_floodfill_cb( SculptSession *ss, int from_v, int to_v, bool is_duplicate, void *userdata) { @@ -521,9 +551,10 @@ static float *sculpt_expand_normal_falloff_create(Sculpt *sd, return dists; } -/* Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into - * account. */ - +/** + * Spherical: Initializes the falloff based on the distance from a vertex, taking symmetry into + * account. + */ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) { SculptSession *ss = ob->sculpt; @@ -551,9 +582,11 @@ static float *sculpt_expand_spherical_falloff_create(Object *ob, const int v) return dists; } -/* Boundary: This falloff mode uses the code from sculpt_boundary to initialize the closest mesh +/** + * Boundary: This falloff mode uses the code from sculpt_boundary to initialize the closest mesh * boundary to a falloff value of 0. Then, it propagates that falloff to the rest of the mesh so it - * stays parallel to the boundary, increasing the falloff value by 1 on each step. */ + * stays parallel to the boundary, increasing the falloff value by 1 on each step. + */ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const int v) { SculptSession *ss = ob->sculpt; @@ -610,9 +643,11 @@ static float *sculpt_expand_boundary_topology_falloff_create(Object *ob, const i return dists; } -/* Topology diagonals. This falloff is similar to topology, but it also considers the diagonals of +/** + * Topology diagonals. This falloff is similar to topology, but it also considers the diagonals of * the base mesh faces when checking a vertex neighbor. For this reason, this is not implement - * using the general floodfill and sculpt neighbors accessors. */ + * using the general flood-fill and sculpt neighbors accessors. + */ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) { SculptSession *ss = ob->sculpt; @@ -669,11 +704,13 @@ static float *sculpt_expand_diagonals_falloff_create(Object *ob, const int v) return dists; } -/* Functions to update the max_falloff value in the ExpandCache. These funcions are called after +/* Functions to update the max_falloff value in the #ExpandCache. These functions are called after * initializing a new falloff to make sure that this value is always updated. */ -/* Updates the max_falloff value for vertices in a ExpandCache based on the current values of the - * falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. */ +/** + * Updates the max_falloff value for vertices in a #ExpandCache based on the current values of the + * falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. + */ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss, ExpandCache *expand_cache) { @@ -693,8 +730,10 @@ static void sculpt_expand_update_max_vert_falloff_value(SculptSession *ss, } } -/* Updates the max_falloff value for faces in a ExpandCache based on the current values of the - * falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. */ +/** + * Updates the max_falloff value for faces in a ExpandCache based on the current values of the + * falloff, skipping any invalid values initialized to FLT_MAX and not initialized components. + */ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss, ExpandCache *expand_cache) { @@ -714,10 +753,12 @@ static void sculpt_expand_update_max_face_falloff_factor(SculptSession *ss, } } -/* Functions to get falloff values for faces from the values from the vertices. This is used for - * expanding Face Sets. Depending on the data type of the SculptSession, this needs to get the per +/** + * Functions to get falloff values for faces from the values from the vertices. This is used for + * expanding Face Sets. Depending on the data type of the #SculptSession, this needs to get the per * face falloff value from the connected vertices of each face or from the grids stored per loops - * for each face. */ + * for each face. + */ static void sculpt_expand_grids_to_faces_falloff(SculptSession *ss, Mesh *mesh, ExpandCache *expand_cache) @@ -751,7 +792,9 @@ static void sculpt_expand_vertex_to_faces_falloff(Mesh *mesh, ExpandCache *expan } } -/* Main function to update the faces falloff from a already calculated vertex falloff. */ +/** + * Main function to update the faces falloff from a already calculated vertex falloff. + */ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *ss, Mesh *mesh, ExpandCache *expand_cache) @@ -777,8 +820,10 @@ static void sculpt_expand_mesh_face_falloff_from_vertex_falloff(SculptSession *s /* Recursions. These functions will generate new falloff values based on the state of the vertices * from the current ExpandCache options and falloff values. */ -/* Geodesic recursion: Initializes falloff values using geodesic distances from the boundary of the - * current vertices state. */ +/** + * Geodesic recursion: Initializes falloff values using geodesic distances from the boundary of the + * current vertices state. + */ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, ExpandCache *expand_cache, BLI_bitmap *enabled_vertices) @@ -804,8 +849,10 @@ static void sculpt_expand_geodesics_from_state_boundary(Object *ob, BLI_gset_free(initial_vertices, NULL); } -/* Topology recursion: Initializes falloff values using topology steps from the boundary of the - * current vertices state, increasing the value by 1 each time a new vertex is visited. */ +/** + * Topology recursion: Initializes falloff values using topology steps from the boundary of the + * current vertices state, increasing the value by 1 each time a new vertex is visited. + */ static void sculpt_expand_topology_from_state_boundary(Object *ob, ExpandCache *expand_cache, BLI_bitmap *enabled_vertices) @@ -837,7 +884,9 @@ static void sculpt_expand_topology_from_state_boundary(Object *ob, expand_cache->vert_falloff = dists; } -/* Main function to create a recursion step from the current ExpandCache state. */ +/** + * Main function to create a recursion step from the current #ExpandCache state. + */ static void sculpt_expand_resursion_step_add(Object *ob, ExpandCache *expand_cache, const eSculptExpandRecursionType recursion_type) @@ -873,8 +922,11 @@ static void sculpt_expand_resursion_step_add(Object *ob, } /* Face Set Boundary falloff. */ -/* When internal falloff is set to true, the falloff will fill the active Face Set with a gradient, - * otherwise the active Face Set will be filled with a constant falloff of 0.0f. */ + +/** + * When internal falloff is set to true, the falloff will fill the active Face Set with a gradient, + * otherwise the active Face Set will be filled with a constant falloff of 0.0f. + */ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, ExpandCache *expand_cache, const int active_face_set, @@ -932,8 +984,10 @@ static void sculpt_expand_initialize_from_face_set_boundary(Object *ob, } } -/* Main function to initialize new falloff values in a ExpandCache given an initial vertex and a - * falloff type. */ +/** + * Main function to initialize new falloff values in a #ExpandCache given an initial vertex and a + * falloff type. + */ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( ExpandCache *expand_cache, Sculpt *sd, @@ -989,9 +1043,11 @@ static void sculpt_expand_falloff_factors_from_vertex_and_symm_create( } } -/* Adds to the snapping Face Set gset all Face Sets which contain all enabled vertices for the - * current ExpandCache state. This improves the usability of snapping, as already enabled elements - * won't switch their state when toggling snapping with the modal keymap.*/ +/** + * Adds to the snapping Face Set `gset` all Face Sets which contain all enabled vertices for the + * current #ExpandCache state. This improves the usability of snapping, as already enabled elements + * won't switch their state when toggling snapping with the modal key-map. + */ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, ExpandCache *expand_cache) { @@ -1035,7 +1091,9 @@ static void sculpt_expand_snap_initialize_from_enabled(SculptSession *ss, expand_cache->invert = prev_invert_state; } -/* Functions to free a ExpandCache. */ +/** + * Functions to free a #ExpandCache. + */ static void sculpt_expand_cache_data_free(ExpandCache *expand_cache) { if (expand_cache->snap_enabled_face_sets) { @@ -1059,7 +1117,9 @@ static void sculpt_expand_cache_free(SculptSession *ss) ss->expand_cache = NULL; } -/* Functions to restore the original state from the ExpandCache when canceling the operator. */ +/** + * Functions to restore the original state from the #ExpandCache when canceling the operator. + */ static void sculpt_expand_restore_face_set_data(SculptSession *ss, ExpandCache *expand_cache) { PBVHNode **nodes; @@ -1140,7 +1200,9 @@ static void sculpt_expand_restore_original_state(bContext *C, } } -/* Cancel operator callback. */ +/** + * Cancel operator callback. + */ static void sculpt_expand_cancel(bContext *C, wmOperator *UNUSED(op)) { Object *ob = CTX_data_active_object(C); @@ -1154,7 +1216,9 @@ static void sculpt_expand_cancel(bContext *C, wmOperator *UNUSED(op)) /* Functions to update the sculpt mesh data. */ -/* Callback to update mask data per PBVH node. */ +/** + * Callback to update mask data per PBVH node. + */ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -1201,7 +1265,9 @@ static void sculpt_expand_mask_update_task_cb(void *__restrict userdata, } } -/* Update Face Set data. Not multithreaded per node as nodes don't contain face arrays. */ +/** + * Update Face Set data. Not multi-threaded per node as nodes don't contain face arrays. + */ static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expand_cache) { const int totface = ss->totfaces; @@ -1223,7 +1289,9 @@ static void sculpt_expand_face_sets_update(SculptSession *ss, ExpandCache *expan } } -/* Callback to update vertex colors per PBVH node. */ +/** + * Callback to update vertex colors per PBVH node. + */ static void sculpt_expand_colors_update_task_cb(void *__restrict userdata, const int i, const TaskParallelTLS *__restrict UNUSED(tls)) @@ -1327,7 +1395,9 @@ static void sculpt_expand_original_state_store(Object *ob, ExpandCache *expand_c } } -/* Restore the state of the Face Sets before a new update. */ +/** + * Restore the state of the Face Sets before a new update. + */ static void sculpt_expand_face_sets_restore(SculptSession *ss, ExpandCache *expand_cache) { const int totfaces = ss->totfaces; @@ -1388,7 +1458,9 @@ static void sculpt_expand_update_for_vertex(bContext *C, Object *ob, const int v sculpt_expand_flush_updates(C); } -/* Updates the SculptSession cursor data and gets the active vertex if the cursor is over the mesh. +/** + * Updates the #SculptSession cursor data and gets the active vertex + * if the cursor is over the mesh. */ static int sculpt_expand_target_vertex_update_and_get(bContext *C, Object *ob, @@ -1404,8 +1476,10 @@ static int sculpt_expand_target_vertex_update_and_get(bContext *C, } } -/* Moves the sculpt pivot to the average point of the boundary enabled vertices of the current - * expand state. Take symmetry and active components into account. */ +/** + * Moves the sculpt pivot to the average point of the boundary enabled vertices of the current + * expand state. Take symmetry and active components into account. + */ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache *expand_cache) { SculptSession *ss = ob->sculpt; @@ -1417,7 +1491,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache BLI_bitmap *enabled_vertices = sculpt_expand_bitmap_from_enabled(ss, expand_cache); /* For boundary topology, position the pivot using only the boundary of the enabled vertices, - * without taking mesh boundary into account. This allows to creat deformations like bending the + * without taking mesh boundary into account. This allows to create deformations like bending the * mesh from the boundary of the mask that was just created. */ const float use_mesh_boundary = expand_cache->falloff_type != SCULPT_EXPAND_FALLOFF_BOUNDARY_TOPOLOGY; @@ -1425,7 +1499,7 @@ static void sculpt_expand_reposition_pivot(bContext *C, Object *ob, ExpandCache BLI_bitmap *boundary_vertices = sculpt_expand_boundary_from_enabled( ss, enabled_vertices, use_mesh_boundary); - /* Ignore invert state, as this is the expected behaviour in most cases and mask are created in + /* Ignore invert state, as this is the expected behavior in most cases and mask are created in * inverted state by default. */ expand_cache->invert = initial_invert_state; @@ -1494,8 +1568,10 @@ static void sculpt_expand_finish(bContext *C) ED_workspace_status_text(C, NULL); } -/* Finds and stores in the ExpandCache the sculpt connected component index for each symmetry pass - * needed for expand. */ +/** + * Finds and stores in the #ExpandCache the sculpt connected component index for each symmetry pass + * needed for expand. + */ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, ExpandCache *expand_cache, const int initial_vertex) @@ -1519,8 +1595,10 @@ static void sculpt_expand_find_active_connected_components_from_vert(Object *ob, } } -/* Stores the active vertex, Face Set and mouse coordinates in the ExpandCache based on the current - * cursor position. */ +/** + * Stores the active vertex, Face Set and mouse coordinates in the #ExpandCache based on the + * current cursor position. + */ static void sculpt_expand_set_initial_components_for_mouse(bContext *C, Object *ob, ExpandCache *expand_cache, @@ -1554,8 +1632,10 @@ static void sculpt_expand_set_initial_components_for_mouse(bContext *C, sculpt_expand_find_active_connected_components_from_vert(ob, expand_cache, initial_vertex); } -/* Displaces the initial mouse coordinates using the new mouse position to get a new active vertex. - * After that, initializes a new falloff of the same type with the new active vertex. */ +/** + * Displaces the initial mouse coordinates using the new mouse position to get a new active vertex. + * After that, initializes a new falloff of the same type with the new active vertex. + */ static void sculpt_expand_move_propagation_origin(bContext *C, Object *ob, const wmEvent *event, @@ -1579,7 +1659,9 @@ static void sculpt_expand_move_propagation_origin(bContext *C, expand_cache->move_preview_falloff_type); } -/* Ensures that the SculptSession contains the required data needed for Expand. */ +/** + * Ensures that the #SculptSession contains the required data needed for Expand. + */ static void sculpt_expand_ensure_sculptsession_data(Object *ob) { SculptSession *ss = ob->sculpt; @@ -1591,7 +1673,9 @@ static void sculpt_expand_ensure_sculptsession_data(Object *ob) } } -/* Returns the active Face Sets ID from the enabled face or grid in the SculptSession. */ +/** + * Returns the active Face Sets ID from the enabled face or grid in the #SculptSession. + */ static int sculpt_expand_active_face_set_id_get(SculptSession *ss, ExpandCache *expand_cache) { switch (BKE_pbvh_type(ss->pbvh)) { @@ -1804,20 +1888,23 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event } } - /* Update the sculpt data with the current state of the ExpandCache. */ + /* Update the sculpt data with the current state of the #ExpandCache. */ sculpt_expand_update_for_vertex(C, ob, target_expand_vertex); return OPERATOR_RUNNING_MODAL; } -/* Deletes the delete_id Face Set ID from the mesh Face Sets and stores the result in r_face_set. - * The faces that were using the delete_id Face Set are filled using the content from their - * neighbors. */ +/** + * Deletes the `delete_id` Face Set ID from the mesh Face Sets + * and stores the result in `r_face_set`. + * The faces that were using the `delete_id` Face Set are filled + * using the content from their neighbors. + */ static void sculpt_expand_delete_face_set_id( int *r_face_sets, Mesh *mesh, MeshElemMap *pmap, const int totface, const int delete_id) { - /* Check that all the face sets IDs in the mesh are not equal to delete_id befor attempting to - * delete it. */ + /* Check that all the face sets IDs in the mesh are not equal to `delete_id` + * before attempting to delete it. */ bool all_same_id = true; for (int i = 0; i < totface; i++) { if (r_face_sets[i] != delete_id) { @@ -1906,7 +1993,9 @@ static void sculpt_expand_cache_initial_config_set(bContext *C, expand_cache->blend_mode = expand_cache->brush->blend; } -/* Does the undo sculpt push for the affected target data of the ExpandCache. */ +/** + * Does the undo sculpt push for the affected target data of the #ExpandCache. + */ static void sculpt_expand_undo_push(Object *ob, ExpandCache *expand_cache) { SculptSession *ss = ob->sculpt; diff --git a/source/blender/editors/uvedit/uvedit_select.c b/source/blender/editors/uvedit/uvedit_select.c index 88802e0d868..f46975c9378 100644 --- a/source/blender/editors/uvedit/uvedit_select.c +++ b/source/blender/editors/uvedit/uvedit_select.c @@ -3313,7 +3313,6 @@ static bool do_lasso_select_mesh_uv(bContext *C, uv_select_all_perform_multi(scene, objects, objects_len, SEL_DESELECT); } - /* don't indent to avoid diff noise! */ for (uint ob_index = 0; ob_index < objects_len; ob_index++) { Object *obedit = objects[ob_index]; @@ -3323,7 +3322,7 @@ static bool do_lasso_select_mesh_uv(bContext *C, const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - if (use_face_center) { /* Face Center Sel */ + if (use_face_center) { /* Face Center Select. */ BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_elem_flag_disable(efa, BM_ELEM_TAG); /* assume not touched */ @@ -3366,7 +3365,7 @@ static bool do_lasso_select_mesh_uv(bContext *C, } } } - else { /* Vert Sel */ + else { /* Vert Selection. */ BM_mesh_elem_hflag_disable_all(em->bm, BM_VERT, BM_ELEM_TAG, false); BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { -- cgit v1.2.3 From a4f81c683896b9ed0c7ca8411c7796f7ccfe65f9 Mon Sep 17 00:00:00 2001 From: Fabian Schempp Date: Tue, 2 Mar 2021 23:09:38 +0100 Subject: Fix T85966: Wrong link picked when dragging multi-input socket The socket drag operator stored the index of the last picked socket into RNA in case the mouse cursor leaves the link while dragging. This id was not unique which is why sometimes a link from an other node with the same id is picked. This patch changes the way the last picked link is stored and stores a pointer to the link directly into bNodeLinkDrag struct instead. Differential Revision: https://developer.blender.org/D10590 --- source/blender/editors/space_node/node_intern.h | 3 +++ .../editors/space_node/node_relationships.c | 24 ++++++---------------- 2 files changed, 9 insertions(+), 18 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 972e6cab123..4ec8f56480e 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -59,6 +59,9 @@ typedef struct bNodeLinkDrag { ListBase links; bool from_multi_input_socket; int in_out; + + /** Temporarily stores the last picked link from multi input socket operator. */ + struct bNodeLink *last_picked_multi_input_socket_link; } bNodeLinkDrag; typedef struct SpaceNode_Runtime { diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index d6edfcce8e8..218102d2261 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -280,7 +280,7 @@ static void pick_input_link_by_link_intersect(const bContext *C, float distance = dist_squared_to_line_segment_v2(cursor, l1, l2); if (distance < cursor_link_touch_distance) { link_to_pick = link; - RNA_int_set(op->ptr, "last_picked_link_index", link->multi_input_socket_index); + nldrag->last_picked_multi_input_socket_link = link_to_pick; } } } @@ -290,13 +290,9 @@ static void pick_input_link_by_link_intersect(const bContext *C, * Not essential for the basic behavior, but can make interaction feel a bit better if * the mouse moves to the right and loses the "selection." */ if (!link_to_pick) { - int last_picked_link_index = RNA_int_get(op->ptr, "last_picked_link_index"); - if (last_picked_link_index > -1) { - LISTBASE_FOREACH (bNodeLink *, link, &snode->edittree->links) { - if (link->multi_input_socket_index == last_picked_link_index) { - link_to_pick = link; - } - } + bNodeLink *last_picked_link = nldrag->last_picked_multi_input_socket_link; + if (last_picked_link) { + link_to_pick = last_picked_link; } } @@ -1032,13 +1028,14 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) float cursor[2]; UI_view2d_region_to_view(®ion->v2d, event->mval[0], event->mval[1], &cursor[0], &cursor[1]); RNA_float_set_array(op->ptr, "drag_start", cursor); - RNA_int_set(op->ptr, "last_picked_link_index", -1); RNA_boolean_set(op->ptr, "has_link_picked", false); ED_preview_kill_jobs(CTX_wm_manager(C), bmain); bNodeLinkDrag *nldrag = node_link_init(bmain, snode, cursor, detach); + nldrag->last_picked_multi_input_socket_link = NULL; + if (nldrag) { op->customdata = nldrag; BLI_addtail(&snode->runtime->linkdrag, nldrag); @@ -1102,15 +1099,6 @@ void NODE_OT_link(wmOperatorType *ot) -UI_PRECISION_FLOAT_MAX, UI_PRECISION_FLOAT_MAX); RNA_def_property_flag(prop, PROP_HIDDEN); - RNA_def_int(ot->srna, - "last_picked_link_index", - -1, - -1, - 4095, - "Last Picked Link Index", - "The index of the last picked link on a multi-input socket", - -1, - 4095); RNA_def_property_flag(prop, PROP_HIDDEN); } -- cgit v1.2.3 From 4dd1068a5789097b50eab159adc995906fa5ea84 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Tue, 2 Mar 2021 22:53:25 -0600 Subject: Fix crash when dragging nodes The `bNodeLinkDrag` struct was NULL when dragging a node instead of a link. It is allocated with `calloc` anyway, so this field doesn't need to be explitely cleared. --- source/blender/editors/space_node/node_relationships.c | 2 -- 1 file changed, 2 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/node_relationships.c b/source/blender/editors/space_node/node_relationships.c index 218102d2261..35dd865047e 100644 --- a/source/blender/editors/space_node/node_relationships.c +++ b/source/blender/editors/space_node/node_relationships.c @@ -1034,8 +1034,6 @@ static int node_link_invoke(bContext *C, wmOperator *op, const wmEvent *event) bNodeLinkDrag *nldrag = node_link_init(bmain, snode, cursor, detach); - nldrag->last_picked_multi_input_socket_link = NULL; - if (nldrag) { op->customdata = nldrag; BLI_addtail(&snode->runtime->linkdrag, nldrag); -- cgit v1.2.3 From b9ee8777525d12942c6c7b3adbd5a087699a6186 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 3 Mar 2021 12:14:06 +0100 Subject: Cleanup: make format --- source/blender/editors/transform/transform_constraints.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 93d5d41e121..43cbcb0aba4 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -572,7 +572,8 @@ static void constraints_rotation_impl(TransInfo *t, break; } /* don't flip axis if asked to or if num input */ - if (r_angle && !((mode & CON_NOFLIP) || hasNumInput(&t->num) || (t->flag & T_INPUT_IS_VALUES_FINAL))) { + if (r_angle && + !((mode & CON_NOFLIP) || hasNumInput(&t->num) || (t->flag & T_INPUT_IS_VALUES_FINAL))) { float view_vector[3]; view_vector_calc(t, t->center_global, view_vector); if (dot_v3v3(r_vec, view_vector) > 0.0f) { -- cgit v1.2.3 From e9c50913f83373e7679b2877a86c043e3bd89cd1 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 3 Mar 2021 12:17:16 +0100 Subject: Cleanup: clang tidy Warning: else-after-return/break --- .../blender/editors/sculpt_paint/sculpt_expand.c | 28 ++++++++++------------ .../editors/space_sequencer/sequencer_add.c | 4 +--- 2 files changed, 13 insertions(+), 19 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 53f2821eb76..2d1d973221a 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -1471,9 +1471,7 @@ static int sculpt_expand_target_vertex_update_and_get(bContext *C, if (SCULPT_cursor_geometry_info_update(C, &sgi, mouse, false)) { return SCULPT_active_vertex_get(ss); } - else { - return SCULPT_EXPAND_VERTEX_NONE; - } + return SCULPT_EXPAND_VERTEX_NONE; } /** @@ -1768,20 +1766,18 @@ static int sculpt_expand_modal(bContext *C, wmOperator *op, const wmEvent *event expand_cache->move_original_falloff_type); break; } + expand_cache->move = true; + expand_cache->move_original_falloff_type = expand_cache->falloff_type; + copy_v2_v2(expand_cache->initial_mouse_move, mouse); + copy_v2_v2(expand_cache->original_mouse_move, expand_cache->initial_mouse); + if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_GEODESIC && + SCULPT_vertex_count_get(ss) > expand_cache->max_geodesic_move_preview) { + /* Set to spherical falloff for preview in high poly meshes as it is the fastest one. + * In most cases it should match closely the preview from geodesic. */ + expand_cache->move_preview_falloff_type = SCULPT_EXPAND_FALLOFF_SPHERICAL; + } else { - expand_cache->move = true; - expand_cache->move_original_falloff_type = expand_cache->falloff_type; - copy_v2_v2(expand_cache->initial_mouse_move, mouse); - copy_v2_v2(expand_cache->original_mouse_move, expand_cache->initial_mouse); - if (expand_cache->falloff_type == SCULPT_EXPAND_FALLOFF_GEODESIC && - SCULPT_vertex_count_get(ss) > expand_cache->max_geodesic_move_preview) { - /* Set to spherical falloff for preview in high poly meshes as it is the fastest one. - * In most cases it should match closely the preview from geodesic. */ - expand_cache->move_preview_falloff_type = SCULPT_EXPAND_FALLOFF_SPHERICAL; - } - else { - expand_cache->move_preview_falloff_type = expand_cache->falloff_type; - } + expand_cache->move_preview_falloff_type = expand_cache->falloff_type; } break; } diff --git a/source/blender/editors/space_sequencer/sequencer_add.c b/source/blender/editors/space_sequencer/sequencer_add.c index 037ce78b9a2..844dbe6a0a5 100644 --- a/source/blender/editors/space_sequencer/sequencer_add.c +++ b/source/blender/editors/space_sequencer/sequencer_add.c @@ -924,9 +924,7 @@ static int sequencer_add_image_strip_calculate_length(wmOperator *op, if (use_placeholders) { return sequencer_image_seq_get_minmax_frame(op, start_frame, minframe, numdigits); } - else { - return RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); - } + return RNA_property_collection_length(op->ptr, RNA_struct_find_property(op->ptr, "files")); } static void sequencer_add_image_strip_load_files( -- cgit v1.2.3 From 7d2f27244c2ccf89b7c02ffab05f533cad1d88d5 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 3 Mar 2021 12:17:48 +0100 Subject: Cleanup: clang tidy Can use const parameter. --- source/blender/editors/sculpt_paint/sculpt_expand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/sculpt_paint/sculpt_expand.c b/source/blender/editors/sculpt_paint/sculpt_expand.c index 2d1d973221a..db6d33c2700 100644 --- a/source/blender/editors/sculpt_paint/sculpt_expand.c +++ b/source/blender/editors/sculpt_paint/sculpt_expand.c @@ -375,7 +375,7 @@ static BLI_bitmap *sculpt_expand_bitmap_from_enabled(SculptSession *ss, ExpandCa * vertex that is not enabled. */ static BLI_bitmap *sculpt_expand_boundary_from_enabled(SculptSession *ss, - BLI_bitmap *enabled_vertices, + const BLI_bitmap *enabled_vertices, const bool use_mesh_boundary) { const int totvert = SCULPT_vertex_count_get(ss); -- cgit v1.2.3 From 3f716bb626fd7d5c3edeae127407e8b361f53e7d Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Wed, 3 Mar 2021 12:23:02 +0100 Subject: Cleanup: clang tidy Warnings: * readability-inconsistent-declaration-parameter-name * readability-redundant-smartptr-get --- source/blender/editors/space_sequencer/sequencer_intern.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_sequencer/sequencer_intern.h b/source/blender/editors/space_sequencer/sequencer_intern.h index 4c942a83f2b..767ac76efe6 100644 --- a/source/blender/editors/space_sequencer/sequencer_intern.h +++ b/source/blender/editors/space_sequencer/sequencer_intern.h @@ -79,7 +79,7 @@ struct Sequence *find_neighboring_sequence(struct Scene *scene, struct Sequence *test, int lr, int sel); -void recurs_sel_seq(struct Sequence *seqm); +void recurs_sel_seq(struct Sequence *seq_meta); int seq_effect_find_selected(struct Scene *scene, struct Sequence *activeseq, int type, -- cgit v1.2.3 From f53221bff7ffdcfb3acf03389450ed5ffb8f7964 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 3 Mar 2021 12:58:33 -0600 Subject: UI: Allow translation for node error messages This commit exposes the strings used in the node error messages for localization. It also changes the message tooltip creation to automatically add the period at the end, to be more consistent with the (arguably bad) design of other tooltips in Blender. Calling `TIP_` directly in the node implementation files allows us to continue using `std::string` concatenation instead of passing variadic arguments. It's also more explicit about which part of the message is translated and which isn't. The files already include the translation header anyway. --- source/blender/editors/space_node/node_draw.cc | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/node_draw.cc b/source/blender/editors/space_node/node_draw.cc index 162f3878f7e..5a0cacf070b 100644 --- a/source/blender/editors/space_node/node_draw.cc +++ b/source/blender/editors/space_node/node_draw.cc @@ -1236,16 +1236,15 @@ static char *node_errors_tooltip_fn(bContext *UNUSED(C), void *argN, const char for (const NodeWarning &warning : warnings.drop_back(1)) { complete_string += warning.message; + /* Adding the period is not ideal for multi-line messages, but it is consistent + * with other tooltip implementations in Blender, so it is added here. */ + complete_string += '.'; complete_string += '\n'; } + /* Let the tooltip system automatically add the last period. */ complete_string += warnings.last().message; - /* Remove the last period-- the tooltip system adds this automatically. */ - if (complete_string.back() == '.') { - complete_string.pop_back(); - } - return BLI_strdupn(complete_string.c_str(), complete_string.size()); } -- cgit v1.2.3 From dd43a3701641a57871564447bdeecf154dbe13a2 Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 3 Mar 2021 13:48:27 -0600 Subject: Geometry Nodes: Allow clearing an attribute text field Because pressing enter will choose the current search item from the menu, and there was no search item with an empty string, it was impossible to clear the text of an attribute text field. This commit adds a simple "X" icon in the top row when you delete the string. --- source/blender/editors/space_node/node_geometry_attribute_search.cc | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 41f04dad221..982c57eb3ec 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -69,6 +69,12 @@ static void attribute_search_update_fn( UI_search_item_add(items, str, (void *)str, ICON_ADD, 0, 0); } + if (str[0] == '\0' && !is_first) { + /* Allow clearing the text field when the string is empty, but not on the first pass, + * or opening an attribute field for the first time would show this search item. */ + UI_search_item_add(items, str, (void *)str, ICON_X, 0, 0); + } + /* Skip the filter when the menu is first opened, so all of the items are visible. */ if (is_first) { for (const std::string &attribute_name : attribute_name_hints) { -- cgit v1.2.3 From e4a55b46c406a646efe4363a3f48dffac17533ba Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Wed, 3 Mar 2021 16:32:55 -0600 Subject: UI: Remove extra blank space for decorators in FCurve modifiers None of these properties can be animated, so it doesn't make sense to leave space for decorators on the right side of the panels. In fact that is what was intended, but they were not manually disabled in all of the panels. --- source/blender/editors/animation/fmodifier_ui.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'source/blender/editors') diff --git a/source/blender/editors/animation/fmodifier_ui.c b/source/blender/editors/animation/fmodifier_ui.c index b344e67f62d..1809daa3fcb 100644 --- a/source/blender/editors/animation/fmodifier_ui.c +++ b/source/blender/editors/animation/fmodifier_ui.c @@ -305,6 +305,7 @@ static void fmodifier_frame_range_draw(const bContext *C, Panel *panel) PointerRNA *ptr = fmodifier_get_pointers(C, panel, NULL); uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); FModifier *fcm = (FModifier *)ptr->data; uiLayoutSetActive(layout, fcm->flag & FMODIFIER_FLAG_RANGERESTRICT); @@ -478,6 +479,7 @@ static void fn_generator_panel_draw(const bContext *C, Panel *panel) uiItemR(layout, ptr, "function_type", 0, "", ICON_NONE); uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); col = uiLayoutColumn(layout, false); uiItemR(col, ptr, "use_additive", 0, NULL, ICON_NONE); @@ -698,6 +700,7 @@ static void envelope_panel_draw(const bContext *C, Panel *panel) FMod_Envelope *env = (FMod_Envelope *)fcm->data; uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* General settings. */ col = uiLayoutColumn(layout, true); @@ -792,6 +795,7 @@ static void limits_panel_draw(const bContext *C, Panel *panel) PointerRNA *ptr = fmodifier_get_pointers(C, panel, NULL); uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* Minimums. */ col = uiLayoutColumn(layout, false); @@ -853,6 +857,7 @@ static void stepped_panel_draw(const bContext *C, Panel *panel) PointerRNA *ptr = fmodifier_get_pointers(C, panel, NULL); uiLayoutSetPropSep(layout, true); + uiLayoutSetPropDecorate(layout, false); /* Stepping Settings. */ col = uiLayoutColumn(layout, false); -- cgit v1.2.3 From d10700a3ac4ae827a8f429df858fc45ff0e6295e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 4 Mar 2021 16:55:50 +1100 Subject: Cleanup: number literals --- source/blender/editors/gpencil/gpencil_edit_curve.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/gpencil_edit_curve.c b/source/blender/editors/gpencil/gpencil_edit_curve.c index 0f9a8c93df9..e766a410889 100644 --- a/source/blender/editors/gpencil/gpencil_edit_curve.c +++ b/source/blender/editors/gpencil/gpencil_edit_curve.c @@ -131,7 +131,7 @@ void GPENCIL_OT_stroke_enter_editcurve_mode(wmOperatorType *ot) "Error Threshold", "Threshold on the maximum deviation from the actual stroke", FLT_MIN, - 10.f); + 10.0f); RNA_def_property_ui_range(prop, FLT_MIN, 10.0f, 0.1f, 5); } -- cgit v1.2.3 From 39b86a989da5053ae5d6628834e5b78a16e6dffa Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 4 Mar 2021 16:57:30 +1100 Subject: Cleanup: use const arrays --- source/blender/editors/interface/interface_draw.c | 4 ++-- source/blender/editors/interface/interface_widgets.c | 2 +- source/blender/editors/mesh/editmesh_knife.c | 2 +- source/blender/editors/transform/transform_constraints.c | 4 ++-- 4 files changed, 6 insertions(+), 6 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_draw.c b/source/blender/editors/interface/interface_draw.c index d10cdc207c2..40cfcaea883 100644 --- a/source/blender/editors/interface/interface_draw.c +++ b/source/blender/editors/interface/interface_draw.c @@ -120,7 +120,7 @@ void UI_draw_roundbox_4fv_ex(const rctf *rect, }; GPUBatch *batch = ui_batch_roundbox_widget_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); - GPU_batch_uniform_4fv_array(batch, "parameters", 11, (float(*)[4]) & widget_params); + GPU_batch_uniform_4fv_array(batch, "parameters", 11, (const float(*)[4]) & widget_params); GPU_blend(GPU_BLEND_ALPHA); GPU_batch_draw(batch); GPU_blend(GPU_BLEND_NONE); @@ -2376,7 +2376,7 @@ void ui_draw_dropshadow( GPUBatch *batch = ui_batch_roundbox_shadow_get(); GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_SHADOW); - GPU_batch_uniform_4fv_array(batch, "parameters", 4, (float(*)[4]) & widget_params); + GPU_batch_uniform_4fv_array(batch, "parameters", 4, (const float(*)[4]) & widget_params); GPU_batch_uniform_1f(batch, "alpha", 1.0f - visibility); GPU_batch_draw(batch); diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index 0fa5999976b..06b87dd857f 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -1180,7 +1180,7 @@ void UI_widgetbase_draw_cache_flush(void) /* draw single */ GPU_batch_program_set_builtin(batch, GPU_SHADER_2D_WIDGET_BASE); GPU_batch_uniform_4fv_array( - batch, "parameters", MAX_WIDGET_PARAMETERS, (float(*)[4])g_widget_base_batch.params); + batch, "parameters", MAX_WIDGET_PARAMETERS, (const float(*)[4])g_widget_base_batch.params); GPU_batch_uniform_3fv(batch, "checkerColorAndSize", checker_params); GPU_batch_draw(batch); } diff --git a/source/blender/editors/mesh/editmesh_knife.c b/source/blender/editors/mesh/editmesh_knife.c index 1f894ec0f1d..b5ec3f388a0 100644 --- a/source/blender/editors/mesh/editmesh_knife.c +++ b/source/blender/editors/mesh/editmesh_knife.c @@ -966,7 +966,7 @@ static void knifetool_draw_angle_snapping(const KnifeTool_OpData *kcd) float planes[4][4]; planes_from_projmat( - (float(*)[4])kcd->projmat, planes[2], planes[0], planes[3], planes[1], NULL, NULL); + (const float(*)[4])kcd->projmat, planes[2], planes[0], planes[3], planes[1], NULL, NULL); /* ray-cast all planes */ { diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index 43cbcb0aba4..2037981e655 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -550,7 +550,7 @@ static void applyObjectConstraintSize(TransInfo *t, } static void constraints_rotation_impl(TransInfo *t, - float axismtx[3][3], + const float axismtx[3][3], float r_vec[3], float *r_angle) { @@ -621,7 +621,7 @@ static void applyObjectConstraintRot( { if (t->con.mode & CON_APPLY) { float tmp_axismtx[3][3]; - float(*axismtx)[3]; + const float(*axismtx)[3]; /* on setup call, use first object */ if (td == NULL) { -- cgit v1.2.3 From be627ab9e2e1b0d0282c204f2c02a413c77a64a5 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 4 Mar 2021 17:00:42 +1100 Subject: Cleanup: comments --- source/blender/editors/interface/interface_handlers.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 75ee3300e63..f6d8d596853 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -168,12 +168,14 @@ static bool ui_mouse_motion_keynav_test(struct uiKeyNavLock *keynav, const wmEve #define PIE_MENU_INTERVAL 0.01 #define BUTTON_AUTO_OPEN_THRESH 0.2 #define BUTTON_MOUSE_TOWARDS_THRESH 1.0 -/* pixels to move the cursor to get out of keyboard navigation */ +/** Pixels to move the cursor to get out of keyboard navigation. */ #define BUTTON_KEYNAV_PX_LIMIT 8 -#define MENU_TOWARDS_MARGIN 20 /* margin in pixels */ -#define MENU_TOWARDS_WIGGLE_ROOM 64 /* tolerance in pixels */ -/* drag-lock distance threshold in pixels */ +/** Margin around the menu, use to check if we're moving towards this rectangle (in pixels). */ +#define MENU_TOWARDS_MARGIN 20 +/** Tolerance for closing menus (in pixels). */ +#define MENU_TOWARDS_WIGGLE_ROOM 64 +/** Drag-lock distance threshold (in pixels). */ #define BUTTON_DRAGLOCK_THRESH 3 typedef enum uiButtonActivateType { -- cgit v1.2.3 From abaa6c3ace14a25c3e47cc91b6da0e569cb06d1e Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 4 Mar 2021 17:06:50 +1100 Subject: Cleanup: use BM_mesh_copy_init_customdata utility function --- source/blender/editors/mesh/editmesh_tools.c | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 43e0ab97f5d..7502b77ae02 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -4172,15 +4172,7 @@ static Base *mesh_separate_tagged( })); BM_mesh_elem_toolflags_ensure(bm_new); /* needed for 'duplicate' bmo */ - CustomData_copy(&bm_old->vdata, &bm_new->vdata, CD_MASK_BMESH.vmask, CD_CALLOC, 0); - CustomData_copy(&bm_old->edata, &bm_new->edata, CD_MASK_BMESH.emask, CD_CALLOC, 0); - CustomData_copy(&bm_old->ldata, &bm_new->ldata, CD_MASK_BMESH.lmask, CD_CALLOC, 0); - CustomData_copy(&bm_old->pdata, &bm_new->pdata, CD_MASK_BMESH.pmask, CD_CALLOC, 0); - - CustomData_bmesh_init_pool(&bm_new->vdata, bm_mesh_allocsize_default.totvert, BM_VERT); - CustomData_bmesh_init_pool(&bm_new->edata, bm_mesh_allocsize_default.totedge, BM_EDGE); - CustomData_bmesh_init_pool(&bm_new->ldata, bm_mesh_allocsize_default.totloop, BM_LOOP); - CustomData_bmesh_init_pool(&bm_new->pdata, bm_mesh_allocsize_default.totface, BM_FACE); + BM_mesh_copy_init_customdata(bm_new, bm_old, &bm_mesh_allocsize_default); /* Take into account user preferences for duplicating actions. */ const eDupli_ID_Flags dupflag = USER_DUP_MESH | (U.dupflag & USER_DUP_ACT); -- cgit v1.2.3 From 2df2f1c908fae86f4b29795384b8dabc192dc338 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 4 Mar 2021 17:10:56 +1100 Subject: Cleanup: use non-zero comparisons for event modifiers Use event modifier checks that follow most of Blender's code. --- source/blender/editors/gpencil/annotate_paint.c | 6 +++--- source/blender/editors/gpencil/gpencil_paint.c | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/annotate_paint.c b/source/blender/editors/gpencil/annotate_paint.c index c8bd38d58fe..e9817f82090 100644 --- a/source/blender/editors/gpencil/annotate_paint.c +++ b/source/blender/editors/gpencil/annotate_paint.c @@ -2093,7 +2093,7 @@ static void annotation_draw_apply_event( p->mval[1] = (float)event->mval[1] - y; /* Key to toggle stabilization. */ - if (event->shift > 0 && p->paintmode == GP_PAINTMODE_DRAW) { + if (event->shift && p->paintmode == GP_PAINTMODE_DRAW) { /* Using permanent stabilization, shift will deactivate the flag. */ if (p->flags & GP_PAINTFLAG_USE_STABILIZER) { if (p->flags & GP_PAINTFLAG_USE_STABILIZER_TEMP) { @@ -2108,7 +2108,7 @@ static void annotation_draw_apply_event( } } /* verify key status for straight lines */ - else if ((event->ctrl > 0) || (event->alt > 0)) { + else if (event->ctrl || event->alt) { if (p->straight[0] == 0) { int dx = abs((int)(p->mval[0] - p->mvalo[0])); int dy = abs((int)(p->mval[1] - p->mvalo[1])); @@ -2348,7 +2348,7 @@ static int annotation_draw_invoke(bContext *C, wmOperator *op, const wmEvent *ev p->flags |= GP_PAINTFLAG_USE_STABILIZER | GP_PAINTFLAG_USE_STABILIZER_TEMP; annotation_draw_toggle_stabilizer_cursor(p, true); } - else if (event->shift > 0) { + else if (event->shift) { p->flags |= GP_PAINTFLAG_USE_STABILIZER_TEMP; annotation_draw_toggle_stabilizer_cursor(p, true); } diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 974f51ff90b..915cac6be77 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -2835,7 +2835,7 @@ static void gpencil_draw_apply_event(bContext *C, /* verify direction for straight lines and guides */ if ((is_speed_guide) || - ((event->alt > 0) && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { + (event->alt && (RNA_boolean_get(op->ptr, "disable_straight") == false))) { if (p->straight == 0) { int dx = (int)fabsf(p->mval[0] - p->mvali[0]); int dy = (int)fabsf(p->mval[1] - p->mvali[1]); @@ -2876,13 +2876,13 @@ static void gpencil_draw_apply_event(bContext *C, /* special eraser modes */ if (p->paintmode == GP_PAINTMODE_ERASER) { - if (event->shift > 0) { + if (event->shift) { p->flags |= GP_PAINTFLAG_HARD_ERASER; } else { p->flags &= ~GP_PAINTFLAG_HARD_ERASER; } - if (event->alt > 0) { + if (event->alt) { p->flags |= GP_PAINTFLAG_STROKE_ERASER; } else { -- cgit v1.2.3 From 12b529b3a675ea69408c5eea84d2329ef156d407 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Thu, 4 Mar 2021 17:17:19 +1100 Subject: Cleanup: redundant struct declarations --- source/blender/editors/include/ED_anim_api.h | 1 - source/blender/editors/include/ED_screen.h | 1 - source/blender/editors/space_node/node_intern.h | 2 -- 3 files changed, 4 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/ED_anim_api.h b/source/blender/editors/include/ED_anim_api.h index 2415c85e299..4b440aa7367 100644 --- a/source/blender/editors/include/ED_anim_api.h +++ b/source/blender/editors/include/ED_anim_api.h @@ -55,7 +55,6 @@ struct FModifier; struct bAction; struct uiBlock; -struct uiLayout; struct PointerRNA; struct PropertyRNA; diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index 61e1f0fdf7d..b3205acb8ee 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -52,7 +52,6 @@ struct rcti; struct uiBlock; struct uiLayout; struct wmKeyConfig; -struct wmMsgBus; struct wmMsgSubscribeKey; struct wmMsgSubscribeValue; struct wmNotifier; diff --git a/source/blender/editors/space_node/node_intern.h b/source/blender/editors/space_node/node_intern.h index 4ec8f56480e..19700b258ae 100644 --- a/source/blender/editors/space_node/node_intern.h +++ b/source/blender/editors/space_node/node_intern.h @@ -38,11 +38,9 @@ struct bContext; struct bNode; struct bNodeLink; struct bNodeSocket; -struct uiBut; struct wmGizmoGroupType; struct wmKeyConfig; struct wmWindow; -struct uiBlock; #ifdef __cplusplus extern "C" { -- cgit v1.2.3 From a988099ee478dd7ab0eca79a24fb6102c5b68f82 Mon Sep 17 00:00:00 2001 From: Jacques Lucke Date: Thu, 4 Mar 2021 13:32:33 +0100 Subject: Fix T86172: tag relations update when pasting nodes Pasting nodes can create new id relations, because nodes can reference IDs. Therefore the depsgraph has to be updated when nodes are pasted. We could somehow check if the pasted nodes referenced IDs, but I'm not sure if this complexity is worth it. --- source/blender/editors/space_node/node_edit.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/node_edit.c b/source/blender/editors/space_node/node_edit.c index 4826b6c72ba..5205e50b0bf 100644 --- a/source/blender/editors/space_node/node_edit.c +++ b/source/blender/editors/space_node/node_edit.c @@ -2231,10 +2231,13 @@ static int node_clipboard_paste_exec(bContext *C, wmOperator *op) link->tosock->new_sock); } - ntreeUpdateTree(CTX_data_main(C), snode->edittree); + Main *bmain = CTX_data_main(C); + ntreeUpdateTree(bmain, snode->edittree); snode_notify(C, snode); snode_dag_update(C, snode); + /* Pasting nodes can create arbitrary new relations, because nodes can reference IDs. */ + DEG_relations_tag_update(bmain); return OPERATOR_FINISHED; } -- cgit v1.2.3 From 63b7ff9f4e5f5b0ff5d6bdb726fcd4e1d118e141 Mon Sep 17 00:00:00 2001 From: Bastien Montagne Date: Thu, 4 Mar 2021 18:39:07 +0100 Subject: Cleanup: Main `foreach ID` code: Remove `MAX_LIBARRAY` and improve comments. The `MAX_LIBARRAY` define was an annoying doublon to the `INDEX_ID_MAX` enum value now defined in `DNA_ID.h`, and it is no more useful. And comments were somewhat outdated. Also added an explanation about chosen order for the `INDEX_ID_` order. --- source/blender/editors/space_outliner/tree/tree_display_libraries.cc | 2 +- source/blender/editors/space_outliner/tree/tree_display_orphaned.cc | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc index cb5f42f08e1..a81ce11498a 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc @@ -111,7 +111,7 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, { const short filter_id_type = id_filter_get(); - ListBase *lbarray[MAX_LIBARRAY]; + ListBase *lbarray[INDEX_ID_MAX]; int tot; if (filter_id_type) { lbarray[0] = which_libbase(&mainvar, space_outliner_.filter_id_type); diff --git a/source/blender/editors/space_outliner/tree/tree_display_orphaned.cc b/source/blender/editors/space_outliner/tree/tree_display_orphaned.cc index 0b17ea98831..559cb289f3f 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_orphaned.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_orphaned.cc @@ -42,7 +42,7 @@ TreeDisplayIDOrphans::TreeDisplayIDOrphans(SpaceOutliner &space_outliner) ListBase TreeDisplayIDOrphans::buildTree(const TreeSourceData &source_data) { ListBase tree = {nullptr}; - ListBase *lbarray[MAX_LIBARRAY]; + ListBase *lbarray[INDEX_ID_MAX]; short filter_id_type = (space_outliner_.filter & SO_FILTER_ID_TYPE) ? space_outliner_.filter_id_type : 0; -- cgit v1.2.3 From 6a662ffda8360f06888452cf43c44b5507f09164 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Thu, 4 Mar 2021 21:30:17 +0100 Subject: GPencil: Implement Autokey button for Draw/Edit and Sculpt mode Now, if the Autokey is not enabled, a new frame is not created and it is used the last active one. If no active frame, a message is displayed and the operation is canceled. This is a common request for 2D artists. Also, grease pencil was not working as Blender does in other areas. Reviewed By: pepeland Differential Revision: https://developer.blender.org/D10557 --- source/blender/editors/gpencil/gpencil_fill.c | 43 +++++++++++++++++----- source/blender/editors/gpencil/gpencil_paint.c | 27 +++++++++++--- source/blender/editors/gpencil/gpencil_primitive.c | 21 +++++++++-- .../blender/editors/gpencil/gpencil_sculpt_paint.c | 13 ++++++- .../editors/transform/transform_convert_gpencil.c | 11 +++++- 5 files changed, 92 insertions(+), 23 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/gpencil/gpencil_fill.c b/source/blender/editors/gpencil/gpencil_fill.c index 406daf9f92e..85130e89ad1 100644 --- a/source/blender/editors/gpencil/gpencil_fill.c +++ b/source/blender/editors/gpencil/gpencil_fill.c @@ -55,6 +55,7 @@ #include "BKE_screen.h" #include "ED_gpencil.h" +#include "ED_keyframing.h" #include "ED_screen.h" #include "ED_space_api.h" #include "ED_view3d.h" @@ -542,12 +543,18 @@ static void gpencil_draw_datablock(tGPDfill *tgpf, const float ink[4]) if (gpl == tgpf->gpl) { if ((gpl->actframe == NULL) || (gpl->actframe->framenum != tgpf->active_cfra)) { short add_frame_mode; - if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { - add_frame_mode = GP_GETFRAME_ADD_COPY; + if (IS_AUTOKEY_ON(tgpf->scene)) { + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { + add_frame_mode = GP_GETFRAME_ADD_COPY; + } + else { + add_frame_mode = GP_GETFRAME_ADD_NEW; + } } else { - add_frame_mode = GP_GETFRAME_ADD_NEW; + add_frame_mode = GP_GETFRAME_USE_PREV; } + BKE_gpencil_layer_frame_get(gpl, tgpf->active_cfra, add_frame_mode); } } @@ -1456,7 +1463,10 @@ static void gpencil_stroke_from_buffer(tGPDfill *tgpf) tgpf->done = true; /* Get frame or create a new one. */ - tgpf->gpf = BKE_gpencil_layer_frame_get(tgpf->gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW); + tgpf->gpf = BKE_gpencil_layer_frame_get(tgpf->gpl, + tgpf->active_cfra, + IS_AUTOKEY_ON(tgpf->scene) ? GP_GETFRAME_ADD_NEW : + GP_GETFRAME_USE_PREV); /* Set frame as selected. */ tgpf->gpf->flag |= GP_FRAME_SELECT; @@ -2064,6 +2074,12 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) estate = OPERATOR_CANCELLED; break; case LEFTMOUSE: + if (!IS_AUTOKEY_ON(tgpf->scene) && (!is_multiedit) && (tgpf->gpl->actframe == NULL)) { + BKE_report(op->reports, RPT_INFO, "No available frame for creating stroke"); + estate = OPERATOR_CANCELLED; + break; + } + /* first time the event is not enabled to show help lines. */ if ((tgpf->oldkey != -1) || (!help_lines)) { ARegion *region = BKE_area_find_region_xy( @@ -2088,17 +2104,24 @@ static int gpencil_fill_modal(bContext *C, wmOperator *op, const wmEvent *event) gpencil_stroke_convertcoords_tpoint( tgpf->scene, tgpf->region, tgpf->ob, &point2D, NULL, &pt->x); + /* Hash of selected frames.*/ + GHash *frame_list = BLI_ghash_int_new_ex(__func__, 64); + /* If not multiframe and there is no frame in CFRA for the active layer, create - * a new frame before to make the hash function can find something. */ + * a new frame. */ if (!is_multiedit) { tgpf->gpf = BKE_gpencil_layer_frame_get( - tgpf->gpl, tgpf->active_cfra, GP_GETFRAME_ADD_NEW); + tgpf->gpl, + tgpf->active_cfra, + IS_AUTOKEY_ON(tgpf->scene) ? GP_GETFRAME_ADD_NEW : GP_GETFRAME_USE_PREV); tgpf->gpf->flag |= GP_FRAME_SELECT; - } - /* Hash of selected frames.*/ - GHash *frame_list = BLI_ghash_int_new_ex(__func__, 64); - BKE_gpencil_frame_selected_hash(tgpf->gpd, frame_list); + BLI_ghash_insert( + frame_list, POINTER_FROM_INT(tgpf->active_cfra), tgpf->gpl->actframe); + } + else { + BKE_gpencil_frame_selected_hash(tgpf->gpd, frame_list); + } /* Loop all frames. */ wmWindow *win = CTX_wm_window(C); diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c index 915cac6be77..1217a3a7e8f 100644 --- a/source/blender/editors/gpencil/gpencil_paint.c +++ b/source/blender/editors/gpencil/gpencil_paint.c @@ -67,6 +67,7 @@ #include "ED_clip.h" #include "ED_gpencil.h" +#include "ED_keyframing.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_view3d.h" @@ -2155,6 +2156,10 @@ static void gpencil_paint_initstroke(tGPsdata *p, continue; } + if (!IS_AUTOKEY_ON(scene) && (gpl->actframe == NULL)) { + continue; + } + /* Add a new frame if needed (and based off the active frame, * as we need some existing strokes to erase) * @@ -2164,7 +2169,8 @@ static void gpencil_paint_initstroke(tGPsdata *p, */ if (gpl->actframe && gpl->actframe->strokes.first) { if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { - gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_COPY); + short frame_mode = IS_AUTOKEY_ON(scene) ? GP_GETFRAME_ADD_COPY : GP_GETFRAME_USE_PREV; + gpl->actframe = BKE_gpencil_layer_frame_get(gpl, CFRA, frame_mode); } has_layer_to_erase = true; break; @@ -2187,11 +2193,16 @@ static void gpencil_paint_initstroke(tGPsdata *p, /* Drawing Modes - Add a new frame if needed on the active layer */ short add_frame_mode; - if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { - add_frame_mode = GP_GETFRAME_ADD_COPY; + if (IS_AUTOKEY_ON(scene)) { + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { + add_frame_mode = GP_GETFRAME_ADD_COPY; + } + else { + add_frame_mode = GP_GETFRAME_ADD_NEW; + } } else { - add_frame_mode = GP_GETFRAME_ADD_NEW; + add_frame_mode = GP_GETFRAME_USE_PREV; } bool need_tag = p->gpl->actframe == NULL; @@ -2206,6 +2217,10 @@ static void gpencil_paint_initstroke(tGPsdata *p, if (G.debug & G_DEBUG) { printf("Error: No frame created (gpencil_paint_init)\n"); } + if (!IS_AUTOKEY_ON(scene)) { + BKE_report(p->reports, RPT_INFO, "No available frame for creating stroke"); + } + return; } p->gpf->flag |= GP_FRAME_PAINT; @@ -2469,6 +2484,8 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) return 0; } + p->reports = op->reports; + /* init painting data */ gpencil_paint_initstroke(p, paintmode, CTX_data_ensure_evaluated_depsgraph(C)); if (p->status == GP_STATUS_ERROR) { @@ -2483,8 +2500,6 @@ static int gpencil_draw_init(bContext *C, wmOperator *op, const wmEvent *event) p->keymodifier = -1; } - p->reports = op->reports; - /* everything is now setup ok */ return 1; } diff --git a/source/blender/editors/gpencil/gpencil_primitive.c b/source/blender/editors/gpencil/gpencil_primitive.c index 12d399f32ca..b29ef2e7ee2 100644 --- a/source/blender/editors/gpencil/gpencil_primitive.c +++ b/source/blender/editors/gpencil/gpencil_primitive.c @@ -71,6 +71,7 @@ #include "RNA_enum_types.h" #include "ED_gpencil.h" +#include "ED_keyframing.h" #include "ED_object.h" #include "ED_screen.h" #include "ED_space_api.h" @@ -1253,9 +1254,18 @@ static void gpencil_primitive_init(bContext *C, wmOperator *op) static int gpencil_primitive_invoke(bContext *C, wmOperator *op, const wmEvent *event) { wmWindow *win = CTX_wm_window(C); + Scene *scene = CTX_data_scene(C); bGPdata *gpd = CTX_data_gpencil_data(C); tGPDprimitive *tgpi = NULL; + if (!IS_AUTOKEY_ON(scene)) { + bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); + if ((gpl == NULL) || (gpl->actframe == NULL)) { + BKE_report(op->reports, RPT_INFO, "No available frame for creating stroke"); + return OPERATOR_CANCELLED; + } + } + /* initialize operator runtime data */ gpencil_primitive_init(C, op); tgpi = op->customdata; @@ -1310,11 +1320,16 @@ static void gpencil_primitive_interaction_end(bContext *C, /* insert keyframes as required... */ short add_frame_mode; - if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { - add_frame_mode = GP_GETFRAME_ADD_COPY; + if (IS_AUTOKEY_ON(tgpi->scene)) { + if (ts->gpencil_flags & GP_TOOL_FLAG_RETAIN_LAST) { + add_frame_mode = GP_GETFRAME_ADD_COPY; + } + else { + add_frame_mode = GP_GETFRAME_ADD_NEW; + } } else { - add_frame_mode = GP_GETFRAME_ADD_NEW; + add_frame_mode = GP_GETFRAME_USE_PREV; } bool need_tag = tgpi->gpl->actframe == NULL; diff --git a/source/blender/editors/gpencil/gpencil_sculpt_paint.c b/source/blender/editors/gpencil/gpencil_sculpt_paint.c index 0d3ab9011d6..9666aca5254 100644 --- a/source/blender/editors/gpencil/gpencil_sculpt_paint.c +++ b/source/blender/editors/gpencil/gpencil_sculpt_paint.c @@ -73,6 +73,7 @@ #include "UI_view2d.h" #include "ED_gpencil.h" +#include "ED_keyframing.h" #include "ED_screen.h" #include "ED_view3d.h" @@ -1019,7 +1020,11 @@ static void gpencil_brush_clone_add(bContext *C, tGP_BrushEditData *gso) if (gpl == NULL) { gpl = CTX_data_active_gpencil_layer(C); } - bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, CFRA, GP_GETFRAME_ADD_NEW); + bGPDframe *gpf = BKE_gpencil_layer_frame_get( + gpl, CFRA, IS_AUTOKEY_ON(scene) ? GP_GETFRAME_ADD_NEW : GP_GETFRAME_USE_PREV); + if (gpf == NULL) { + continue; + } /* Make a new stroke */ new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true); @@ -1334,6 +1339,10 @@ static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso /* go through each layer, and ensure that we've got a valid frame to use */ LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) { + if (!IS_AUTOKEY_ON(scene) && (gpl->actframe == NULL)) { + continue; + } + /* only editable and visible layers are considered */ if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) { bGPDframe *gpf = gpl->actframe; @@ -1343,7 +1352,7 @@ static void gpencil_sculpt_brush_init_stroke(bContext *C, tGP_BrushEditData *gso * - This is useful when animating as it saves that "uh-oh" moment when you realize you've * spent too much time editing the wrong frame. */ - if (gpf->framenum != cfra) { + if ((IS_AUTOKEY_ON(scene)) && (gpf->framenum != cfra)) { BKE_gpencil_frame_addcopy(gpl, cfra); /* Need tag to recalculate evaluated data to avoid crashes. */ DEG_id_tag_update(&gso->gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); diff --git a/source/blender/editors/transform/transform_convert_gpencil.c b/source/blender/editors/transform/transform_convert_gpencil.c index 244cd552495..45df0e66691 100644 --- a/source/blender/editors/transform/transform_convert_gpencil.c +++ b/source/blender/editors/transform/transform_convert_gpencil.c @@ -37,6 +37,7 @@ #include "BKE_gpencil_geom.h" #include "ED_gpencil.h" +#include "ED_keyframing.h" #include "transform.h" #include "transform_convert.h" @@ -114,6 +115,7 @@ static void createTransGPencil_curves(bContext *C, #define SEL_F3 (1 << 2) View3D *v3d = t->view; + Scene *scene = CTX_data_scene(C); const bool handle_only_selected_visible = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED); const bool handle_all_visible = (v3d->overlay.handle_display == CURVE_HANDLE_ALL); @@ -230,7 +232,9 @@ static void createTransGPencil_curves(bContext *C, } if ((gpf->framenum != cfra) && (!is_multiedit)) { - gpf = BKE_gpencil_frame_addcopy(gpl, cfra); + if (IS_AUTOKEY_ON(scene)) { + gpf = BKE_gpencil_frame_addcopy(gpl, cfra); + } /* in some weird situations (framelock enabled) return NULL */ if (gpf == NULL) { continue; @@ -405,6 +409,7 @@ static void createTransGPencil_strokes(bContext *C, const bool is_prop_edit_connected, const bool is_scale_thickness) { + Scene *scene = CTX_data_scene(C); TransData *td = NULL; float mtx[3][3], smtx[3][3]; @@ -517,7 +522,9 @@ static void createTransGPencil_strokes(bContext *C, * spent too much time editing the wrong frame... */ if ((gpf->framenum != cfra) && (!is_multiedit)) { - gpf = BKE_gpencil_frame_addcopy(gpl, cfra); + if (IS_AUTOKEY_ON(scene)) { + gpf = BKE_gpencil_frame_addcopy(gpl, cfra); + } /* in some weird situations (framelock enabled) return NULL */ if (gpf == NULL) { continue; -- cgit v1.2.3 From e0ba6a44114e3b8e2b463cd1de1e978fb92b32e9 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 5 Mar 2021 14:03:20 +1100 Subject: Cleanup: rename evt to event Following naming convention of most operators. --- source/blender/editors/animation/anim_channels_edit.c | 4 ++-- source/blender/editors/armature/pose_edit.c | 4 ++-- source/blender/editors/gpencil/gpencil_data.c | 2 +- source/blender/editors/object/object_edit.c | 4 ++-- source/blender/editors/space_action/action_data.c | 4 ++-- source/blender/editors/space_nla/nla_channels.c | 4 ++-- source/blender/editors/space_outliner/outliner_edit.c | 2 +- 7 files changed, 12 insertions(+), 12 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/animation/anim_channels_edit.c b/source/blender/editors/animation/anim_channels_edit.c index 38820e05869..711ec0a9d22 100644 --- a/source/blender/editors/animation/anim_channels_edit.c +++ b/source/blender/editors/animation/anim_channels_edit.c @@ -2544,7 +2544,7 @@ static bool animchannels_find_poll(bContext *C) } /* find_invoke() - Get initial channels */ -static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *evt) +static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent *event) { bAnimContext ac; @@ -2557,7 +2557,7 @@ static int animchannels_find_invoke(bContext *C, wmOperator *op, const wmEvent * RNA_string_set(op->ptr, "query", ac.ads->searchstr); /* defer to popup */ - return WM_operator_props_popup(C, op, evt); + return WM_operator_props_popup(C, op, event); } /* find_exec() - Called to set the value */ diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 78bce8679bb..e65871c0896 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -476,9 +476,9 @@ static int pose_clear_paths_exec(bContext *C, wmOperator *op) } /* operator callback/wrapper */ -static int pose_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *evt) +static int pose_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if ((evt->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { + if ((event->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { RNA_boolean_set(op->ptr, "only_selected", true); } return pose_clear_paths_exec(C, op); diff --git a/source/blender/editors/gpencil/gpencil_data.c b/source/blender/editors/gpencil/gpencil_data.c index 039bc50fcc9..fd2758c8a08 100644 --- a/source/blender/editors/gpencil/gpencil_data.c +++ b/source/blender/editors/gpencil/gpencil_data.c @@ -1420,7 +1420,7 @@ void GPENCIL_OT_layer_merge(wmOperatorType *ot) /* ********************** Change Layer ***************************** */ -static int gpencil_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +static int gpencil_layer_change_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { uiPopupMenu *pup; uiLayout *layout; diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index da14d4ef52a..c774bc9f9cc 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -1330,9 +1330,9 @@ static int object_clear_paths_exec(bContext *C, wmOperator *op) } /* operator callback/wrapper */ -static int object_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *evt) +static int object_clear_paths_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - if ((evt->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { + if ((event->shift) && !RNA_struct_property_is_set(op->ptr, "only_selected")) { RNA_boolean_set(op->ptr, "only_selected", true); } return object_clear_paths_exec(C, op); diff --git a/source/blender/editors/space_action/action_data.c b/source/blender/editors/space_action/action_data.c index 3a584a7f0cb..efa714e315d 100644 --- a/source/blender/editors/space_action/action_data.c +++ b/source/blender/editors/space_action/action_data.c @@ -663,11 +663,11 @@ static int action_unlink_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static int action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *evt) +static int action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *event) { /* NOTE: this is hardcoded to match the behavior for the unlink button * (in interface_templates.c). */ - RNA_boolean_set(op->ptr, "force_delete", evt->shift != 0); + RNA_boolean_set(op->ptr, "force_delete", event->shift != 0); return action_unlink_exec(C, op); } diff --git a/source/blender/editors/space_nla/nla_channels.c b/source/blender/editors/space_nla/nla_channels.c index fb297672f0f..f2cea23af76 100644 --- a/source/blender/editors/space_nla/nla_channels.c +++ b/source/blender/editors/space_nla/nla_channels.c @@ -594,11 +594,11 @@ static int nla_action_unlink_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } -static int nla_action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *evt) +static int nla_action_unlink_invoke(bContext *C, wmOperator *op, const wmEvent *event) { /* NOTE: this is hardcoded to match the behavior for the unlink button * (in interface_templates.c) */ - RNA_boolean_set(op->ptr, "force_delete", evt->shift != 0); + RNA_boolean_set(op->ptr, "force_delete", event->shift != 0); return nla_action_unlink_exec(C, op); } diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index d1260f02c67..09aa268d856 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -2267,7 +2267,7 @@ static bool ed_operator_outliner_id_orphans_active(bContext *C) /** \} */ -static int outliner_orphans_purge_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt)) +static int outliner_orphans_purge_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { Main *bmain = CTX_data_main(C); int num_tagged[INDEX_ID_MAX] = {0}; -- cgit v1.2.3 From aa8671024223368b0a4ef38c5229c270f50f2902 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 5 Mar 2021 14:33:38 +1100 Subject: Cleanup: spelling --- source/blender/editors/util/ed_draw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/util/ed_draw.c b/source/blender/editors/util/ed_draw.c index 94adba36664..d7b22b4f601 100644 --- a/source/blender/editors/util/ed_draw.c +++ b/source/blender/editors/util/ed_draw.c @@ -116,7 +116,7 @@ BLI_INLINE bool metadata_is_valid(ImBuf *ibuf, char *r_str, short index, int off BLI_INLINE bool metadata_is_custom_drawable(const char *field) { - /* Metadata field stored by Blender for multilayer EXR images. Is rather + /* Metadata field stored by Blender for multi-layer EXR images. Is rather * useless to be viewed all the time. Can still be seen in the Metadata * panel. */ if (STREQ(field, "BlenderMultiChannel")) { -- cgit v1.2.3 From 23dfcc5ac7edcc374c94a0b7317a7c410bf1fb41 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Fri, 5 Mar 2021 14:42:48 +1100 Subject: Cleanup: rename event to event_type Reserve `event` for wmEvent. --- source/blender/editors/armature/pose_lib.c | 4 ++-- source/blender/editors/interface/interface_handlers.c | 8 ++++---- source/blender/editors/interface/interface_intern.h | 4 ++-- .../blender/editors/interface/interface_region_menu_pie.c | 14 +++++++------- 4 files changed, 15 insertions(+), 15 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/pose_lib.c b/source/blender/editors/armature/pose_lib.c index 45f623f3a9d..dd90f9f2cc3 100644 --- a/source/blender/editors/armature/pose_lib.c +++ b/source/blender/editors/armature/pose_lib.c @@ -1321,10 +1321,10 @@ static void poselib_preview_get_next(tPoseLib_PreviewData *pld, int step) } /* specially handle events for searching */ -static void poselib_preview_handle_search(tPoseLib_PreviewData *pld, ushort event, char ascii) +static void poselib_preview_handle_search(tPoseLib_PreviewData *pld, ushort event_type, char ascii) { /* try doing some form of string manipulation first */ - switch (event) { + switch (event_type) { case EVT_BACKSPACEKEY: if (pld->searchstr[0] && pld->search_cursor) { short len = strlen(pld->searchstr); diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index f6d8d596853..042f10ddded 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -10446,7 +10446,7 @@ static int ui_pie_handler(bContext *C, const wmEvent *event, uiPopupBlockHandle } } - if (event->type == block->pie_data.event && !is_click_style) { + if (event->type == block->pie_data.event_type && !is_click_style) { if (event->val != KM_RELEASE) { ui_handle_menu_button(C, event, menu); @@ -10620,7 +10620,7 @@ static int ui_handle_menus_recursive(bContext *C, /* root pie menus accept the key that spawned * them as double click to improve responsiveness */ const bool do_recursion = (!(block->flag & UI_BLOCK_RADIAL) || - event->type != block->pie_data.event); + event->type != block->pie_data.event_type); if (do_recursion) { if (is_parent_inside == false) { @@ -10913,7 +10913,7 @@ static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) /* set last pie event to allow chained pie spawning */ if (block->flag & UI_BLOCK_RADIAL) { - win->last_pie_event = block->pie_data.event; + win->pie_event_type_last = block->pie_data.event_type; reset_pie = true; } @@ -10956,7 +10956,7 @@ static int ui_popup_handler(bContext *C, const wmEvent *event, void *userdata) wmWindow *win = CTX_wm_window(C); if (win) { - win->last_pie_event = EVENT_NONE; + win->pie_event_type_last = EVENT_NONE; } } diff --git a/source/blender/editors/interface/interface_intern.h b/source/blender/editors/interface/interface_intern.h index c39e2b3ff8a..1d4a44e0c76 100644 --- a/source/blender/editors/interface/interface_intern.h +++ b/source/blender/editors/interface/interface_intern.h @@ -424,8 +424,8 @@ struct PieMenuData { float last_pos[2]; double duration_gesture; int flags; - /** initial event used to fire the pie menu, store here so we can query for release */ - int event; + /** Initial event used to fire the pie menu, store here so we can query for release */ + short event_type; float alphafac; }; diff --git a/source/blender/editors/interface/interface_region_menu_pie.c b/source/blender/editors/interface/interface_region_menu_pie.c index 81c627816b9..05aa139e055 100644 --- a/source/blender/editors/interface/interface_region_menu_pie.c +++ b/source/blender/editors/interface/interface_region_menu_pie.c @@ -122,26 +122,26 @@ uiPieMenu *UI_pie_menu_begin(struct bContext *C, const char *title, int icon, co * it is always assumed to be click style */ if (event->type == LEFTMOUSE || ELEM(event->val, KM_RELEASE, KM_CLICK)) { pie->block_radial->pie_data.flags |= UI_PIE_CLICK_STYLE; - pie->block_radial->pie_data.event = EVENT_NONE; - win->lock_pie_event = EVENT_NONE; + pie->block_radial->pie_data.event_type = EVENT_NONE; + win->pie_event_type_lock = EVENT_NONE; } else { - if (win->last_pie_event != EVENT_NONE) { + if (win->pie_event_type_last != EVENT_NONE) { /* original pie key has been released, so don't propagate the event */ - if (win->lock_pie_event == EVENT_NONE) { + if (win->pie_event_type_lock == EVENT_NONE) { event_type = EVENT_NONE; pie->block_radial->pie_data.flags |= UI_PIE_CLICK_STYLE; } else { - event_type = win->last_pie_event; + event_type = win->pie_event_type_last; } } else { event_type = event->type; } - pie->block_radial->pie_data.event = event_type; - win->lock_pie_event = event_type; + pie->block_radial->pie_data.event_type = event_type; + win->pie_event_type_lock = event_type; } pie->layout = UI_block_layout( -- cgit v1.2.3 From 99e186671247b7dbcb01e9c95709a251fdbefc87 Mon Sep 17 00:00:00 2001 From: Antonio Vazquez Date: Fri, 5 Mar 2021 10:36:57 +0100 Subject: Cleanup: Use boolean in WM_cursor_wait --- source/blender/editors/armature/armature_relations.c | 4 ++-- source/blender/editors/curve/editcurve.c | 4 ++-- source/blender/editors/gpencil/gpencil_mesh.c | 4 ++-- source/blender/editors/mesh/editmesh_tools.c | 4 ++-- source/blender/editors/object/object_bake.c | 2 +- source/blender/editors/object/object_bake_api.c | 2 +- source/blender/editors/object/object_relations.c | 8 ++++---- source/blender/editors/render/render_internal.c | 4 ++-- source/blender/editors/render/render_shading.c | 2 +- source/blender/editors/space_clip/tracking_ops_solve.c | 2 +- source/blender/editors/space_clip/tracking_ops_track.c | 2 +- source/blender/editors/space_image/image_ops.c | 4 ++-- 12 files changed, 21 insertions(+), 21 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/armature/armature_relations.c b/source/blender/editors/armature/armature_relations.c index 4dbe448c4ec..66ca38ce218 100644 --- a/source/blender/editors/armature/armature_relations.c +++ b/source/blender/editors/armature/armature_relations.c @@ -624,7 +624,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op) bool ok = false; /* set wait cursor in case this takes a while */ - WM_cursor_wait(1); + WM_cursor_wait(true); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( @@ -709,7 +709,7 @@ static int separate_armature_exec(bContext *C, wmOperator *op) MEM_freeN(bases); /* Recalculate/redraw + cleanup */ - WM_cursor_wait(0); + WM_cursor_wait(false); if (ok) { BKE_report(op->reports, RPT_INFO, "Separated bones"); diff --git a/source/blender/editors/curve/editcurve.c b/source/blender/editors/curve/editcurve.c index 0593cedb5a1..33ef6a5d026 100644 --- a/source/blender/editors/curve/editcurve.c +++ b/source/blender/editors/curve/editcurve.c @@ -1359,7 +1359,7 @@ static int separate_exec(bContext *C, wmOperator *op) int error_generic; } status = {0}; - WM_cursor_wait(1); + WM_cursor_wait(true); uint bases_len = 0; Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data( @@ -1426,7 +1426,7 @@ static int separate_exec(bContext *C, wmOperator *op) status.changed++; } MEM_freeN(bases); - WM_cursor_wait(0); + WM_cursor_wait(false); if (status.unselected == bases_len) { BKE_report(op->reports, RPT_ERROR, "No point was selected"); diff --git a/source/blender/editors/gpencil/gpencil_mesh.c b/source/blender/editors/gpencil/gpencil_mesh.c index 7e6b42f284b..b7ed77801c0 100644 --- a/source/blender/editors/gpencil/gpencil_mesh.c +++ b/source/blender/editors/gpencil/gpencil_mesh.c @@ -251,7 +251,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) gpd->draw_mode = (project_type == GP_REPROJECT_KEEP) ? GP_DRAWMODE_3D : GP_DRAWMODE_2D; /* Set cursor to indicate working. */ - WM_cursor_wait(1); + WM_cursor_wait(true); GP_SpaceConversion gsc = {NULL}; SnapObjectContext *sctx = NULL; @@ -385,7 +385,7 @@ static int gpencil_bake_mesh_animation_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); /* Reset cursor. */ - WM_cursor_wait(0); + WM_cursor_wait(false); /* done */ return OPERATOR_FINISHED; diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index 7502b77ae02..68ac2842bab 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -1967,9 +1967,9 @@ static int edbm_duplicate_exec(bContext *C, wmOperator *op) static int edbm_duplicate_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) { - WM_cursor_wait(1); + WM_cursor_wait(true); edbm_duplicate_exec(C, op); - WM_cursor_wait(0); + WM_cursor_wait(false); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_bake.c b/source/blender/editors/object/object_bake.c index 9618774eea8..a5cad4e087c 100644 --- a/source/blender/editors/object/object_bake.c +++ b/source/blender/editors/object/object_bake.c @@ -572,7 +572,7 @@ static int multiresbake_image_exec(bContext *C, wmOperator *op) G.is_break = false; WM_jobs_start(CTX_wm_manager(C), wm_job); - WM_cursor_wait(0); + WM_cursor_wait(false); /* add modal handler for ESC */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/object/object_bake_api.c b/source/blender/editors/object/object_bake_api.c index 9ec0c625f71..610551e8539 100644 --- a/source/blender/editors/object/object_bake_api.c +++ b/source/blender/editors/object/object_bake_api.c @@ -1897,7 +1897,7 @@ static int bake_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event) WM_jobs_start(CTX_wm_manager(C), wm_job); - WM_cursor_wait(0); + WM_cursor_wait(false); /* add modal handler for ESC */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/object/object_relations.c b/source/blender/editors/object/object_relations.c index 5a2ef1c6556..e74d04ab17f 100644 --- a/source/blender/editors/object/object_relations.c +++ b/source/blender/editors/object/object_relations.c @@ -893,10 +893,10 @@ bool ED_object_parent_set(ReportList *reports, reports, depsgraph, scene, ob, par, ARM_GROUPS_ENVELOPE, xmirror); } else if (partype == PAR_ARMATURE_AUTO) { - WM_cursor_wait(1); + WM_cursor_wait(true); ED_object_vgroup_calc_from_armature( reports, depsgraph, scene, ob, par, ARM_GROUPS_AUTO, xmirror); - WM_cursor_wait(0); + WM_cursor_wait(false); } /* get corrected inverse */ ob->partype = PAROBJECT; @@ -912,9 +912,9 @@ bool ED_object_parent_set(ReportList *reports, ED_gpencil_add_armature_weights(C, reports, ob, par, GP_PAR_ARMATURE_NAME); } else if (ELEM(partype, PAR_ARMATURE_AUTO, PAR_ARMATURE_ENVELOPE)) { - WM_cursor_wait(1); + WM_cursor_wait(true); ED_gpencil_add_armature_weights(C, reports, ob, par, GP_PAR_ARMATURE_AUTO); - WM_cursor_wait(0); + WM_cursor_wait(false); } /* get corrected inverse */ ob->partype = PAROBJECT; diff --git a/source/blender/editors/render/render_internal.c b/source/blender/editors/render/render_internal.c index b525d8a373e..1f59e361526 100644 --- a/source/blender/editors/render/render_internal.c +++ b/source/blender/editors/render/render_internal.c @@ -938,7 +938,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even } /* handle UI stuff */ - WM_cursor_wait(1); + WM_cursor_wait(true); /* flush sculpt and editmode changes */ ED_editors_flush_edits_ex(bmain, true, false); @@ -1058,7 +1058,7 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even WM_jobs_start(CTX_wm_manager(C), wm_job); - WM_cursor_wait(0); + WM_cursor_wait(false); WM_event_add_notifier(C, NC_SCENE | ND_RENDER_RESULT, scene); /* we set G.is_rendering here already instead of only in the job, this ensure diff --git a/source/blender/editors/render/render_shading.c b/source/blender/editors/render/render_shading.c index 32d4abcabd4..bfad79a1da9 100644 --- a/source/blender/editors/render/render_shading.c +++ b/source/blender/editors/render/render_shading.c @@ -1224,7 +1224,7 @@ static int light_cache_bake_invoke(bContext *C, wmOperator *op, const wmEvent *U WM_jobs_start(wm, wm_job); - WM_cursor_wait(0); + WM_cursor_wait(false); return OPERATOR_RUNNING_MODAL; } diff --git a/source/blender/editors/space_clip/tracking_ops_solve.c b/source/blender/editors/space_clip/tracking_ops_solve.c index b65dc909d5f..96504651e44 100644 --- a/source/blender/editors/space_clip/tracking_ops_solve.c +++ b/source/blender/editors/space_clip/tracking_ops_solve.c @@ -244,7 +244,7 @@ static int solve_camera_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE G.is_break = false; WM_jobs_start(CTX_wm_manager(C), wm_job); - WM_cursor_wait(0); + WM_cursor_wait(false); /* add modal handler for ESC */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/space_clip/tracking_ops_track.c b/source/blender/editors/space_clip/tracking_ops_track.c index e480ec2db05..9882304d97d 100644 --- a/source/blender/editors/space_clip/tracking_ops_track.c +++ b/source/blender/editors/space_clip/tracking_ops_track.c @@ -356,7 +356,7 @@ static int track_markers(bContext *C, wmOperator *op, bool use_job) G.is_break = false; WM_jobs_start(CTX_wm_manager(C), wm_job); - WM_cursor_wait(0); + WM_cursor_wait(false); /* Add modal handler for ESC. */ WM_event_add_modal_handler(C, op); diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index 4008ca228ac..fc3619f01b9 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -1816,11 +1816,11 @@ static bool save_image_op( opts->save_as_render = (RNA_struct_find_property(op->ptr, "save_as_render") && RNA_boolean_get(op->ptr, "save_as_render")); - WM_cursor_wait(1); + WM_cursor_wait(true); bool ok = BKE_image_save(op->reports, bmain, ima, iuser, opts); - WM_cursor_wait(0); + WM_cursor_wait(false); /* Remember file path for next save. */ BLI_strncpy(G.ima, opts->filepath, sizeof(G.ima)); -- cgit v1.2.3 From 6bc01222c63b235e60578966a486a920ca6ce495 Mon Sep 17 00:00:00 2001 From: Jeroen Bakker Date: Fri, 5 Mar 2021 09:47:50 +0100 Subject: Cleanup: Sync header+implementaiton definition. Gave warning on Windows platform. There are more of these cases. --- source/blender/editors/include/ED_object.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 5dfce6071f0..0767ce21382 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -163,8 +163,8 @@ extern struct EnumPropertyItem prop_make_parent_types[]; bool ED_object_parent_set(struct ReportList *reports, const struct bContext *C, struct Scene *scene, - struct Object *ob, - struct Object *par, + struct Object *const ob, + struct Object *const par, int partype, const bool xmirror, const bool keep_transform, -- cgit v1.2.3 From e0442a955badbbf7ee6f280bb988054175802bc1 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 5 Mar 2021 12:28:15 +0100 Subject: UI Code Quality: Port Outliner Grease Pencil layers to new design Continuation of work in 2e221de4ceee and 249e4df110e0. Now the tree-element types have to be ported one by one. This is probably the most straight forward type to port. --- .../blender/editors/space_outliner/CMakeLists.txt | 2 ++ .../blender/editors/space_outliner/outliner_tree.c | 14 ++++---- .../editors/space_outliner/tree/tree_element.cc | 3 ++ .../tree/tree_element_gpencil_layer.cc | 40 ++++++++++++++++++++++ .../tree/tree_element_gpencil_layer.hh | 34 ++++++++++++++++++ 5 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.cc create mode 100644 source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.hh (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index d54265aa292..040410c0bdd 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -57,6 +57,7 @@ set(SRC tree/tree_element.cc tree/tree_element_anim_data.cc tree/tree_element_driver_base.cc + tree/tree_element_gpencil_layer.cc tree/tree_element_nla.cc outliner_intern.h @@ -66,6 +67,7 @@ set(SRC tree/tree_element.hh tree/tree_element_anim_data.hh tree/tree_element_driver_base.hh + tree/tree_element_gpencil_layer.hh tree/tree_element_nla.hh ) diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index f94f19246fa..0d82ba992c2 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1016,16 +1016,16 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, outliner_add_id_contents(space_outliner, te, tselem, id); } } - else if (ELEM(type, TSE_ANIM_DATA, TSE_DRIVER_BASE, TSE_NLA, TSE_NLA_ACTION, TSE_NLA_TRACK)) { + else if (ELEM(type, + TSE_ANIM_DATA, + TSE_DRIVER_BASE, + TSE_NLA, + TSE_NLA_ACTION, + TSE_NLA_TRACK, + TSE_GP_LAYER)) { /* Should already use new AbstractTreeElement design. */ BLI_assert(0); } - else if (type == TSE_GP_LAYER) { - bGPDlayer *gpl = (bGPDlayer *)idv; - - te->name = gpl->info; - te->directdata = gpl; - } else if (type == TSE_SEQUENCE) { Sequence *seq = (Sequence *)idv; diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index 27846614994..a692aae37c3 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -22,6 +22,7 @@ #include "tree_element_anim_data.hh" #include "tree_element_driver_base.hh" +#include "tree_element_gpencil_layer.hh" #include "tree_element_nla.hh" #include "tree_element.h" @@ -46,6 +47,8 @@ static AbstractTreeElement *tree_element_create(int type, TreeElement &legacy_te return new TreeElementNLATrack(legacy_te, *static_cast(idv)); case TSE_NLA_ACTION: return new TreeElementNLAAction(legacy_te); + case TSE_GP_LAYER: + return new TreeElementGPencilLayer(legacy_te, *static_cast(idv)); default: break; } diff --git a/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.cc b/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.cc new file mode 100644 index 00000000000..91e6fdcde4b --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.cc @@ -0,0 +1,40 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup spoutliner + */ + +#include "BLI_utildefines.h" + +#include "DNA_gpencil_types.h" + +#include "../outliner_intern.h" + +#include "tree_element_gpencil_layer.hh" + +namespace blender::ed::outliner { + +TreeElementGPencilLayer::TreeElementGPencilLayer(TreeElement &legacy_te, bGPDlayer &gplayer) + : AbstractTreeElement(legacy_te) +{ + BLI_assert(legacy_te.store_elem->type == TSE_GP_LAYER); + /* this element's info */ + legacy_te.name = gplayer.info; + legacy_te.directdata = &gplayer; +} + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.hh b/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.hh new file mode 100644 index 00000000000..dd18dd42344 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.hh @@ -0,0 +1,34 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup spoutliner + */ + +#pragma once + +#include "tree_element.hh" + +struct bGPDlayer; + +namespace blender::ed::outliner { + +class TreeElementGPencilLayer : public AbstractTreeElement { + public: + TreeElementGPencilLayer(TreeElement &legacy_te, bGPDlayer &gplayer); +}; + +} // namespace blender::ed::outliner -- cgit v1.2.3 From fe35551df2d874d51073b1dc4a582a1962255949 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sybren=20A=2E=20St=C3=BCvel?= Date: Fri, 5 Mar 2021 15:00:56 +0100 Subject: Asset Browser Space API: add `activate_asset_by_id()` function Add an RNA function `activate_asset_by_id(asset_id: ID, deferred: bool)` to the File Browser space type, which intended to be used to activate an asset's entry as identified by its `ID *`. Calling it changes the active asset, but only if the given ID can actually be found. The activation can be deferred (by passing `deferred=True`) until the next refresh operation has finished. This is necessary when an asset has just been added, as it will be loaded by the filebrowser in a background job. Reviewed By: Severin Differential Revision: https://developer.blender.org/D10549 --- source/blender/editors/include/ED_fileselect.h | 7 +++ source/blender/editors/space_file/file_intern.h | 15 +++++++ source/blender/editors/space_file/filelist.c | 11 ++--- source/blender/editors/space_file/filelist.h | 2 + source/blender/editors/space_file/filesel.c | 60 +++++++++++++++++++++++++ source/blender/editors/space_file/space_file.c | 51 ++++++++++++++++++--- 6 files changed, 135 insertions(+), 11 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/include/ED_fileselect.h b/source/blender/editors/include/ED_fileselect.h index 7538dac1354..983ae94b637 100644 --- a/source/blender/editors/include/ED_fileselect.h +++ b/source/blender/editors/include/ED_fileselect.h @@ -145,6 +145,13 @@ void ED_fileselect_exit(struct wmWindowManager *wm, struct SpaceFile *sfile); bool ED_fileselect_is_asset_browser(const struct SpaceFile *sfile); +struct ID *ED_fileselect_active_asset_get(const struct SpaceFile *sfile); + +/* Activate the file that corresponds to the given ID. + * Pass deferred=true to wait for the next refresh before activating. */ +void ED_fileselect_activate_by_id(struct SpaceFile *sfile, + struct ID *asset_id, + const bool deferred); void ED_fileselect_window_params_get(const struct wmWindow *win, int win_size[2], diff --git a/source/blender/editors/space_file/file_intern.h b/source/blender/editors/space_file/file_intern.h index 56fb588776e..deb32812f44 100644 --- a/source/blender/editors/space_file/file_intern.h +++ b/source/blender/editors/space_file/file_intern.h @@ -112,6 +112,21 @@ int autocomplete_file(struct bContext *C, char *str, void *arg_v); void file_params_renamefile_activate(struct SpaceFile *sfile, struct FileSelectParams *params); +typedef void *onReloadFnData; +typedef void (*onReloadFn)(struct SpaceFile *space_data, onReloadFnData custom_data); +typedef struct SpaceFile_Runtime { + /* Called once after the file browser has reloaded. Reset to NULL after calling. + * Use file_on_reload_callback_register() to register a callback. */ + onReloadFn on_reload; + onReloadFnData on_reload_custom_data; +} SpaceFile_Runtime; + +/* Register an on-reload callback function. Note that there can only be one such function at a + * time; registering a new one will overwrite the previous one. */ +void file_on_reload_callback_register(struct SpaceFile *sfile, + onReloadFn callback, + onReloadFnData custom_data); + /* file_panels.c */ void file_tool_props_region_panels_register(struct ARegionType *art); void file_execute_region_panels_register(struct ARegionType *art); diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 33c37875372..518fc777c67 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -1990,9 +1990,7 @@ static void filelist_file_release_entry(FileList *filelist, FileDirEntry *entry) filelist_entry_free(entry); } -static FileDirEntry *filelist_file_ex(struct FileList *filelist, - const int index, - const bool use_request) +FileDirEntry *filelist_file_ex(struct FileList *filelist, const int index, const bool use_request) { FileDirEntry *ret = NULL, *old; FileListEntryCache *cache = &filelist->filelist_cache; @@ -3464,7 +3462,7 @@ void filelist_readjob_start(FileList *filelist, const bContext *C) filelist_readjob_endjob(flrj); filelist_readjob_free(flrj); - WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST, NULL); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED, NULL); return; } @@ -3476,7 +3474,10 @@ void filelist_readjob_start(FileList *filelist, const bContext *C) WM_JOB_PROGRESS, WM_JOB_TYPE_FILESEL_READDIR); WM_jobs_customdata_set(wm_job, flrj, filelist_readjob_free); - WM_jobs_timer(wm_job, 0.01, NC_SPACE | ND_SPACE_FILE_LIST, NC_SPACE | ND_SPACE_FILE_LIST); + WM_jobs_timer(wm_job, + 0.01, + NC_SPACE | ND_SPACE_FILE_LIST, + NC_SPACE | ND_SPACE_FILE_LIST | NA_JOB_FINISHED); WM_jobs_callbacks( wm_job, filelist_readjob_startjob, NULL, filelist_readjob_update, filelist_readjob_endjob); diff --git a/source/blender/editors/space_file/filelist.h b/source/blender/editors/space_file/filelist.h index 16984bb6e43..7eecd7a05de 100644 --- a/source/blender/editors/space_file/filelist.h +++ b/source/blender/editors/space_file/filelist.h @@ -93,6 +93,8 @@ void filelist_setdir(struct FileList *filelist, char *r_dir); int filelist_files_ensure(struct FileList *filelist); int filelist_needs_reading(struct FileList *filelist); FileDirEntry *filelist_file(struct FileList *filelist, int index); +FileDirEntry *filelist_file_ex(struct FileList *filelist, int index, bool use_request); + int filelist_file_findpath(struct FileList *filelist, const char *file); struct ID *filelist_file_get_id(const struct FileDirEntry *file); FileDirEntry *filelist_entry_find_uuid(struct FileList *filelist, const int uuid[4]); diff --git a/source/blender/editors/space_file/filesel.c b/source/blender/editors/space_file/filesel.c index 6917893ab5f..7015ca970a3 100644 --- a/source/blender/editors/space_file/filesel.c +++ b/source/blender/editors/space_file/filesel.c @@ -454,6 +454,66 @@ bool ED_fileselect_is_asset_browser(const SpaceFile *sfile) return (sfile->browse_mode == FILE_BROWSE_MODE_ASSETS); } +struct ID *ED_fileselect_active_asset_get(const SpaceFile *sfile) +{ + if (!ED_fileselect_is_asset_browser(sfile)) { + return NULL; + } + + FileSelectParams *params = ED_fileselect_get_active_params(sfile); + const FileDirEntry *file = filelist_file(sfile->files, params->active_file); + if (file == NULL) { + return NULL; + } + + return filelist_file_get_id(file); +} + +static void on_reload_activate_by_id(SpaceFile *sfile, onReloadFnData custom_data) +{ + ID *asset_id = (ID *)custom_data; + ED_fileselect_activate_by_id(sfile, asset_id, false); +} + +void ED_fileselect_activate_by_id(SpaceFile *sfile, ID *asset_id, const bool deferred) +{ + if (!ED_fileselect_is_asset_browser(sfile)) { + return; + } + + /* If there are filelist operations running now ("pending" true) or soon ("force reset" true), + * there is a fair chance that the to-be-activated ID will only be present after these operations + * have completed. Defer activation until then. */ + if (deferred || filelist_pending(sfile->files) || filelist_needs_force_reset(sfile->files)) { + /* This should be thread-safe, as this function is likely called from the main thread, and + * notifiers (which cause a call to the on-reload callback function) are handled on the main + * thread as well. */ + file_on_reload_callback_register(sfile, on_reload_activate_by_id, asset_id); + return; + } + + FileSelectParams *params = ED_fileselect_get_active_params(sfile); + struct FileList *files = sfile->files; + + const int num_files_filtered = filelist_files_ensure(files); + for (int file_index = 0; file_index < num_files_filtered; ++file_index) { + const FileDirEntry *file = filelist_file_ex(files, file_index, false); + + if (filelist_file_get_id(file) != asset_id) { + filelist_entry_select_set(files, file, FILE_SEL_REMOVE, FILE_SEL_SELECTED, CHECK_ALL); + continue; + } + + params->active_file = file_index; + filelist_entry_select_set(files, file, FILE_SEL_ADD, FILE_SEL_SELECTED, CHECK_ALL); + + /* Keep looping to deselect the other files. */ + } + + WM_main_add_notifier(NC_ASSET | NA_ACTIVATED, NULL); + WM_main_add_notifier(NC_ASSET | NA_SELECTED, NULL); +} + /* The subset of FileSelectParams.flag items we store into preferences. Note that FILE_SORT_ALPHA * may also be remembered, but only conditionally. */ #define PARAMS_FLAGS_REMEMBERED (FILE_HIDE_DOT) diff --git a/source/blender/editors/space_file/space_file.c b/source/blender/editors/space_file/space_file.c index b175844a710..2c9c2688e88 100644 --- a/source/blender/editors/space_file/space_file.c +++ b/source/blender/editors/space_file/space_file.c @@ -173,6 +173,7 @@ static void file_free(SpaceLink *sl) MEM_SAFE_FREE(sfile->params); MEM_SAFE_FREE(sfile->asset_params); + MEM_SAFE_FREE(sfile->runtime); if (sfile->layout) { MEM_freeN(sfile->layout); @@ -188,6 +189,10 @@ static void file_init(wmWindowManager *UNUSED(wm), ScrArea *area) if (sfile->layout) { sfile->layout->dirty = true; } + + if (sfile->runtime == NULL) { + sfile->runtime = MEM_callocN(sizeof(*sfile->runtime), __func__); + } } static void file_exit(wmWindowManager *wm, ScrArea *area) @@ -209,6 +214,7 @@ static SpaceLink *file_duplicate(SpaceLink *sl) /* clear or remove stuff from old */ sfilen->op = NULL; /* file window doesn't own operators */ + sfilen->runtime = NULL; sfilen->previews_timer = NULL; sfilen->smoothscroll_timer = NULL; @@ -392,6 +398,26 @@ static void file_refresh(const bContext *C, ScrArea *area) ED_area_tag_redraw(area); } +void file_on_reload_callback_register(SpaceFile *sfile, + onReloadFn callback, + onReloadFnData custom_data) +{ + sfile->runtime->on_reload = callback; + sfile->runtime->on_reload_custom_data = custom_data; +} + +static void file_on_reload_callback_call(SpaceFile *sfile) +{ + if (sfile->runtime->on_reload == NULL) { + return; + } + + sfile->runtime->on_reload(sfile, sfile->runtime->on_reload_custom_data); + + sfile->runtime->on_reload = NULL; + sfile->runtime->on_reload_custom_data = NULL; +} + static void file_listener(const wmSpaceTypeListenerParams *params) { ScrArea *area = params->area; @@ -419,12 +445,26 @@ static void file_listener(const wmSpaceTypeListenerParams *params) } break; } + switch (wmn->action) { + case NA_JOB_FINISHED: + file_on_reload_callback_call(sfile); + break; + } break; case NC_ASSET: { - if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) { - /* Full refresh of the file list if local asset data was changed. Refreshing this view is - * cheap and users expect this to be updated immediately. */ - file_tag_reset_list(area, sfile); + switch (wmn->action) { + case NA_SELECTED: + case NA_ACTIVATED: + ED_area_tag_refresh(area); + break; + case NA_ADDED: + case NA_REMOVED: + if (sfile->files && filelist_needs_reset_on_main_changes(sfile->files)) { + /* Full refresh of the file list if local asset data was changed. Refreshing this view + * is cheap and users expect this to be updated immediately. */ + file_tag_reset_list(area, sfile); + } + break; } break; } @@ -464,8 +504,7 @@ static void file_main_region_listener(const wmRegionListenerParams *params) } break; case NC_ID: - if (ELEM(wmn->action, NA_RENAME)) { - /* In case the filelist shows ID names. */ + if (ELEM(wmn->action, NA_SELECTED, NA_ACTIVATED, NA_RENAME)) { ED_region_tag_redraw(region); } break; -- cgit v1.2.3 From ed84161529527274852d5665f93a7d8b7cd1be9c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebastia=CC=81n=20Barschkis?= Date: Fri, 5 Mar 2021 17:35:25 +0100 Subject: Cleanup: Rename func occurences to _fn Use _fn as a suffix for callbacks. --- source/blender/editors/space_file/filelist.c | 50 ++++++++++++++-------------- 1 file changed, 25 insertions(+), 25 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_file/filelist.c b/source/blender/editors/space_file/filelist.c index 518fc777c67..757ec7c741f 100644 --- a/source/blender/editors/space_file/filelist.c +++ b/source/blender/editors/space_file/filelist.c @@ -410,14 +410,14 @@ typedef struct FileList { /* Set given path as root directory, * if last bool is true may change given string in place to a valid value. * Returns True if valid dir. */ - bool (*checkdirf)(struct FileList *, char *, const bool); + bool (*check_dir_fn)(struct FileList *, char *, const bool); /* Fill filelist (to be called by read job). */ - void (*read_jobf)( + void (*read_job_fn)( Main *, struct FileList *, const char *, short *, short *, float *, ThreadMutex *); /* Filter an entry of current filelist. */ - bool (*filterf)(struct FileListInternEntry *, const char *, FileListFilter *); + bool (*filter_fn)(struct FileListInternEntry *, const char *, FileListFilter *); short tags; /* FileListTags */ } FileList; @@ -963,7 +963,7 @@ void filelist_filter(FileList *filelist) /* Filter remap & count how many files are left after filter in a single loop. */ for (file = filelist->filelist_intern.entries.first; file; file = file->next) { - if (filelist->filterf(file, filelist->filelist.root, &filelist->filter_data)) { + if (filelist->filter_fn(file, filelist->filelist.root, &filelist->filter_data)) { filtered_tmp[num_filtered++] = file; } } @@ -1742,25 +1742,25 @@ void filelist_settype(FileList *filelist, short type) filelist->tags = 0; switch (filelist->type) { case FILE_MAIN: - filelist->checkdirf = filelist_checkdir_main; - filelist->read_jobf = filelist_readjob_main; - filelist->filterf = is_filtered_main; + filelist->check_dir_fn = filelist_checkdir_main; + filelist->read_job_fn = filelist_readjob_main; + filelist->filter_fn = is_filtered_main; break; case FILE_LOADLIB: - filelist->checkdirf = filelist_checkdir_lib; - filelist->read_jobf = filelist_readjob_lib; - filelist->filterf = is_filtered_lib; + filelist->check_dir_fn = filelist_checkdir_lib; + filelist->read_job_fn = filelist_readjob_lib; + filelist->filter_fn = is_filtered_lib; break; case FILE_MAIN_ASSET: - filelist->checkdirf = filelist_checkdir_main_assets; - filelist->read_jobf = filelist_readjob_main_assets; - filelist->filterf = is_filtered_main_assets; + filelist->check_dir_fn = filelist_checkdir_main_assets; + filelist->read_job_fn = filelist_readjob_main_assets; + filelist->filter_fn = is_filtered_main_assets; filelist->tags |= FILELIST_TAGS_USES_MAIN_DATA | FILELIST_TAGS_NO_THREADS; break; default: - filelist->checkdirf = filelist_checkdir_dir; - filelist->read_jobf = filelist_readjob_dir; - filelist->filterf = is_filtered_file; + filelist->check_dir_fn = filelist_checkdir_dir; + filelist->read_job_fn = filelist_readjob_dir; + filelist->filter_fn = is_filtered_file; break; } @@ -1867,7 +1867,7 @@ const char *filelist_dir(struct FileList *filelist) bool filelist_is_dir(struct FileList *filelist, const char *path) { - return filelist->checkdirf(filelist, (char *)path, false); + return filelist->check_dir_fn(filelist, (char *)path, false); } /** @@ -1879,7 +1879,7 @@ void filelist_setdir(struct FileList *filelist, char *r_dir) BLI_assert(strlen(r_dir) < FILE_MAX_LIBEXTRA); BLI_path_normalize_dir(BKE_main_blendfile_path_from_global(), r_dir); - const bool is_valid_path = filelist->checkdirf(filelist, r_dir, !allow_invalid); + const bool is_valid_path = filelist->check_dir_fn(filelist, r_dir, !allow_invalid); BLI_assert(is_valid_path || allow_invalid); UNUSED_VARS_NDEBUG(is_valid_path); @@ -3356,13 +3356,13 @@ static void filelist_readjob_startjob(void *flrjv, short *stop, short *do_update BLI_mutex_unlock(&flrj->lock); - flrj->tmp_filelist->read_jobf(flrj->current_main, - flrj->tmp_filelist, - flrj->main_name, - stop, - do_update, - progress, - &flrj->lock); + flrj->tmp_filelist->read_job_fn(flrj->current_main, + flrj->tmp_filelist, + flrj->main_name, + stop, + do_update, + progress, + &flrj->lock); } static void filelist_readjob_update(void *flrjv) -- cgit v1.2.3 From b9e54566e3b1a49d9757680da64d8e19c136c706 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 5 Mar 2021 17:38:53 +0100 Subject: Cleanup: Add & use enum value for ID Outliner element type Code to check if the Outliner tree-element type was the general ID one would always check against "0" (explicity or even implicitly). For somebody unfamiliar with the code this is very confusing. Instead the value should be given a name, e.g. through an enum. Adds `TSE_SOME_ID` as the "default" ID tree-element type. Other types may still represent IDs, as I explained in a comment at the definition. There may also still be cases where the type is checked against "0". I noted in the comment that such cases should be cleaned up if found. --- .../editors/space_outliner/outliner_collections.c | 10 ++--- .../editors/space_outliner/outliner_context.c | 2 +- .../editors/space_outliner/outliner_dragdrop.c | 8 ++-- .../blender/editors/space_outliner/outliner_draw.c | 35 +++++++-------- .../blender/editors/space_outliner/outliner_edit.c | 8 ++-- .../editors/space_outliner/outliner_select.c | 16 +++---- .../blender/editors/space_outliner/outliner_sync.c | 4 +- .../editors/space_outliner/outliner_tools.c | 9 ++-- .../blender/editors/space_outliner/outliner_tree.c | 51 ++++++++++++---------- .../editors/space_outliner/outliner_utils.c | 8 ++-- .../space_outliner/tree/tree_display_libraries.cc | 4 +- .../space_outliner/tree/tree_display_orphaned.cc | 3 +- .../space_outliner/tree/tree_display_scenes.cc | 3 +- .../space_outliner/tree/tree_display_view_layer.cc | 14 +++--- .../space_outliner/tree/tree_element_anim_data.cc | 3 +- 15 files changed, 95 insertions(+), 83 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_outliner/outliner_collections.c b/source/blender/editors/space_outliner/outliner_collections.c index ef5733fe375..d54e35f659c 100644 --- a/source/blender/editors/space_outliner/outliner_collections.c +++ b/source/blender/editors/space_outliner/outliner_collections.c @@ -71,7 +71,7 @@ bool outliner_is_collection_tree_element(const TreeElement *te) TSE_VIEW_COLLECTION_BASE)) { return true; } - if (tselem->type == 0 && te->idcode == ID_GR) { + if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_GR) { return true; } @@ -94,7 +94,7 @@ Collection *outliner_collection_from_tree_element(const TreeElement *te) Scene *scene = (Scene *)tselem->id; return scene->master_collection; } - if (tselem->type == 0 && te->idcode == ID_GR) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_GR)) { return (Collection *)tselem->id; } @@ -111,7 +111,7 @@ TreeTraversalAction outliner_find_selected_collections(TreeElement *te, void *cu return TRAVERSE_CONTINUE; } - if (tselem->type || (tselem->id && GS(tselem->id->name) != ID_GR)) { + if ((tselem->type != TSE_SOME_ID) || (tselem->id && GS(tselem->id->name) != ID_GR)) { return TRAVERSE_SKIP_CHILDS; } @@ -127,7 +127,7 @@ TreeTraversalAction outliner_find_selected_objects(TreeElement *te, void *custom return TRAVERSE_CONTINUE; } - if (tselem->type || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) { + if ((tselem->type != TSE_SOME_ID) || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) { return TRAVERSE_SKIP_CHILDS; } @@ -1458,7 +1458,7 @@ static TreeTraversalAction outliner_hide_find_data_to_edit(TreeElement *te, void BLI_gset_add(data->collections_to_edit, lc); } } - else if (tselem->type == 0 && te->idcode == ID_OB) { + else if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { Object *ob = (Object *)tselem->id; Base *base = BKE_view_layer_base_find(data->view_layer, ob); BLI_gset_add(data->bases_to_edit, base); diff --git a/source/blender/editors/space_outliner/outliner_context.c b/source/blender/editors/space_outliner/outliner_context.c index e2b3b79e027..4293d8da73e 100644 --- a/source/blender/editors/space_outliner/outliner_context.c +++ b/source/blender/editors/space_outliner/outliner_context.c @@ -34,7 +34,7 @@ static void outliner_context_selected_ids_recursive(const ListBase *subtree, { LISTBASE_FOREACH (const TreeElement *, te, subtree) { const TreeStoreElem *tse = TREESTORE(te); - if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, 0, TSE_LAYER_COLLECTION))) { + if ((tse->flag & TSE_SELECTED) && (ELEM(tse->type, TSE_SOME_ID, TSE_LAYER_COLLECTION))) { CTX_data_id_list_add(result, tse->id); } outliner_context_selected_ids_recursive(&te->subtree, result); diff --git a/source/blender/editors/space_outliner/outliner_dragdrop.c b/source/blender/editors/space_outliner/outliner_dragdrop.c index 3090cab75ae..01fb0fc6f78 100644 --- a/source/blender/editors/space_outliner/outliner_dragdrop.c +++ b/source/blender/editors/space_outliner/outliner_dragdrop.c @@ -124,7 +124,7 @@ static ID *outliner_ID_drop_find(bContext *C, const wmEvent *event, short idcode TreeElement *te = outliner_drop_find(C, event); TreeStoreElem *tselem = (te) ? TREESTORE(te) : NULL; - if (te && te->idcode == idcode && tselem->type == 0) { + if (te && (te->idcode == idcode) && (tselem->type == TSE_SOME_ID)) { return tselem->id; } return NULL; @@ -215,7 +215,7 @@ static bool is_collection_element(TreeElement *te) static bool is_object_element(TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); - return tselem->type == 0 && te->idcode == ID_OB; + return (tselem->type == TSE_SOME_ID) && te->idcode == ID_OB; } static bool is_pchan_element(TreeElement *te) @@ -281,7 +281,7 @@ static int outliner_get_insert_index(TreeElement *drag_te, static bool parent_drop_allowed(TreeElement *te, Object *potential_child) { TreeStoreElem *tselem = TREESTORE(te); - if (te->idcode != ID_OB || tselem->type != 0) { + if ((te->idcode != ID_OB) || (tselem->type != TSE_SOME_ID)) { return false; } @@ -421,7 +421,7 @@ static int parent_drop_invoke(bContext *C, wmOperator *op, const wmEvent *event) TreeElement *te = outliner_drop_find(C, event); TreeStoreElem *tselem = te ? TREESTORE(te) : NULL; - if (!(te && te->idcode == ID_OB && tselem->type == 0)) { + if (!(te && (te->idcode == ID_OB) && (tselem->type == TSE_SOME_ID))) { return OPERATOR_CANCELLED; } diff --git a/source/blender/editors/space_outliner/outliner_draw.c b/source/blender/editors/space_outliner/outliner_draw.c index 008ae727947..690adb09570 100644 --- a/source/blender/editors/space_outliner/outliner_draw.c +++ b/source/blender/editors/space_outliner/outliner_draw.c @@ -675,7 +675,7 @@ static void namebutton_fn(bContext *C, void *tsep, char *oldname) if (ts && tselem) { TreeElement *te = outliner_find_tree_element(&space_outliner->tree, tselem); - if (tselem->type == 0) { + if (tselem->type == TSE_SOME_ID) { BLI_libblock_ensure_unique_name(bmain, tselem->id->name); switch (GS(tselem->id->name)) { @@ -1100,11 +1100,11 @@ static void outliner_draw_restrictbuts(uiBlock *block, UI_but_drawflag_enable(bt, UI_BUT_ICON_REVERSE); } } - else if ((tselem->type == 0 && te->idcode == ID_OB) && + else if (((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) && (te->flag & TE_CHILD_NOT_IN_COLLECTION)) { /* Don't show restrict columns for children that are not directly inside the collection. */ } - else if (tselem->type == 0 && te->idcode == ID_OB) { + else if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { PointerRNA ptr; Object *ob = (Object *)tselem->id; RNA_id_pointer_create(&ob->id, &ptr); @@ -1699,7 +1699,7 @@ static void outliner_draw_userbuts(uiBlock *block, LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); if (te->ys + 2 * UI_UNIT_Y >= region->v2d.cur.ymin && te->ys <= region->v2d.cur.ymax) { - if (tselem->type == 0) { + if (tselem->type == TSE_SOME_ID) { uiBut *bt; ID *id = tselem->id; const char *tip = NULL; @@ -1949,7 +1949,7 @@ static void outliner_draw_mode_column_toggle(uiBlock *block, TreeStoreElem *tselem, const bool lock_object_modes) { - if (tselem->type != 0 || te->idcode != ID_OB) { + if ((tselem->type != TSE_SOME_ID) || (te->idcode != ID_OB)) { return; } @@ -2046,7 +2046,7 @@ TreeElementIcon tree_element_get_icon(TreeStoreElem *tselem, TreeElement *te) { TreeElementIcon data = {0}; - if (tselem->type) { + if (tselem->type != TSE_SOME_ID) { switch (tselem->type) { case TSE_ANIM_DATA: data.icon = ICON_ANIM_DATA; /* XXX */ @@ -2825,7 +2825,8 @@ int tree_element_id_type_to_index(TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); - const int id_index = tselem->type == 0 ? BKE_idtype_idcode_to_index(te->idcode) : INDEX_ID_GR; + const int id_index = (tselem->type == TSE_SOME_ID) ? BKE_idtype_idcode_to_index(te->idcode) : + INDEX_ID_GR; if (id_index < INDEX_ID_OB) { return id_index; } @@ -2862,9 +2863,9 @@ static void outliner_draw_iconrow(bContext *C, te->flag &= ~(TE_ICONROW | TE_ICONROW_MERGED); /* object hierarchy always, further constrained on level */ - if (level < 1 || (tselem->type == 0 && te->idcode == ID_OB)) { + if ((level < 1) || ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB))) { /* active blocks get white circle */ - if (tselem->type == 0) { + if (tselem->type == TSE_SOME_ID) { if (te->idcode == ID_OB) { active = (tvc->obact == (Object *)tselem->id) ? OL_DRAWSEL_NORMAL : OL_DRAWSEL_NONE; } @@ -2879,7 +2880,7 @@ static void outliner_draw_iconrow(bContext *C, active = tree_element_type_active_state_get(C, tvc, te, tselem); } - if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION, TSE_R_LAYER, TSE_GP_LAYER)) { + if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION, TSE_R_LAYER, TSE_GP_LAYER)) { outliner_draw_iconrow_doit(block, te, fstyle, xmax, offsx, ys, alpha_fac, active, 1); } else { @@ -2954,7 +2955,7 @@ static bool element_should_draw_faded(const TreeViewContext *tvc, const TreeElement *te, const TreeStoreElem *tselem) { - if (tselem->type == 0) { + if (tselem->type == TSE_SOME_ID) { switch (te->idcode) { case ID_OB: { const Object *ob = (const Object *)tselem->id; @@ -3023,7 +3024,7 @@ static void outliner_draw_tree_element(bContext *C, GPU_blend(GPU_BLEND_ALPHA); /* Colors for active/selected data. */ - if (tselem->type == 0) { + if (tselem->type == TSE_SOME_ID) { if (te->idcode == ID_OB) { Object *ob = (Object *)tselem->id; Base *base = (te->directdata) ? (Base *)te->directdata : @@ -3080,7 +3081,7 @@ static void outliner_draw_tree_element(bContext *C, if (tselem->type == TSE_VIEW_COLLECTION_BASE) { /* Scene collection in view layer can't expand/collapse. */ } - else if (te->subtree.first || (tselem->type == 0 && te->idcode == ID_SCE) || + else if (te->subtree.first || ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_SCE)) || (te->flag & TE_LAZY_CLOSED)) { /* Open/close icon, only when sub-levels, except for scene. */ int icon_x = startx; @@ -3117,7 +3118,7 @@ static void outliner_draw_tree_element(bContext *C, offsx += 2 * ufac; } - if (ELEM(tselem->type, 0, TSE_LAYER_COLLECTION) || + if (ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION) || ((tselem->type == TSE_RNA_STRUCT) && RNA_struct_is_ID(te->rnaptr.type))) { const BIFIconID lib_icon = UI_icon_from_library(tselem->id); if (lib_icon != ICON_NONE) { @@ -3143,7 +3144,7 @@ static void outliner_draw_tree_element(bContext *C, /* Closed item, we draw the icons, not when it's a scene, or master-server list though. */ if (!TSELEM_OPEN(tselem, space_outliner)) { if (te->subtree.first) { - if (tselem->type == 0 && te->idcode == ID_SCE) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_SCE)) { /* Pass. */ } /* this tree element always has same amount of branches, so don't draw */ @@ -3210,7 +3211,7 @@ static bool subtree_contains_object(ListBase *lb) { LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { return true; } } @@ -3265,7 +3266,7 @@ static void outliner_draw_hierarchy_lines_recursive(uint pos, y = *starty; } - else if (tselem->type == 0 && te->idcode == ID_OB) { + else if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { if (subtree_contains_object(&te->subtree)) { draw_hierarchy_line = true; is_object_line = true; diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index 09aa268d856..c63209b6b60 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -638,7 +638,7 @@ static bool outliner_id_remap_find_tree_element(bContext *C, if (y > te->ys && y < te->ys + UI_UNIT_Y) { TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0 && tselem->id) { + if ((tselem->type == TSE_SOME_ID) && tselem->id) { printf("found id %s (%p)!\n", tselem->id->name, tselem->id); RNA_enum_set(op->ptr, "id_type", GS(tselem->id->name)); @@ -763,7 +763,7 @@ static int outliner_id_copy_tag(SpaceOutliner *space_outliner, ListBase *tree) TreeStoreElem *tselem = TREESTORE(te); /* if item is selected and is an ID, tag it as needing to be copied. */ - if (tselem->flag & TSE_SELECTED && ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) { + if (tselem->flag & TSE_SELECTED && ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) { ID *id = tselem->id; if (!(id->tag & LIB_TAG_DOIT)) { BKE_copybuffer_tag_ID(tselem->id); @@ -1640,7 +1640,7 @@ static int subtree_has_objects(ListBase *lb) { LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { return 1; } if (subtree_has_objects(&te->subtree)) { @@ -1658,7 +1658,7 @@ static void tree_element_show_hierarchy(Scene *scene, SpaceOutliner *space_outli TreeStoreElem *tselem = TREESTORE(te); if (ELEM(tselem->type, - 0, + TSE_SOME_ID, TSE_SCENE_OBJECTS_BASE, TSE_VIEW_COLLECTION_BASE, TSE_LAYER_COLLECTION)) { diff --git a/source/blender/editors/space_outliner/outliner_select.c b/source/blender/editors/space_outliner/outliner_select.c index e31af48ab7e..d53a37fa60e 100644 --- a/source/blender/editors/space_outliner/outliner_select.c +++ b/source/blender/editors/space_outliner/outliner_select.c @@ -198,7 +198,7 @@ void outliner_item_mode_toggle(bContext *C, { TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { Object *ob = (Object *)tselem->id; Base *base = BKE_view_layer_base_find(tvc->view_layer, ob); @@ -301,7 +301,7 @@ static void tree_element_object_activate(bContext *C, Object *ob = NULL; /* if id is not object, we search back */ - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { ob = (Object *)tselem->id; } else { @@ -443,7 +443,7 @@ static void tree_element_world_activate(bContext *C, Scene *scene, TreeElement * TreeElement *tep = te->parent; if (tep) { TreeStoreElem *tselem = TREESTORE(tep); - if (tselem->type == 0) { + if (tselem->type == TSE_SOME_ID) { sce = (Scene *)tselem->id; } } @@ -1165,7 +1165,7 @@ static void outliner_set_properties_tab(bContext *C, TreeElement *te, TreeStoreE int context = 0; /* ID Types */ - if (tselem->type == 0) { + if (tselem->type == TSE_SOME_ID) { RNA_id_pointer_create(tselem->id, &ptr); switch (te->idcode) { @@ -1374,12 +1374,12 @@ static void do_outliner_item_activate_tree_element(bContext *C, tvc->scene, tvc->view_layer, te, - (extend && tselem->type == 0) ? OL_SETSEL_EXTEND : - OL_SETSEL_NORMAL, - recursive && tselem->type == 0); + (extend && tselem->type == TSE_SOME_ID) ? OL_SETSEL_EXTEND : + OL_SETSEL_NORMAL, + recursive && tselem->type == TSE_SOME_ID); } - if (tselem->type == 0) { /* The lib blocks. */ + if (tselem->type == TSE_SOME_ID) { /* The lib blocks. */ if (do_activate_data == false) { /* Only select in outliner. */ } diff --git a/source/blender/editors/space_outliner/outliner_sync.c b/source/blender/editors/space_outliner/outliner_sync.c index 8bd5e3a130a..6543a909a41 100644 --- a/source/blender/editors/space_outliner/outliner_sync.c +++ b/source/blender/editors/space_outliner/outliner_sync.c @@ -326,7 +326,7 @@ static void outliner_sync_selection_from_outliner(Scene *scene, LISTBASE_FOREACH (TreeElement *, te, tree) { TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { if (sync_types->object) { outliner_select_sync_to_object(view_layer, te, tselem, selected_items->objects); } @@ -503,7 +503,7 @@ static void outliner_sync_selection_to_outliner(ViewLayer *view_layer, LISTBASE_FOREACH (TreeElement *, te, tree) { TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_OB) { if (sync_types->object) { outliner_select_sync_from_object(view_layer, active_data->object, te, tselem); } diff --git a/source/blender/editors/space_outliner/outliner_tools.c b/source/blender/editors/space_outliner/outliner_tools.c index b735064cfef..9af2ba6a82b 100644 --- a/source/blender/editors/space_outliner/outliner_tools.c +++ b/source/blender/editors/space_outliner/outliner_tools.c @@ -106,7 +106,7 @@ static void get_element_operation_type( TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { /* Layer collection points to collection ID. */ - if (!ELEM(tselem->type, 0, TSE_LAYER_COLLECTION)) { + if (!ELEM(tselem->type, TSE_SOME_ID, TSE_LAYER_COLLECTION)) { if (*datalevel == 0) { *datalevel = tselem->type; } @@ -402,7 +402,8 @@ static void outliner_do_libdata_operation(bContext *C, LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); if (tselem->flag & TSE_SELECTED) { - if ((tselem->type == 0 && te->idcode != 0) || tselem->type == TSE_LAYER_COLLECTION) { + if (((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) || + tselem->type == TSE_LAYER_COLLECTION) { TreeStoreElem *tsep = te->parent ? TREESTORE(te->parent) : NULL; operation_fn(C, reports, scene, te, tsep, tselem, user_data); } @@ -1044,7 +1045,7 @@ void outliner_do_object_operation_ex(bContext *C, TreeStoreElem *tselem = TREESTORE(te); bool select_handled = false; if (tselem->flag & TSE_SELECTED) { - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { /* When objects selected in other scenes... dunno if that should be allowed. */ Scene *scene_owner = (Scene *)outliner_search_back(te, ID_SCE); if (scene_owner && scene_act != scene_owner) { @@ -1601,7 +1602,7 @@ static TreeTraversalAction outliner_find_objects_to_delete(TreeElement *te, void return TRAVERSE_CONTINUE; } - if (tselem->type || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) { + if ((tselem->type != TSE_SOME_ID) || (tselem->id == NULL) || (GS(tselem->id->name) != ID_OB)) { return TRAVERSE_SKIP_CHILDS; } diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 0d82ba992c2..3696ef8dbce 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -307,7 +307,7 @@ static void outliner_add_line_styles(SpaceOutliner *space_outliner, continue; } linestyle->id.tag &= ~LIB_TAG_DOIT; - outliner_add_element(space_outliner, lb, linestyle, te, 0, 0); + outliner_add_element(space_outliner, lb, linestyle, te, TSE_SOME_ID, 0); } } } @@ -332,7 +332,7 @@ static void outliner_add_scene_contents(SpaceOutliner *space_outliner, } /* World */ - outliner_add_element(space_outliner, lb, sce->world, te, 0, 0); + outliner_add_element(space_outliner, lb, sce->world, te, TSE_SOME_ID, 0); /* Collections */ ten = outliner_add_element(space_outliner, lb, &sce->id, te, TSE_SCENE_COLLECTION_BASE, 0); @@ -343,7 +343,7 @@ static void outliner_add_scene_contents(SpaceOutliner *space_outliner, ten = outliner_add_element(space_outliner, lb, sce, te, TSE_SCENE_OBJECTS_BASE, 0); ten->name = IFACE_("Objects"); FOREACH_SCENE_OBJECT_BEGIN (sce, ob) { - outliner_add_element(space_outliner, &ten->subtree, ob, ten, 0, 0); + outliner_add_element(space_outliner, &ten->subtree, ob, ten, TSE_SOME_ID, 0); } FOREACH_SCENE_OBJECT_END; outliner_make_object_parent_hierarchy(&ten->subtree); @@ -368,14 +368,14 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, &te->subtree, ob->poselib, te, - 0, + TSE_SOME_ID, 0); /* XXX FIXME.. add a special type for this. */ if (ob->proxy && !ID_IS_LINKED(ob)) { outliner_add_element(space_outliner, &te->subtree, ob->proxy, te, TSE_PROXY, 0); } - outliner_add_element(space_outliner, &te->subtree, ob->data, te, 0, 0); + outliner_add_element(space_outliner, &te->subtree, ob->data, te, TSE_SOME_ID, 0); if (ob->pose) { bArmature *arm = ob->data; @@ -458,7 +458,7 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, } for (int a = 0; a < ob->totcol; a++) { - outliner_add_element(space_outliner, &te->subtree, ob->mat[a], te, 0, a); + outliner_add_element(space_outliner, &te->subtree, ob->mat[a], te, TSE_SOME_ID, a); } if (!BLI_listbase_is_empty(&ob->constraints)) { @@ -624,7 +624,8 @@ static void outliner_add_object_contents(SpaceOutliner *space_outliner, /* duplicated group */ if (ob->instance_collection && (ob->transflag & OB_DUPLICOLLECTION)) { - outliner_add_element(space_outliner, &te->subtree, ob->instance_collection, te, 0, 0); + outliner_add_element( + space_outliner, &te->subtree, ob->instance_collection, te, TSE_SOME_ID, 0); } } @@ -686,9 +687,9 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, outliner_add_element(space_outliner, &te->subtree, me, te, TSE_ANIM_DATA, 0); } - outliner_add_element(space_outliner, &te->subtree, me->key, te, 0, 0); + outliner_add_element(space_outliner, &te->subtree, me->key, te, TSE_SOME_ID, 0); for (int a = 0; a < me->totcol; a++) { - outliner_add_element(space_outliner, &te->subtree, me->mat[a], te, 0, a); + outliner_add_element(space_outliner, &te->subtree, me->mat[a], te, TSE_SOME_ID, a); } /* could do tfaces with image links, but the images are not grouped nicely. * would require going over all tfaces, sort images in use. etc... */ @@ -702,7 +703,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, } for (int a = 0; a < cu->totcol; a++) { - outliner_add_element(space_outliner, &te->subtree, cu->mat[a], te, 0, a); + outliner_add_element(space_outliner, &te->subtree, cu->mat[a], te, TSE_SOME_ID, a); } break; } @@ -714,7 +715,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, } for (int a = 0; a < mb->totcol; a++) { - outliner_add_element(space_outliner, &te->subtree, mb->mat[a], te, 0, a); + outliner_add_element(space_outliner, &te->subtree, mb->mat[a], te, TSE_SOME_ID, a); } break; } @@ -730,7 +731,7 @@ static void outliner_add_id_contents(SpaceOutliner *space_outliner, if (outliner_animdata_test(tex->adt)) { outliner_add_element(space_outliner, &te->subtree, tex, te, TSE_ANIM_DATA, 0); } - outliner_add_element(space_outliner, &te->subtree, tex->ima, te, 0, 0); + outliner_add_element(space_outliner, &te->subtree, tex->ima, te, TSE_SOME_ID, 0); break; } case ID_CA: { @@ -1008,7 +1009,7 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, if (te->type) { outliner_tree_element_type_expand(te->type, space_outliner); } - else if (type == 0) { + else if (type == TSE_SOME_ID) { TreeStoreElem *tsepar = parent ? TREESTORE(parent) : NULL; /* ID data-block. */ @@ -1229,7 +1230,7 @@ BLI_INLINE void outliner_add_collection_objects(SpaceOutliner *space_outliner, TreeElement *parent) { LISTBASE_FOREACH (CollectionObject *, cob, &collection->gobject) { - outliner_add_element(space_outliner, tree, cob->ob, parent, 0, 0); + outliner_add_element(space_outliner, tree, cob->ob, parent, TSE_SOME_ID, 0); } } @@ -1240,7 +1241,8 @@ static TreeElement *outliner_add_collection_recursive(SpaceOutliner *space_outli outliner_add_collection_init(ten, collection); LISTBASE_FOREACH (CollectionChild *, child, &collection->children) { - outliner_add_element(space_outliner, &ten->subtree, &child->collection->id, ten, 0, 0); + outliner_add_element( + space_outliner, &ten->subtree, &child->collection->id, ten, TSE_SOME_ID, 0); } if (space_outliner->outlinevis != SO_SCENES) { @@ -1265,7 +1267,7 @@ void outliner_make_object_parent_hierarchy(ListBase *lb) TreeElement *ten = te->next; TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && te->idcode == ID_OB) { Object *ob = (Object *)tselem->id; if (ob->parent && ob->parent->id.newid) { BLI_remlink(lb, te); @@ -1406,7 +1408,7 @@ static void outliner_sort(ListBase *lb) /* sorting rules; only object lists, ID lists, or deformgroups */ if (ELEM(tselem->type, TSE_DEFGROUP, TSE_ID_BASE) || - (tselem->type == 0 && te->idcode == ID_OB)) { + ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB))) { int totelem = BLI_listbase_count(lb); if (totelem > 1) { @@ -1420,7 +1422,7 @@ static void outliner_sort(ListBase *lb) tp->name = te->name; tp->idcode = te->idcode; - if (tselem->type && tselem->type != TSE_DEFGROUP) { + if ((tselem->type != TSE_SOME_ID) && tselem->type != TSE_DEFGROUP) { tp->idcode = 0; /* Don't sort this. */ } if (tselem->type == TSE_ID_BASE) { @@ -1471,7 +1473,7 @@ static void outliner_collections_children_sort(ListBase *lb) TreeStoreElem *tselem = TREESTORE(te); /* Sorting rules: only object lists. */ - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { int totelem = BLI_listbase_count(lb); if (totelem > 1) { @@ -1546,7 +1548,7 @@ static bool test_collection_callback(TreeElement *te) static bool test_object_callback(TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); - return ((tselem->type == 0) && (te->idcode == ID_OB)); + return ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)); } /** @@ -1707,7 +1709,7 @@ static bool outliner_element_visible_get(ViewLayer *view_layer, } TreeStoreElem *tselem = TREESTORE(te); - if ((tselem->type == 0) && (te->idcode == ID_OB)) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { if ((exclude_filter & SO_FILTER_OB_TYPE) == SO_FILTER_OB_TYPE) { return false; } @@ -1790,14 +1792,15 @@ static bool outliner_element_visible_get(ViewLayer *view_layer, return is_visible; } - if ((te->parent != NULL) && (TREESTORE(te->parent)->type == 0) && + if ((te->parent != NULL) && (TREESTORE(te->parent)->type == TSE_SOME_ID) && (te->parent->idcode == ID_OB)) { if (exclude_filter & SO_FILTER_NO_CHILDREN) { return false; } } } - else if (te->parent != NULL && TREESTORE(te->parent)->type == 0 && te->parent->idcode == ID_OB) { + else if ((te->parent != NULL) && (TREESTORE(te->parent)->type == TSE_SOME_ID) && + (te->parent->idcode == ID_OB)) { if (exclude_filter & SO_FILTER_NO_OB_CONTENT) { return false; } @@ -1821,7 +1824,7 @@ static bool outliner_element_is_collection_or_object(TreeElement *te) { TreeStoreElem *tselem = TREESTORE(te); - if ((tselem->type == 0) && (te->idcode == ID_OB)) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { return true; } diff --git a/source/blender/editors/space_outliner/outliner_utils.c b/source/blender/editors/space_outliner/outliner_utils.c index 92178cfdfc9..562457c62e9 100644 --- a/source/blender/editors/space_outliner/outliner_utils.c +++ b/source/blender/editors/space_outliner/outliner_utils.c @@ -226,7 +226,7 @@ TreeElement *outliner_find_id(SpaceOutliner *space_outliner, ListBase *lb, const { LISTBASE_FOREACH (TreeElement *, te, lb) { TreeStoreElem *tselem = TREESTORE(te); - if (tselem->type == 0) { + if (tselem->type == TSE_SOME_ID) { if (tselem->id == id) { return te; } @@ -266,7 +266,7 @@ TreeElement *outliner_find_editbone(ListBase *lb, const EditBone *ebone) } TreeStoreElem *tselem = TREESTORE(te); - if (ELEM(tselem->type, 0, TSE_EBONE)) { + if (ELEM(tselem->type, TSE_SOME_ID, TSE_EBONE)) { TreeElement *tes = outliner_find_editbone(&te->subtree, ebone); if (tes) { return tes; @@ -283,7 +283,7 @@ TreeElement *outliner_search_back_te(TreeElement *te, short idcode) while (te) { tselem = TREESTORE(te); - if (tselem->type == 0 && te->idcode == idcode) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == idcode)) { return te; } te = te->parent; @@ -510,7 +510,7 @@ Base *ED_outliner_give_base_under_cursor(bContext *C, const int mval[2]) te = outliner_find_item_at_y(space_outliner, &space_outliner->tree, view_mval[1]); if (te) { TreeStoreElem *tselem = TREESTORE(te); - if ((tselem->type == 0) && (te->idcode == ID_OB)) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { Object *ob = (Object *)tselem->id; base = (te->directdata) ? (Base *)te->directdata : BKE_view_layer_base_find(view_layer, ob); } diff --git a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc index a81ce11498a..91b690d35fa 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_libraries.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_libraries.cc @@ -144,7 +144,7 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, if (!tenlib) { /* Create library tree element on demand, depending if there are any data-blocks. */ if (lib) { - tenlib = outliner_add_element(&space_outliner_, &lb, lib, nullptr, 0, 0); + tenlib = outliner_add_element(&space_outliner_, &lb, lib, nullptr, TSE_SOME_ID, 0); } else { tenlib = outliner_add_element(&space_outliner_, &lb, &mainvar, nullptr, TSE_ID_BASE, 0); @@ -168,7 +168,7 @@ TreeElement *TreeDisplayLibraries::add_library_contents(Main &mainvar, for (ID *id : List(lbarray[a])) { if (library_id_filter_poll(lib, id)) { - outliner_add_element(&space_outliner_, &ten->subtree, id, ten, 0, 0); + outliner_add_element(&space_outliner_, &ten->subtree, id, ten, TSE_SOME_ID, 0); } } } diff --git a/source/blender/editors/space_outliner/tree/tree_display_orphaned.cc b/source/blender/editors/space_outliner/tree/tree_display_orphaned.cc index 559cb289f3f..69ccf014642 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_orphaned.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_orphaned.cc @@ -76,7 +76,8 @@ ListBase TreeDisplayIDOrphans::buildTree(const TreeSourceData &source_data) /* Add the orphaned data-blocks - these will not be added with any subtrees attached. */ for (ID *id : List(lbarray[a])) { if (ID_REAL_USERS(id) <= 0) { - outliner_add_element(&space_outliner_, (te) ? &te->subtree : &tree, id, te, 0, 0); + outliner_add_element( + &space_outliner_, (te) ? &te->subtree : &tree, id, te, TSE_SOME_ID, 0); } } } diff --git a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc index f377512d81e..390f81cfcd1 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_scenes.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_scenes.cc @@ -46,7 +46,8 @@ ListBase TreeDisplayScenes::buildTree(const TreeSourceData &source_data) for (ID *id : List(source_data.bmain->scenes)) { Scene *scene = reinterpret_cast(id); - TreeElement *te = outliner_add_element(&space_outliner_, &tree, scene, nullptr, 0, 0); + TreeElement *te = outliner_add_element( + &space_outliner_, &tree, scene, nullptr, TSE_SOME_ID, 0); TreeStoreElem *tselem = TREESTORE(te); /* New scene elements open by default */ diff --git a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc index a0ebac5f451..89c9960a24f 100644 --- a/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc +++ b/source/blender/editors/space_outliner/tree/tree_display_view_layer.cc @@ -80,7 +80,7 @@ ListBase TreeDisplayViewLayer::buildTree(const TreeSourceData &source_data) /* Show objects in the view layer. */ for (Base *base : List(view_layer_->object_bases)) { TreeElement *te_object = outliner_add_element( - &space_outliner_, &tree, base->object, nullptr, 0, 0); + &space_outliner_, &tree, base->object, nullptr, TSE_SOME_ID, 0); te_object->directdata = base; } @@ -158,7 +158,7 @@ void TreeDisplayViewLayer::add_layer_collection_objects(ListBase &tree, for (CollectionObject *cob : List(lc.collection->gobject)) { Base *base = BKE_view_layer_base_find(view_layer_, cob->ob); TreeElement *te_object = outliner_add_element( - &space_outliner_, &tree, base->object, &ten, 0, 0); + &space_outliner_, &tree, base->object, &ten, TSE_SOME_ID, 0); te_object->directdata = base; } } @@ -203,7 +203,7 @@ void ObjectsChildrenBuilder::object_tree_elements_lookup_create_recursive(TreeEl continue; } - if (tselem->type == 0 && te->idcode == ID_OB) { + if ((tselem->type == TSE_SOME_ID) && (te->idcode == ID_OB)) { Object *ob = (Object *)tselem->id; /* Lookup children or add new, empty children vector. */ Vector &tree_elements = object_tree_elements_map_.lookup_or_add(ob, {}); @@ -261,8 +261,12 @@ void ObjectsChildrenBuilder::make_object_parent_hierarchy_collections() if (!found) { /* We add the child in the tree even if it is not in the collection. * We deliberately clear its sub-tree though, to make it less prominent. */ - TreeElement *child_ob_tree_element = outliner_add_element( - &outliner_, &parent_ob_tree_element->subtree, child, parent_ob_tree_element, 0, 0); + TreeElement *child_ob_tree_element = outliner_add_element(&outliner_, + &parent_ob_tree_element->subtree, + child, + parent_ob_tree_element, + TSE_SOME_ID, + 0); outliner_free_tree(&child_ob_tree_element->subtree); child_ob_tree_element->flag |= TE_CHILD_NOT_IN_COLLECTION; child_ob_tree_elements.append(child_ob_tree_element); diff --git a/source/blender/editors/space_outliner/tree/tree_element_anim_data.cc b/source/blender/editors/space_outliner/tree/tree_element_anim_data.cc index 13a25800800..5a9568ea906 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_anim_data.cc +++ b/source/blender/editors/space_outliner/tree/tree_element_anim_data.cc @@ -44,7 +44,8 @@ TreeElementAnimData::TreeElementAnimData(TreeElement &legacy_te, ID &id) void TreeElementAnimData::expand(SpaceOutliner &space_outliner) const { /* Animation data-block itself. */ - outliner_add_element(&space_outliner, &legacy_te_.subtree, anim_data_.action, &legacy_te_, 0, 0); + outliner_add_element( + &space_outliner, &legacy_te_.subtree, anim_data_.action, &legacy_te_, TSE_SOME_ID, 0); expand_drivers(space_outliner); expand_NLA_tracks(space_outliner); -- cgit v1.2.3 From ae005393dce4746c0ee97887ea1a81281a1f726f Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 5 Mar 2021 17:39:52 +0100 Subject: Fix incorrect assert in Outliner ID deletion Mistake in aa3a4973a30f. The expanded `ELEM()` check would include `0 && te->idcode != 0`, which always evaluates to `false`/`0`. That wouldn't cause the asset to fail, but the `te->idcode` part would never be checked. Fixed the error and cleaned up the check against "0" with a check against `TSE_SOME_ID`, see b9e54566e3b1a. --- source/blender/editors/space_outliner/outliner_edit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_outliner/outliner_edit.c b/source/blender/editors/space_outliner/outliner_edit.c index c63209b6b60..18abe17d515 100644 --- a/source/blender/editors/space_outliner/outliner_edit.c +++ b/source/blender/editors/space_outliner/outliner_edit.c @@ -464,7 +464,8 @@ static void id_delete(bContext *C, ReportList *reports, TreeElement *te, TreeSto ID *id = tselem->id; BLI_assert(id != NULL); - BLI_assert(ELEM(tselem->type, 0 && te->idcode != 0, TSE_LAYER_COLLECTION)); + BLI_assert(((tselem->type == TSE_SOME_ID) && (te->idcode != 0)) || + (tselem->type == TSE_LAYER_COLLECTION)); UNUSED_VARS_NDEBUG(te); if (te->idcode == ID_LI && ((Library *)id)->parent != NULL) { -- cgit v1.2.3 From 3a907e742507dde9b26eb5f531c18ad00b0e2ab6 Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 5 Mar 2021 18:01:31 +0100 Subject: Outliner: Barebones to port IDs to new Outliner tree-element code design Continuation of work in 2e221de4ceee and 249e4df110e0 . This prepares things so we can start porting the individual ID types to the new code design. I already added some code for library IDs, because they need some special handling during construction, which I didn't want to break. The `AbstractTreeElement::isExpandValid()` check can be removed once types were ported and can be assumed to have a proper `expand()` implemenation. Also makes `TreeElementGPencilLayer` `final` which I forgot in e0442a955bad. --- .../blender/editors/space_outliner/CMakeLists.txt | 2 + .../blender/editors/space_outliner/outliner_tree.c | 7 +- .../editors/space_outliner/tree/tree_element.cc | 9 ++ .../editors/space_outliner/tree/tree_element.h | 1 + .../editors/space_outliner/tree/tree_element.hh | 9 ++ .../tree/tree_element_gpencil_layer.hh | 2 +- .../editors/space_outliner/tree/tree_element_id.cc | 100 +++++++++++++++++++++ .../editors/space_outliner/tree/tree_element_id.hh | 48 ++++++++++ 8 files changed, 176 insertions(+), 2 deletions(-) create mode 100644 source/blender/editors/space_outliner/tree/tree_element_id.cc create mode 100644 source/blender/editors/space_outliner/tree/tree_element_id.hh (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_outliner/CMakeLists.txt b/source/blender/editors/space_outliner/CMakeLists.txt index 040410c0bdd..7b9bc44f986 100644 --- a/source/blender/editors/space_outliner/CMakeLists.txt +++ b/source/blender/editors/space_outliner/CMakeLists.txt @@ -58,6 +58,7 @@ set(SRC tree/tree_element_anim_data.cc tree/tree_element_driver_base.cc tree/tree_element_gpencil_layer.cc + tree/tree_element_id.cc tree/tree_element_nla.cc outliner_intern.h @@ -68,6 +69,7 @@ set(SRC tree/tree_element_anim_data.hh tree/tree_element_driver_base.hh tree/tree_element_gpencil_layer.hh + tree/tree_element_id.hh tree/tree_element_nla.hh ) diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index 3696ef8dbce..f109efbaa2b 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -992,6 +992,11 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, else if (type == TSE_ID_BASE) { /* pass */ } + else if (type == TSE_SOME_ID) { + if (!te->type) { + BLI_assert(!"Expected this ID type to be ported to new Outliner tree-element design"); + } + } else { /* Other cases must be caught above. */ BLI_assert(TSE_IS_REAL_ID(tselem)); @@ -1006,7 +1011,7 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, te->idcode = GS(id->name); } - if (te->type) { + if (te->type && outliner_tree_element_type_is_expand_valid(te->type)) { outliner_tree_element_type_expand(te->type, space_outliner); } else if (type == TSE_SOME_ID) { diff --git a/source/blender/editors/space_outliner/tree/tree_element.cc b/source/blender/editors/space_outliner/tree/tree_element.cc index a692aae37c3..79c2831475f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.cc +++ b/source/blender/editors/space_outliner/tree/tree_element.cc @@ -23,6 +23,7 @@ #include "tree_element_anim_data.hh" #include "tree_element_driver_base.hh" #include "tree_element_gpencil_layer.hh" +#include "tree_element_id.hh" #include "tree_element_nla.hh" #include "tree_element.h" @@ -37,6 +38,8 @@ static AbstractTreeElement *tree_element_create(int type, TreeElement &legacy_te ID &id = *static_cast(idv); switch (type) { + case TSE_SOME_ID: + return TreeElementID::createFromID(legacy_te, id); case TSE_ANIM_DATA: return new TreeElementAnimData(legacy_te, id); case TSE_DRIVER_BASE: @@ -82,6 +85,12 @@ void outliner_tree_element_type_expand(TreeElementType *type, SpaceOutliner *spa outliner::tree_element_expand(reinterpret_cast(*type), *space_outliner); } +bool outliner_tree_element_type_is_expand_valid(TreeElementType *type) +{ + outliner::AbstractTreeElement &element = reinterpret_cast( + *type); + return element.isExpandValid(); +} void outliner_tree_element_type_free(TreeElementType **type) { diff --git a/source/blender/editors/space_outliner/tree/tree_element.h b/source/blender/editors/space_outliner/tree/tree_element.h index d88c37180b3..c3dec1bf68a 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.h +++ b/source/blender/editors/space_outliner/tree/tree_element.h @@ -39,6 +39,7 @@ TreeElementType *outliner_tree_element_type_create(int type, TreeElement *legacy void outliner_tree_element_type_free(TreeElementType **type); void outliner_tree_element_type_expand(TreeElementType *type, SpaceOutliner *space_outliner); +bool outliner_tree_element_type_is_expand_valid(TreeElementType *type); #ifdef __cplusplus } diff --git a/source/blender/editors/space_outliner/tree/tree_element.hh b/source/blender/editors/space_outliner/tree/tree_element.hh index 8a1ebb51eae..3e61dd25898 100644 --- a/source/blender/editors/space_outliner/tree/tree_element.hh +++ b/source/blender/editors/space_outliner/tree/tree_element.hh @@ -48,6 +48,15 @@ class AbstractTreeElement { virtual void expand(SpaceOutliner &) const { } + + /** + * Just while transitioning to the new tree-element design: Some types are only partially ported, + * and the expanding isn't done yet. + */ + virtual bool isExpandValid() const + { + return true; + } }; } // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.hh b/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.hh index dd18dd42344..da57ef63f1f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_gpencil_layer.hh @@ -26,7 +26,7 @@ struct bGPDlayer; namespace blender::ed::outliner { -class TreeElementGPencilLayer : public AbstractTreeElement { +class TreeElementGPencilLayer final : public AbstractTreeElement { public: TreeElementGPencilLayer(TreeElement &legacy_te, bGPDlayer &gplayer); }; diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.cc b/source/blender/editors/space_outliner/tree/tree_element_id.cc new file mode 100644 index 00000000000..26787475635 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_id.cc @@ -0,0 +1,100 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup spoutliner + */ + +#include "DNA_ID.h" + +#include "BLI_utildefines.h" + +#include "../outliner_intern.h" + +#include "tree_element_id.hh" + +namespace blender::ed::outliner { + +TreeElementID::TreeElementID(TreeElement &legacy_te, const ID &id) : AbstractTreeElement(legacy_te) +{ + BLI_assert(legacy_te_.store_elem->type == TSE_SOME_ID); + BLI_assert(TSE_IS_REAL_ID(legacy_te_.store_elem)); + + /* Default, some specific types override this. */ + legacy_te_.name = id.name + 2; + legacy_te_.idcode = GS(id.name); +} + +TreeElementID *TreeElementID::createFromID(TreeElement &legacy_te, const ID &id) +{ + switch (ID_Type type = GS(id.name); type) { + case ID_LI: + return new TreeElementIDLibrary(legacy_te, id); + case ID_SCE: + case ID_OB: + case ID_ME: + case ID_CU: + case ID_MB: + case ID_MA: + case ID_TE: + case ID_LT: + case ID_LA: + case ID_CA: + case ID_KE: + case ID_SCR: + case ID_WO: + case ID_SPK: + case ID_GR: + case ID_NT: + case ID_BR: + case ID_PA: + case ID_MC: + case ID_MSK: + case ID_LS: + case ID_LP: + case ID_GD: + case ID_WS: + case ID_HA: + case ID_PT: + case ID_VO: + case ID_SIM: + case ID_WM: + case ID_IM: + case ID_VF: + case ID_TXT: + case ID_SO: + case ID_AR: + case ID_AC: + case ID_PAL: + case ID_PC: + case ID_CF: + return new TreeElementID(legacy_te, id); + /* Deprecated */ + case ID_IP: + BLI_assert(!"Outliner trying to build tree-element for deprecated ID type"); + return nullptr; + } + + return nullptr; +} + +TreeElementIDLibrary::TreeElementIDLibrary(TreeElement &legacy_te, const ID &id) + : TreeElementID(legacy_te, id) +{ + legacy_te.name = ((Library &)id).filepath; +} + +} // namespace blender::ed::outliner diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.hh b/source/blender/editors/space_outliner/tree/tree_element_id.hh new file mode 100644 index 00000000000..d1e672ed387 --- /dev/null +++ b/source/blender/editors/space_outliner/tree/tree_element_id.hh @@ -0,0 +1,48 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/** \file + * \ingroup spoutliner + */ + +#pragma once + +#include "tree_element.hh" + +namespace blender::ed::outliner { + +class TreeElementID : public AbstractTreeElement { + public: + TreeElementID(TreeElement &legacy_te, const ID &id); + + static TreeElementID *createFromID(TreeElement &legacy_te, const ID &id); + + /** + * Expanding not implemented for all types yet. Once it is, this can be set to true or + * `AbstractTreeElement::expandValid()` can be removed alltogether. + */ + bool isExpandValid() const override + { + return false; + } +}; + +class TreeElementIDLibrary final : public TreeElementID { + public: + TreeElementIDLibrary(TreeElement &legacy_te, const ID &id); +}; + +} // namespace blender::ed::outliner -- cgit v1.2.3 From 4addcf1efcd6d6cc4d72a730bd88f4b89d2f1a8d Mon Sep 17 00:00:00 2001 From: Julian Eisel Date: Fri, 5 Mar 2021 18:14:56 +0100 Subject: Cleanup: Remove redundant special handling after previous commit Not needed anymore since 3a907e742507. --- source/blender/editors/space_outliner/outliner_tree.c | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_outliner/outliner_tree.c b/source/blender/editors/space_outliner/outliner_tree.c index f109efbaa2b..6ca986660c1 100644 --- a/source/blender/editors/space_outliner/outliner_tree.c +++ b/source/blender/editors/space_outliner/outliner_tree.c @@ -1001,13 +1001,7 @@ TreeElement *outliner_add_element(SpaceOutliner *space_outliner, /* Other cases must be caught above. */ BLI_assert(TSE_IS_REAL_ID(tselem)); - /* do here too, for blend file viewer, own ID_LI then shows file name */ - if (GS(id->name) == ID_LI) { - te->name = ((Library *)id)->filepath; - } - else { - te->name = id->name + 2; /* Default, can be overridden by Library or non-ID data. */ - } + te->name = id->name + 2; /* Default, can be overridden by Library or non-ID data. */ te->idcode = GS(id->name); } -- cgit v1.2.3 From becc36cce5248417fe4f626dedb50804c0e0eb1d Mon Sep 17 00:00:00 2001 From: Hans Goudey Date: Fri, 5 Mar 2021 14:45:09 -0600 Subject: Geometry Nodes: Sort attribute search items when menu opens Because the search didn't run when the menu first opens, the attributes appeared in a different order than after you typed anything into the search field. This commit instead runs the search when the menu is first opened, but it only sorts items without filtering. --- .../editors/space_node/node_geometry_attribute_search.cc | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_node/node_geometry_attribute_search.cc b/source/blender/editors/space_node/node_geometry_attribute_search.cc index 982c57eb3ec..b03346577a8 100644 --- a/source/blender/editors/space_node/node_geometry_attribute_search.cc +++ b/source/blender/editors/space_node/node_geometry_attribute_search.cc @@ -75,15 +75,9 @@ static void attribute_search_update_fn( UI_search_item_add(items, str, (void *)str, ICON_X, 0, 0); } - /* Skip the filter when the menu is first opened, so all of the items are visible. */ - if (is_first) { - for (const std::string &attribute_name : attribute_name_hints) { - /* Just use the pointer to the name string as the search data, - * since it's not used anyway but we need a pointer. */ - UI_search_item_add(items, attribute_name.c_str(), (void *)&attribute_name, ICON_NONE, 0, 0); - } - return; - } + /* Don't filter when the menu is first opened, but still run the search + * so the items are in the same order they will appear in while searching. */ + const char *string = is_first ? "" : str; StringSearch *search = BLI_string_search_new(); for (const std::string &attribute_name : attribute_name_hints) { @@ -91,7 +85,7 @@ static void attribute_search_update_fn( } std::string **filtered_items; - const int filtered_amount = BLI_string_search_query(search, str, (void ***)&filtered_items); + const int filtered_amount = BLI_string_search_query(search, string, (void ***)&filtered_items); for (const int i : IndexRange(filtered_amount)) { std::string *item = filtered_items[i]; -- cgit v1.2.3 From 3bc406274b17c6232c0ba5f86d9be7f3449aa804 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Sat, 6 Mar 2021 13:03:30 +1100 Subject: Cleanup: comments --- source/blender/editors/space_outliner/tree/tree_element_id.hh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'source/blender/editors') diff --git a/source/blender/editors/space_outliner/tree/tree_element_id.hh b/source/blender/editors/space_outliner/tree/tree_element_id.hh index d1e672ed387..612c1cd4a6f 100644 --- a/source/blender/editors/space_outliner/tree/tree_element_id.hh +++ b/source/blender/editors/space_outliner/tree/tree_element_id.hh @@ -32,7 +32,7 @@ class TreeElementID : public AbstractTreeElement { /** * Expanding not implemented for all types yet. Once it is, this can be set to true or - * `AbstractTreeElement::expandValid()` can be removed alltogether. + * `AbstractTreeElement::expandValid()` can be removed altogether. */ bool isExpandValid() const override { -- cgit v1.2.3