diff options
31 files changed, 671 insertions, 477 deletions
diff --git a/intern/cycles/util/util_ssei.h b/intern/cycles/util/util_ssei.h index ce8f7de3fa2..cd51dbff2f1 100644 --- a/intern/cycles/util/util_ssei.h +++ b/intern/cycles/util/util_ssei.h @@ -457,7 +457,8 @@ template<size_t i0, size_t i1, size_t i2, size_t i3> __forceinline const ssei shuffle(const ssei &a, const ssei &b) { # ifdef __KERNEL_NEON__ - int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a), vreinterpretq_s32_m128i(b)); + int32x4_t result = shuffle_neon<int32x4_t, i0, i1, i2, i3>(vreinterpretq_s32_m128i(a), + vreinterpretq_s32_m128i(b)); return vreinterpretq_m128i_s32(result); # else return _mm_castps_si128( diff --git a/release/scripts/addons b/release/scripts/addons -Subproject 81815ea92c2071a08566dc66d4a871b6e2f5c86 +Subproject 4cb833e84acfd2be5fa08ce75118ce9cb60643b diff --git a/release/scripts/startup/bl_ui/space_info.py b/release/scripts/startup/bl_ui/space_info.py index cd65980fc0d..3a97b104271 100644 --- a/release/scripts/startup/bl_ui/space_info.py +++ b/release/scripts/startup/bl_ui/space_info.py @@ -92,16 +92,15 @@ class INFO_MT_area(Menu): layout.separator() - layout.operator("screen.area_dupli", icon='WINDOW') - - layout.separator() - layout.operator("screen.screen_full_area") layout.operator( "screen.screen_full_area", - text="Toggle Fullscreen Area", - icon='FULLSCREEN_ENTER', - ).use_hide_panels = True + text="Toggle Fullscreen Area").use_hide_panels = True + layout.operator("screen.area_dupli") + + layout.separator() + + layout.operator("screen.area_close") class INFO_MT_context_menu(Menu): diff --git a/source/blender/blenkernel/BKE_attribute_access.hh b/source/blender/blenkernel/BKE_attribute_access.hh index f6a6de04b70..5feae1bf4ca 100644 --- a/source/blender/blenkernel/BKE_attribute_access.hh +++ b/source/blender/blenkernel/BKE_attribute_access.hh @@ -94,6 +94,7 @@ class OutputAttribute { SaveFn save_; std::optional<fn::GVMutableArray_GSpan> optional_span_varray_; bool ignore_old_values_ = false; + bool save_has_been_called_ = false; public: OutputAttribute() = default; @@ -109,6 +110,10 @@ class OutputAttribute { { } + OutputAttribute(OutputAttribute &&other) = default; + + ~OutputAttribute(); + operator bool() const { return varray_.get() != nullptr; diff --git a/source/blender/blenkernel/BKE_collection.h b/source/blender/blenkernel/BKE_collection.h index 3412be92a3a..7963d54126e 100644 --- a/source/blender/blenkernel/BKE_collection.h +++ b/source/blender/blenkernel/BKE_collection.h @@ -112,7 +112,9 @@ bool BKE_scene_collections_object_remove(struct Main *bmain, struct Object *object, const bool free_us); void BKE_collections_object_remove_nulls(struct Main *bmain); -void BKE_collections_child_remove_nulls(struct Main *bmain, struct Collection *old_collection); +void BKE_collections_child_remove_nulls(struct Main *bmain, + struct Collection *parent_collection, + struct Collection *child_collection); /* Dependencies. */ diff --git a/source/blender/blenkernel/intern/attribute_access.cc b/source/blender/blenkernel/intern/attribute_access.cc index 32a4885572d..c24630c5666 100644 --- a/source/blender/blenkernel/intern/attribute_access.cc +++ b/source/blender/blenkernel/intern/attribute_access.cc @@ -182,6 +182,7 @@ AttributeDomain attribute_domain_highest_priority(Span<AttributeDomain> domains) void OutputAttribute::save() { + save_has_been_called_ = true; if (optional_span_varray_.has_value()) { optional_span_varray_->save(); } @@ -190,6 +191,15 @@ void OutputAttribute::save() } } +OutputAttribute::~OutputAttribute() +{ + if (!save_has_been_called_) { + if (varray_) { + std::cout << "Warning: Call `save()` to make sure that changes persist in all cases.\n"; + } + } +} + GVArrayPtr BuiltinCustomDataLayerProvider::try_get_for_read( const GeometryComponent &component) const { diff --git a/source/blender/blenkernel/intern/collection.c b/source/blender/blenkernel/intern/collection.c index 89bb7b36406..b29dd007c51 100644 --- a/source/blender/blenkernel/intern/collection.c +++ b/source/blender/blenkernel/intern/collection.c @@ -1302,41 +1302,50 @@ static void collection_missing_parents_remove(Collection *collection) * * \note caller must ensure #BKE_main_collection_sync_remap() is called afterwards! * - * \param collection: may be \a NULL, + * \param parent_collection: The collection owning the pointers that were remapped. May be \a NULL, + * in which case whole \a bmain database of collections is checked. + * \param child_collection: The collection that was remapped to another pointer. May be \a NULL, * in which case whole \a bmain database of collections is checked. */ -void BKE_collections_child_remove_nulls(Main *bmain, Collection *collection) -{ - if (collection == NULL) { - /* We need to do the checks in two steps when more than one collection may be involved, - * otherwise we can miss some cases... - * Also, master collections are not in bmain, so we also need to loop over scenes. - */ - for (collection = bmain->collections.first; collection != NULL; - collection = collection->id.next) { - collection_null_children_remove(collection); - } - for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { - collection_null_children_remove(scene->master_collection); +void BKE_collections_child_remove_nulls(Main *bmain, + Collection *parent_collection, + Collection *child_collection) +{ + if (child_collection == NULL) { + if (parent_collection != NULL) { + collection_null_children_remove(parent_collection); + } + else { + /* We need to do the checks in two steps when more than one collection may be involved, + * otherwise we can miss some cases... + * Also, master collections are not in bmain, so we also need to loop over scenes. + */ + for (child_collection = bmain->collections.first; child_collection != NULL; + child_collection = child_collection->id.next) { + collection_null_children_remove(child_collection); + } + for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { + collection_null_children_remove(scene->master_collection); + } } - for (collection = bmain->collections.first; collection != NULL; - collection = collection->id.next) { - collection_missing_parents_remove(collection); + for (child_collection = bmain->collections.first; child_collection != NULL; + child_collection = child_collection->id.next) { + collection_missing_parents_remove(child_collection); } for (Scene *scene = bmain->scenes.first; scene != NULL; scene = scene->id.next) { collection_missing_parents_remove(scene->master_collection); } } else { - for (CollectionParent *parent = collection->parents.first, *parent_next; parent; + for (CollectionParent *parent = child_collection->parents.first, *parent_next; parent; parent = parent_next) { parent_next = parent->next; collection_null_children_remove(parent->collection); - if (!collection_find_child(parent->collection, collection)) { - BLI_freelinkN(&collection->parents, parent); + if (!collection_find_child(parent->collection, child_collection)) { + BLI_freelinkN(&child_collection->parents, parent); } } } diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index e54c3716660..2ecd0e6bd85 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -529,7 +529,7 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, /* For every corner, mix the values from the adjacent edges on the face. */ for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { - const int loop_index_prev = (loop_index - 1) % poly.totloop; + const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop; const MLoop &loop = mesh.mloop[loop_index]; const MLoop &loop_prev = mesh.mloop[loop_index_prev]; mixer.mix_in(loop_index, old_values[loop.e]); diff --git a/source/blender/blenkernel/intern/lib_remap.c b/source/blender/blenkernel/intern/lib_remap.c index 1f597bbb9a6..b32b97dc250 100644 --- a/source/blender/blenkernel/intern/lib_remap.c +++ b/source/blender/blenkernel/intern/lib_remap.c @@ -303,6 +303,7 @@ static void libblock_remap_data_postprocess_object_update(Main *bmain, /* Can be called with both old_collection and new_collection being NULL, * this means we have to check whole Main database then. */ static void libblock_remap_data_postprocess_collection_update(Main *bmain, + Collection *owner_collection, Collection *UNUSED(old_collection), Collection *new_collection) { @@ -311,7 +312,7 @@ static void libblock_remap_data_postprocess_collection_update(Main *bmain, * and BKE_main_collection_sync_remap() does not tolerate any of those, so for now always check * whole existing collections for NULL pointers. * I'd consider optimizing that whole collection remapping process a TODO for later. */ - BKE_collections_child_remove_nulls(bmain, NULL /*old_collection*/); + BKE_collections_child_remove_nulls(bmain, owner_collection, NULL /*old_collection*/); } else { /* Temp safe fix, but a "tad" brute force... We should probably be able to use parents from @@ -523,7 +524,7 @@ void BKE_libblock_remap_locked(Main *bmain, void *old_idv, void *new_idv, const break; case ID_GR: libblock_remap_data_postprocess_collection_update( - bmain, (Collection *)old_id, (Collection *)new_id); + bmain, NULL, (Collection *)old_id, (Collection *)new_id); break; case ID_ME: case ID_CU: @@ -628,6 +629,12 @@ void BKE_libblock_relink_ex( switch (GS(id->name)) { case ID_SCE: case ID_GR: { + /* Note: here we know which collection we have affected, so at lest for NULL children + * detection we can only process that one. + * This is also a required fix in case `id` would not be in Main anymore, which can happen + * e.g. when called from `id_delete`. */ + Collection *owner_collection = (GS(id->name) == ID_GR) ? (Collection *)id : + ((Scene *)id)->master_collection; if (old_id) { switch (GS(old_id->name)) { case ID_OB: @@ -636,7 +643,7 @@ void BKE_libblock_relink_ex( break; case ID_GR: libblock_remap_data_postprocess_collection_update( - bmain, (Collection *)old_id, (Collection *)new_id); + bmain, owner_collection, (Collection *)old_id, (Collection *)new_id); break; default: break; @@ -644,7 +651,7 @@ void BKE_libblock_relink_ex( } else { /* No choice but to check whole objects/collections. */ - libblock_remap_data_postprocess_collection_update(bmain, NULL, NULL); + libblock_remap_data_postprocess_collection_update(bmain, owner_collection, NULL, NULL); libblock_remap_data_postprocess_object_update(bmain, NULL, NULL); } break; diff --git a/source/blender/blenlib/BLI_compiler_attrs.h b/source/blender/blenlib/BLI_compiler_attrs.h index 680c4bc78da..4b5a7d671f2 100644 --- a/source/blender/blenlib/BLI_compiler_attrs.h +++ b/source/blender/blenlib/BLI_compiler_attrs.h @@ -98,3 +98,10 @@ #else # define ATTR_ALIGN(x) __attribute__((aligned(x))) #endif + +/* Alignment directive */ +#ifdef _WIN64 +# define ALIGN_STRUCT __declspec(align(64)) +#else +# define ALIGN_STRUCT +#endif diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc index 7a0c1b5b693..7893e8c64c1 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.cc @@ -33,6 +33,7 @@ namespace blender::deg { RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph) : have_backup(false), + id_data({nullptr}), animation_backup(depsgraph), scene_backup(depsgraph), sound_backup(depsgraph), @@ -51,6 +52,8 @@ void RuntimeBackup::init_from_id(ID *id) } have_backup = true; + id_data.py_instance = id->py_instance; + animation_backup.init_from_id(id); const ID_Type id_type = GS(id->name); @@ -89,6 +92,8 @@ void RuntimeBackup::restore_to_id(ID *id) return; } + id->py_instance = id_data.py_instance; + animation_backup.restore_to_id(id); const ID_Type id_type = GS(id->name); diff --git a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h index c6249c83daa..0629dbe62b4 100644 --- a/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h +++ b/source/blender/depsgraph/intern/eval/deg_eval_runtime_backup.h @@ -58,6 +58,11 @@ class RuntimeBackup { * copy-on-write mechanism. */ bool have_backup; + /* Struct members of the ID pointer. */ + struct { + void *py_instance; + } id_data; + AnimationBackup animation_backup; SceneBackup scene_backup; SoundBackup sound_backup; diff --git a/source/blender/draw/engines/eevee/eevee_materials.c b/source/blender/draw/engines/eevee/eevee_materials.c index f05aa562e6b..131f9a954cf 100644 --- a/source/blender/draw/engines/eevee/eevee_materials.c +++ b/source/blender/draw/engines/eevee/eevee_materials.c @@ -765,12 +765,15 @@ static void eevee_hair_cache_populate(EEVEE_Data *vedata, if (matcache.depth_grp) { *matcache.depth_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.depth_grp); + DRW_shgroup_add_material_resources(*matcache.depth_grp_p, matcache.shading_gpumat); } if (matcache.shading_grp) { *matcache.shading_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shading_grp); + DRW_shgroup_add_material_resources(*matcache.shading_grp_p, matcache.shading_gpumat); } if (matcache.shadow_grp) { *matcache.shadow_grp_p = DRW_shgroup_hair_create_sub(ob, psys, md, matcache.shadow_grp); + DRW_shgroup_add_material_resources(*matcache.shadow_grp_p, matcache.shading_gpumat); *cast_shadow = true; } diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c index a2a69d94ce9..d90f74fbf4f 100644 --- a/source/blender/editors/gpencil/gpencil_edit.c +++ b/source/blender/editors/gpencil/gpencil_edit.c @@ -1818,18 +1818,13 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_active_object(C); bGPdata *gpd = (bGPdata *)ob->data; - Scene *scene = CTX_data_scene(C); bGPDlayer *target_layer = NULL; ListBase strokes = {NULL, NULL}; int layer_num = RNA_int_get(op->ptr, "layer"); const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS); + const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd); - if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) { - BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition"); - return OPERATOR_CANCELLED; - } - - /* if autolock enabled, disabled now */ + /* If autolock enabled, disabled now. */ if (use_autolock) { gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS; } @@ -1852,53 +1847,59 @@ static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* Extract all strokes to move to this layer - * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes - * getting repeatedly moved - */ - CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) { - bGPDframe *gpf = gpl->actframe; - - /* skip if no frame with strokes, or if this is the layer we're moving strokes to */ - if ((gpl == target_layer) || (gpf == NULL)) { + /* Extract all strokes to move to this layer. */ + CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) { + /* Skip if this is the layer we're moving strokes to. */ + if (gpl_src == target_layer) { continue; } + bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe; + for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) { + if ((gpf_src == gpl_src->actframe) || + ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) { + if (gpf_src == NULL) { + continue; + } - /* make copies of selected strokes, and deselect these once we're done */ - LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) { + bGPDstroke *gpsn = NULL; + BLI_listbase_clear(&strokes); + for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) { + gpsn = gps->next; + /* Skip strokes that are invalid for current view. */ + if (ED_gpencil_stroke_can_use(C, gps) == false) { + continue; + } + /* Check if the color is editable. */ + if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) { + continue; + } - /* skip strokes that are invalid for current view */ - if (ED_gpencil_stroke_can_use(C, gps) == false) { - continue; - } + if (gps->flag & GP_STROKE_SELECT) { + BLI_remlink(&gpf_src->strokes, gps); + BLI_addtail(&strokes, gps); + } + } + /* Paste them all in one go. */ + if (strokes.first) { + bGPDframe *gpf_dst = BKE_gpencil_layer_frame_get( + target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW); - /* Check if the color is editable. */ - if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) { - continue; + BLI_movelisttolist(&gpf_dst->strokes, &strokes); + BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); + } } - - /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */ - if (gps->flag & GP_STROKE_SELECT) { - BLI_remlink(&gpf->strokes, gps); - BLI_addtail(&strokes, gps); + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; } } - - /* if new layer and autolock, lock old layer */ + /* If new layer and autolock, lock old layer. */ if ((layer_num == -1) && (use_autolock)) { - gpl->flag |= GP_LAYER_LOCKED; + gpl_src->flag |= GP_LAYER_LOCKED; } } CTX_DATA_END; - /* Paste them all in one go */ - if (strokes.first) { - bGPDframe *gpf = BKE_gpencil_layer_frame_get(target_layer, CFRA, GP_GETFRAME_ADD_NEW); - - BLI_movelisttolist(&gpf->strokes, &strokes); - BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL)); - } - /* back autolock status */ if (use_autolock) { gpd->flag |= GP_DATA_AUTOLOCK_LAYERS; @@ -3814,13 +3815,13 @@ static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op) } changed = true; - /* If not multi-edit, exit loop. */ - if (!is_multiedit) { - break; - } } } } + /* If not multi-edit, exit loop. */ + if (!is_multiedit) { + break; + } } } CTX_DATA_END; diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index b3205acb8ee..169b25a1358 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -200,7 +200,7 @@ ScrArea *ED_screen_areas_iter_next(const bScreen *screen, const ScrArea *area); /* screens */ void ED_screens_init(struct Main *bmain, struct wmWindowManager *wm); void ED_screen_draw_edges(struct wmWindow *win); -void ED_screen_draw_join_shape(struct ScrArea *sa1, struct ScrArea *sa2); +void ED_screen_draw_join_highlight(struct ScrArea *sa1, struct ScrArea *sa2); void ED_screen_draw_split_preview(struct ScrArea *area, const int dir, const float fac); void ED_screen_refresh(struct wmWindowManager *wm, struct wmWindow *win); void ED_screen_ensure_updated(struct wmWindowManager *wm, diff --git a/source/blender/editors/interface/interface_handlers.c b/source/blender/editors/interface/interface_handlers.c index 4cbf5fca49a..5a254db0eec 100644 --- a/source/blender/editors/interface/interface_handlers.c +++ b/source/blender/editors/interface/interface_handlers.c @@ -1114,16 +1114,17 @@ static void ui_apply_but_TAB(bContext *C, uiBut *but, uiHandleButtonData *data) static void ui_apply_but_NUM(bContext *C, uiBut *but, uiHandleButtonData *data) { if (data->str) { - if (ui_but_string_set(C, but, data->str)) { - data->value = ui_but_value_get(but); - } - else { + double value; + /* Check if the string value is a number and cancel if it's equal to the startvalue. */ + if (ui_but_string_eval_number(C, but, data->str, &value) && (value == data->startvalue)) { data->cancel = true; return; } - /* If the value entered is the exact same, do not trigger an update. */ - if (data->value == data->startvalue) { + if (ui_but_string_set(C, but, data->str)) { + data->value = ui_but_value_get(but); + } + else { data->cancel = true; return; } diff --git a/source/blender/editors/interface/interface_widgets.c b/source/blender/editors/interface/interface_widgets.c index e351cd30c14..8dbebe2a94c 100644 --- a/source/blender/editors/interface/interface_widgets.c +++ b/source/blender/editors/interface/interface_widgets.c @@ -2420,7 +2420,8 @@ static void widget_draw_text_icon(const uiFontStyle *fstyle, } if (!no_text_padding) { - const int text_padding = (UI_TEXT_MARGIN_X * U.widget_unit) / but->block->aspect; + const int text_padding = round_fl_to_int((UI_TEXT_MARGIN_X * U.widget_unit) / + but->block->aspect); if (but->editstr) { rect->xmin += text_padding; } diff --git a/source/blender/editors/screen/screen_draw.c b/source/blender/editors/screen/screen_draw.c index 2ba7ef8f972..6d1409a9044 100644 --- a/source/blender/editors/screen/screen_draw.c +++ b/source/blender/editors/screen/screen_draw.c @@ -33,184 +33,11 @@ #include "WM_api.h" +#include "UI_interface.h" #include "UI_resources.h" #include "screen_intern.h" -/** - * Draw horizontal shape visualizing future joining - * (left as well right direction of future joining). - */ -static void draw_horizontal_join_shape(ScrArea *area, char dir, uint pos) -{ - const float width = screen_geom_area_width(area) - 1; - const float height = screen_geom_area_height(area) - 1; - - float w, h; - if (height < width) { - h = height / 8; - w = height / 4; - } - else { - h = width / 8; - w = width / 4; - } - - vec2f points[10]; - points[0].x = area->v1->vec.x; - points[0].y = area->v1->vec.y + height / 2; - - points[1].x = area->v1->vec.x; - points[1].y = area->v1->vec.y; - - points[2].x = area->v4->vec.x - w; - points[2].y = area->v4->vec.y; - - points[3].x = area->v4->vec.x - w; - points[3].y = area->v4->vec.y + height / 2 - 2 * h; - - points[4].x = area->v4->vec.x - 2 * w; - points[4].y = area->v4->vec.y + height / 2; - - points[5].x = area->v4->vec.x - w; - points[5].y = area->v4->vec.y + height / 2 + 2 * h; - - points[6].x = area->v3->vec.x - w; - points[6].y = area->v3->vec.y; - - points[7].x = area->v2->vec.x; - points[7].y = area->v2->vec.y; - - points[8].x = area->v4->vec.x; - points[8].y = area->v4->vec.y + height / 2 - h; - - points[9].x = area->v4->vec.x; - points[9].y = area->v4->vec.y + height / 2 + h; - - if (dir == 'l') { - /* when direction is left, then we flip direction of arrow */ - float cx = area->v1->vec.x + width; - for (int i = 0; i < 10; i++) { - points[i].x -= cx; - points[i].x = -points[i].x; - points[i].x += area->v1->vec.x; - } - } - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 0; i < 5; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immEnd(); - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 4; i < 8; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immVertex2f(pos, points[0].x, points[0].y); - immEnd(); - - immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y); - immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y); -} - -/** - * Draw vertical shape visualizing future joining (up/down direction). - */ -static void draw_vertical_join_shape(ScrArea *area, char dir, uint pos) -{ - const float width = screen_geom_area_width(area) - 1; - const float height = screen_geom_area_height(area) - 1; - - float w, h; - if (height < width) { - h = height / 4; - w = height / 8; - } - else { - h = width / 4; - w = width / 8; - } - - vec2f points[10]; - points[0].x = area->v1->vec.x + width / 2; - points[0].y = area->v3->vec.y; - - points[1].x = area->v2->vec.x; - points[1].y = area->v2->vec.y; - - points[2].x = area->v1->vec.x; - points[2].y = area->v1->vec.y + h; - - points[3].x = area->v1->vec.x + width / 2 - 2 * w; - points[3].y = area->v1->vec.y + h; - - points[4].x = area->v1->vec.x + width / 2; - points[4].y = area->v1->vec.y + 2 * h; - - points[5].x = area->v1->vec.x + width / 2 + 2 * w; - points[5].y = area->v1->vec.y + h; - - points[6].x = area->v4->vec.x; - points[6].y = area->v4->vec.y + h; - - points[7].x = area->v3->vec.x; - points[7].y = area->v3->vec.y; - - points[8].x = area->v1->vec.x + width / 2 - w; - points[8].y = area->v1->vec.y; - - points[9].x = area->v1->vec.x + width / 2 + w; - points[9].y = area->v1->vec.y; - - if (dir == 'u') { - /* when direction is up, then we flip direction of arrow */ - float cy = area->v1->vec.y + height; - for (int i = 0; i < 10; i++) { - points[i].y -= cy; - points[i].y = -points[i].y; - points[i].y += area->v1->vec.y; - } - } - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 0; i < 5; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immEnd(); - - immBegin(GPU_PRIM_TRI_FAN, 5); - - for (int i = 4; i < 8; i++) { - immVertex2f(pos, points[i].x, points[i].y); - } - - immVertex2f(pos, points[0].x, points[0].y); - immEnd(); - - immRectf(pos, points[2].x, points[2].y, points[8].x, points[8].y); - immRectf(pos, points[6].x, points[6].y, points[9].x, points[9].y); -} - -/** - * Draw join shape due to direction of joining. - */ -static void draw_join_shape(ScrArea *area, char dir, uint pos) -{ - if (ELEM(dir, 'u', 'd')) { - draw_vertical_join_shape(area, dir, pos); - } - else { - draw_horizontal_join_shape(area, dir, pos); - } -} - #define CORNER_RESOLUTION 3 static void do_vert_pair(GPUVertBuf *vbo, uint pos, uint *vidx, int corner, int i) @@ -290,28 +117,6 @@ static GPUBatch *batch_screen_edges_get(int *corner_len) #undef CORNER_RESOLUTION -/** - * Draw screen area darker with arrow (visualization of future joining). - */ -static void scrarea_draw_shape_dark(ScrArea *area, char dir, uint pos) -{ - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ub(0, 0, 0, 50); - - draw_join_shape(area, dir, pos); -} - -/** - * Draw screen area lighter with arrow shape ("eraser" of previous dark shape). - */ -static void scrarea_draw_shape_light(ScrArea *area, char UNUSED(dir), uint pos) -{ - GPU_blend(GPU_BLEND_ALPHA); - immUniformColor4ub(255, 255, 255, 25); - - immRectf(pos, area->v1->vec.x, area->v1->vec.y, area->v3->vec.x, area->v3->vec.y); -} - static void drawscredge_area_draw( int sizex, int sizey, short x1, short y1, short x2, short y2, float edge_thickness) { @@ -427,50 +232,92 @@ void ED_screen_draw_edges(wmWindow *win) } /** - * The blended join arrows. + * Visual indication of the two areas involved in a proposed join. * * \param sa1: Area from which the resultant originates. * \param sa2: Target area that will be replaced. */ -void ED_screen_draw_join_shape(ScrArea *sa1, ScrArea *sa2) +void ED_screen_draw_join_highlight(ScrArea *sa1, ScrArea *sa2) { - uint pos = GPU_vertformat_attr_add(immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); - immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); - - GPU_line_width(1); - - /* blended join arrow */ int dir = area_getorientation(sa1, sa2); - int dira = -1; - if (dir != -1) { - switch (dir) { - case 0: /* W */ - dir = 'r'; - dira = 'l'; - break; - case 1: /* N */ - dir = 'd'; - dira = 'u'; - break; - case 2: /* E */ - dir = 'l'; - dira = 'r'; - break; - case 3: /* S */ - dir = 'u'; - dira = 'd'; - break; - } + if (dir == -1) { + return; + } - GPU_blend(GPU_BLEND_ALPHA); + /* Rect of the combined areas.*/ + bool vertical = ELEM(dir, 1, 3); + rctf combined = {.xmin = vertical ? MAX2(sa1->totrct.xmin, sa2->totrct.xmin) : + MIN2(sa1->totrct.xmin, sa2->totrct.xmin), + .xmax = vertical ? MIN2(sa1->totrct.xmax, sa2->totrct.xmax) : + MAX2(sa1->totrct.xmax, sa2->totrct.xmax), + .ymin = vertical ? MIN2(sa1->totrct.ymin, sa2->totrct.ymin) : + MAX2(sa1->totrct.ymin, sa2->totrct.ymin), + .ymax = vertical ? MAX2(sa1->totrct.ymax, sa2->totrct.ymax) : + MIN2(sa1->totrct.ymax, sa2->totrct.ymax)}; - scrarea_draw_shape_dark(sa2, dir, pos); - scrarea_draw_shape_light(sa1, dira, pos); + uint pos_id = GPU_vertformat_attr_add( + immVertexFormat(), "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT); + immBindBuiltinProgram(GPU_SHADER_2D_UNIFORM_COLOR); + GPU_blend(GPU_BLEND_ALPHA); - GPU_blend(GPU_BLEND_NONE); + /* Highlight source (sa1) within combined area. */ + immUniformColor4fv((const float[4]){1.0f, 1.0f, 1.0f, 0.10f}); + immRectf(pos_id, + MAX2(sa1->totrct.xmin, combined.xmin), + MAX2(sa1->totrct.ymin, combined.ymin), + MIN2(sa1->totrct.xmax, combined.xmax), + MIN2(sa1->totrct.ymax, combined.ymax)); + + /* Highlight destination (sa2) within combined area. */ + immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.25f}); + immRectf(pos_id, + MAX2(sa2->totrct.xmin, combined.xmin), + MAX2(sa2->totrct.ymin, combined.ymin), + MIN2(sa2->totrct.xmax, combined.xmax), + MIN2(sa2->totrct.ymax, combined.ymax)); + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + if (offset1 < 0 || offset2 > 0) { + /* Show partial areas that will be closed. */ + immUniformColor4fv((const float[4]){0.0f, 0.0f, 0.0f, 0.8f}); + if (vertical) { + if (sa1->totrct.xmin < combined.xmin) { + immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymin, combined.xmin, sa1->totrct.ymax); + } + if (sa2->totrct.xmin < combined.xmin) { + immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymin, combined.xmin, sa2->totrct.ymax); + } + if (sa1->totrct.xmax > combined.xmax) { + immRectf(pos_id, combined.xmax, sa1->totrct.ymin, sa1->totrct.xmax, sa1->totrct.ymax); + } + if (sa2->totrct.xmax > combined.xmax) { + immRectf(pos_id, combined.xmax, sa2->totrct.ymin, sa2->totrct.xmax, sa2->totrct.ymax); + } + } + else { + if (sa1->totrct.ymin < combined.ymin) { + immRectf(pos_id, sa1->totrct.xmin, combined.ymin, sa1->totrct.xmax, sa1->totrct.ymin); + } + if (sa2->totrct.ymin < combined.ymin) { + immRectf(pos_id, sa2->totrct.xmin, combined.ymin, sa2->totrct.xmax, sa2->totrct.ymin); + } + if (sa1->totrct.ymax > combined.ymax) { + immRectf(pos_id, sa1->totrct.xmin, sa1->totrct.ymax, sa1->totrct.xmax, combined.ymax); + } + if (sa2->totrct.ymax > combined.ymax) { + immRectf(pos_id, sa2->totrct.xmin, sa2->totrct.ymax, sa2->totrct.xmax, combined.ymax); + } + } } immUnbindProgram(); + GPU_blend(GPU_BLEND_NONE); + + /* Outline the combined area. */ + UI_draw_roundbox_corner_set(UI_CNR_ALL); + UI_draw_roundbox_4fv(&combined, false, 7 * U.pixelsize, (float[4]){1.0f, 1.0f, 1.0f, 0.8f}); } void ED_screen_draw_split_preview(ScrArea *area, const int dir, const float fac) diff --git a/source/blender/editors/screen/screen_edit.c b/source/blender/editors/screen/screen_edit.c index 7ad8eada3b9..8e51772d801 100644 --- a/source/blender/editors/screen/screen_edit.c +++ b/source/blender/editors/screen/screen_edit.c @@ -284,42 +284,37 @@ void screen_new_activate_prepare(const wmWindow *win, bScreen *screen_new) /* used with join operator */ int area_getorientation(ScrArea *area, ScrArea *sb) { - if (area == NULL || sb == NULL) { + if (area == NULL || sb == NULL || area == sb) { return -1; } - ScrVert *saBL = area->v1; - ScrVert *saTL = area->v2; - ScrVert *saTR = area->v3; - ScrVert *saBR = area->v4; + vec2s saBL = area->v1->vec; + vec2s saTL = area->v2->vec; + vec2s saTR = area->v3->vec; + vec2s saBR = area->v4->vec; - ScrVert *sbBL = sb->v1; - ScrVert *sbTL = sb->v2; - ScrVert *sbTR = sb->v3; - ScrVert *sbBR = sb->v4; + vec2s sbBL = sb->v1->vec; + vec2s sbTL = sb->v2->vec; + vec2s sbTR = sb->v3->vec; + vec2s sbBR = sb->v4->vec; - if (saBL->vec.x == sbBR->vec.x && saTL->vec.x == sbTR->vec.x) { /* area to right of sb = W */ - if ((abs(saBL->vec.y - sbBR->vec.y) <= AREAJOINTOLERANCE) && - (abs(saTL->vec.y - sbTR->vec.y) <= AREAJOINTOLERANCE)) { + if (saBL.x == sbBR.x && saTL.x == sbTR.x) { /* area to right of sb = W */ + if ((MIN2(saTL.y, sbTR.y) - MAX2(saBL.y, sbBR.y)) > AREAJOINTOLERANCEY) { return 0; } } - else if (saTL->vec.y == sbBL->vec.y && - saTR->vec.y == sbBR->vec.y) { /* area to bottom of sb = N */ - if ((abs(saTL->vec.x - sbBL->vec.x) <= AREAJOINTOLERANCE) && - (abs(saTR->vec.x - sbBR->vec.x) <= AREAJOINTOLERANCE)) { + else if (saTL.y == sbBL.y && saTR.y == sbBR.y) { /* area to bottom of sb = N */ + if ((MIN2(saTR.x, sbBR.x) - MAX2(saTL.x, sbBL.x)) > AREAJOINTOLERANCEX) { return 1; } } - else if (saTR->vec.x == sbTL->vec.x && saBR->vec.x == sbBL->vec.x) { /* area to left of sb = E */ - if ((abs(saTR->vec.y - sbTL->vec.y) <= AREAJOINTOLERANCE) && - (abs(saBR->vec.y - sbBL->vec.y) <= AREAJOINTOLERANCE)) { + else if (saTR.x == sbTL.x && saBR.x == sbBL.x) { /* area to left of sb = E */ + if ((MIN2(saTR.y, sbTL.y) - MAX2(saBR.y, sbBL.y)) > AREAJOINTOLERANCEY) { return 2; } } - else if (saBL->vec.y == sbTL->vec.y && saBR->vec.y == sbTR->vec.y) { /* area on top of sb = S*/ - if ((abs(saBL->vec.x - sbTL->vec.x) <= AREAJOINTOLERANCE) && - (abs(saBR->vec.x - sbTR->vec.x) <= AREAJOINTOLERANCE)) { + else if (saBL.y == sbTL.y && saBR.y == sbTR.y) { /* area on top of sb = S */ + if ((MIN2(saBR.x, sbTR.x) - MAX2(saBL.x, sbTL.x)) > AREAJOINTOLERANCEX) { return 3; } } @@ -327,6 +322,35 @@ int area_getorientation(ScrArea *area, ScrArea *sb) return -1; } +/* Get alignment offset of adjacent areas. 'dir' value is like area_getorientation(). */ +void area_getoffsets(ScrArea *area, ScrArea *sb, const int dir, int *offset1, int *offset2) +{ + if (area == NULL || sb == NULL) { + *offset1 = INT_MAX; + *offset2 = INT_MAX; + } + else if (dir == 0) { /* West: sa on right and sb to the left. */ + *offset1 = sb->v3->vec.y - area->v2->vec.y; + *offset2 = sb->v4->vec.y - area->v1->vec.y; + } + else if (dir == 1) { /* North: sa below and sb above. */ + *offset1 = area->v2->vec.x - sb->v1->vec.x; + *offset2 = area->v3->vec.x - sb->v4->vec.x; + } + else if (dir == 2) { /* East: sa on left and sb to the right. */ + *offset1 = sb->v2->vec.y - area->v3->vec.y; + *offset2 = sb->v1->vec.y - area->v4->vec.y; + } + else if (dir == 3) { /* South: sa above and sb below. */ + *offset1 = area->v1->vec.x - sb->v2->vec.x; + *offset2 = area->v4->vec.x - sb->v3->vec.x; + } + else { + *offset1 = INT_MAX; + *offset2 = INT_MAX; + } +} + /* Screen verts with horizontal position equal to from_x are moved to to_x. */ static void screen_verts_halign(const wmWindow *win, const bScreen *screen, @@ -390,18 +414,24 @@ static void screen_areas_align( } } -/* Helper function to join 2 areas, it has a return value, 0=failed 1=success - * used by the split, join operators - */ -int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) +/* Simple join of two areas without any splitting. Will return false if not possible. */ +static bool screen_area_join_aligned(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) { int dir = area_getorientation(sa1, sa2); - if (dir == -1) { - return 0; + return false; + } + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + + int tolerance = ELEM(dir, 0, 2) ? AREAJOINTOLERANCEY : AREAJOINTOLERANCEX; + if ((abs(offset1) >= tolerance) || (abs(offset2) >= tolerance)) { + return false; } - /* Align areas if they are not. Do sanity checking before getting here. */ + /* Align areas if they are not. */ screen_areas_align(C, screen, sa1, sa2, dir); if (dir == 0) { /* sa1 to right of sa2 = W */ @@ -434,7 +464,107 @@ int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) /* Update preview thumbnail */ BKE_icon_changed(screen->id.icon_id); - return 1; + return true; +} + +/* Slice off and return new area. "Reverse" gives right/bottom, rather than left/top. */ +static ScrArea *screen_area_trim( + bContext *C, bScreen *screen, ScrArea **area, int size, int dir, bool reverse) +{ + bool vertical = ELEM(dir, 1, 3); + if (abs(size) < (vertical ? AREAJOINTOLERANCEX : AREAJOINTOLERANCEY)) { + return NULL; + } + + /* Measurement with ScrVerts because winx and winy might not be correct at this time. */ + float fac = abs(size) / (float)(vertical ? ((*area)->v3->vec.x - (*area)->v1->vec.x) : + ((*area)->v3->vec.y - (*area)->v1->vec.y)); + fac = (reverse == vertical) ? 1.0f - fac : fac; + ScrArea *newsa = area_split(CTX_wm_window(C), screen, *area, vertical ? 'v' : 'h', fac, 1); + + /* area_split always returns smallest of the two areas, so might have to swap. */ + if (((fac > 0.5f) == vertical) != reverse) { + ScrArea *temp = *area; + *area = newsa; + newsa = temp; + } + + return newsa; +} + +/* Join any two neighboring areas. Might create new areas, kept if over min_remainder. */ +static bool screen_area_join_ex( + bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2, bool close_all_remainders) +{ + int dir = area_getorientation(sa1, sa2); + if (dir == -1) { + return false; + } + + int offset1; + int offset2; + area_getoffsets(sa1, sa2, dir, &offset1, &offset2); + + /* Split Left/Top into new area if overhanging. */ + ScrArea *side1 = screen_area_trim(C, screen, (offset1 > 0) ? &sa2 : &sa1, offset1, dir, false); + + /* Split Right/Bottom into new area if overhanging. */ + ScrArea *side2 = screen_area_trim(C, screen, (offset2 > 0) ? &sa1 : &sa2, offset2, dir, true); + + /* The two areas now line up, so join them. */ + screen_area_join_aligned(C, screen, sa1, sa2); + + if (close_all_remainders || offset1 < 0 || offset2 > 0) { + /* Close both if trimiming sa1. */ + screen_area_close(C, screen, side1); + screen_area_close(C, screen, side2); + } + + BKE_icon_changed(screen->id.icon_id); + return true; +} + +/* Join any two neighboring areas. Might involve complex changes. */ +int screen_area_join(bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2) +{ + return screen_area_join_ex(C, screen, sa1, sa2, false); +} + +/* Close a screen area, allowing any neighbor to take its place. */ +bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area) +{ + if (area == NULL) { + return false; + } + + ScrArea *sa2 = NULL; + + /* Find the most-aligned joinable area. Larger size breaks ties. */ + int min_alignment = INT_MAX; + int max_size = 0; + LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) { + int dir = area_getorientation(area, ar); + if (dir != -1) { + int offset1; + int offset2; + area_getoffsets(area, ar, dir, &offset1, &offset2); + int area_alignment = abs(offset1) + abs(offset2); + if (area_alignment < min_alignment) { + min_alignment = area_alignment; + max_size = ar->winx * ar->winy; + sa2 = ar; + } + else if (area_alignment == min_alignment) { + int area_size = ar->winx * ar->winy; + if (area_size > max_size) { + max_size = area_size; + sa2 = ar; + } + } + } + } + + return screen_area_join_ex(C, screen, sa2, area, true); } /* ****************** EXPORTED API TO OTHER MODULES *************************** */ diff --git a/source/blender/editors/screen/screen_intern.h b/source/blender/editors/screen/screen_intern.h index c51ff559786..cda7ba6a4a9 100644 --- a/source/blender/editors/screen/screen_intern.h +++ b/source/blender/editors/screen/screen_intern.h @@ -34,7 +34,9 @@ struct bContextDataResult; #define AZONEFADEIN (5.0f * U.widget_unit) /* when #AZone is totally visible */ #define AZONEFADEOUT (6.5f * U.widget_unit) /* when we start seeing the #AZone */ -#define AREAJOINTOLERANCE (1.0f * U.widget_unit) /* Edges must be close to allow joining. */ +/* Edges must be within these to allow joining. */ +#define AREAJOINTOLERANCEX (AREAMINX * U.dpi_fac) +#define AREAJOINTOLERANCEY (HEADERY * U.dpi_fac) /* Expanded interaction influence of area borders. */ #define BORDERPADDING (U.dpi_fac + U.pixelsize) @@ -58,7 +60,8 @@ ScrArea *area_split( const wmWindow *win, bScreen *screen, ScrArea *area, char dir, float fac, int merge); int screen_area_join(struct bContext *C, bScreen *screen, ScrArea *sa1, ScrArea *sa2); int area_getorientation(ScrArea *area, ScrArea *sb); - +void area_getoffsets(ScrArea *area, ScrArea *sb, const int dir, int *offset1, int *offset2); +bool screen_area_close(struct bContext *C, bScreen *screen, ScrArea *area); struct AZone *ED_area_actionzone_find_xy(ScrArea *area, const int xy[2]); /* screen_geometry.c */ diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 5cd4e8c353b..cc920aece0a 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -1387,6 +1387,58 @@ static void SCREEN_OT_area_dupli(wmOperatorType *ot) /** \} */ /* -------------------------------------------------------------------- */ +/** \name Area Close Operator + * + * Close selected area, replace by expanding a neighbor + * \{ */ + +/* operator callback */ +static int area_close_invoke(bContext *C, wmOperator *UNUSED(op), const wmEvent *UNUSED(event)) +{ + ScrArea *area = CTX_wm_area(C); + if ((area != NULL) && screen_area_close(C, CTX_wm_screen(C), area)) { + WM_event_add_notifier(C, NC_SCREEN | NA_EDITED, NULL); + return OPERATOR_FINISHED; + } + return OPERATOR_CANCELLED; +} + +static bool area_close_poll(bContext *C) +{ + if (!ED_operator_areaactive(C)) { + return false; + } + + ScrArea *area = CTX_wm_area(C); + + if (ED_area_is_global(area)) { + return false; + } + + bScreen *screen = CTX_wm_screen(C); + + /* Can this area join with ANY other area? */ + LISTBASE_FOREACH (ScrArea *, ar, &screen->areabase) { + if (area_getorientation(ar, area) != -1) { + return true; + } + } + + return false; +} + +static void SCREEN_OT_area_close(wmOperatorType *ot) +{ + ot->name = "Close Area"; + ot->description = "Close selected area"; + ot->idname = "SCREEN_OT_area_close"; + ot->invoke = area_close_invoke; + ot->poll = area_close_poll; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ /** \name Move Area Edge Operator * \{ */ @@ -3212,7 +3264,7 @@ static void SCREEN_OT_screen_full_area(wmOperatorType *ot) typedef struct sAreaJoinData { ScrArea *sa1; /* first area to be considered */ ScrArea *sa2; /* second area to be considered */ - void *draw_callback; /* call `ED_screen_draw_join_shape` */ + void *draw_callback; /* call 'ED_screen_draw_join_highlight' */ } sAreaJoinData; @@ -3222,7 +3274,7 @@ static void area_join_draw_cb(const struct wmWindow *UNUSED(win), void *userdata sAreaJoinData *sd = op->customdata; if (sd->sa1 && sd->sa2) { - ED_screen_draw_join_shape(sd->sa1, sd->sa2); + ED_screen_draw_join_highlight(sd->sa1, sd->sa2); } } @@ -4069,6 +4121,69 @@ static void SCREEN_OT_header_toggle_menus(wmOperatorType *ot) /** \name Region Context Menu Operator (Header/Footer/Navbar) * \{ */ +static void screen_area_menu_items(ScrArea *area, uiLayout *layout) +{ + if (ED_area_is_global(area)) { + return; + } + + PointerRNA ptr; + + /* Mouse position as if in middle of area. */ + const int loc[2] = {BLI_rcti_cent_x(&area->totrct), BLI_rcti_cent_y(&area->totrct)}; + + /* Vertical Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Vertical Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + + RNA_int_set_array(&ptr, "cursor", loc); + RNA_enum_set(&ptr, "direction", 'v'); + + /* Horizontal Split */ + uiItemFullO(layout, + "SCREEN_OT_area_split", + IFACE_("Horizontal Split"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + + RNA_int_set_array(&ptr, "cursor", &loc[0]); + RNA_enum_set(&ptr, "direction", 'h'); + + uiItemS(layout); + + if (area->spacetype != SPACE_FILE) { + uiItemO(layout, + area->full ? IFACE_("Restore Areas") : IFACE_("Maximize Area"), + ICON_NONE, + "SCREEN_OT_screen_full_area"); + + if (!area->full) { + uiItemFullO(layout, + "SCREEN_OT_screen_full_area", + IFACE_("Full Screen Area"), + ICON_NONE, + NULL, + WM_OP_INVOKE_DEFAULT, + 0, + &ptr); + RNA_boolean_set(&ptr, "use_hide_panels", true); + } + } + + uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_dupli"); + uiItemS(layout); + uiItemO(layout, NULL, ICON_NONE, "SCREEN_OT_area_close"); +} + void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) { ScrArea *area = CTX_wm_area(C); @@ -4102,17 +4217,9 @@ void ED_screens_header_tools_menu_create(bContext *C, uiLayout *layout, void *UN if (!ELEM(area->spacetype, SPACE_TOPBAR)) { uiItemS(layout); - uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); - } - - /* File browser should be fullscreen all the time, top-bar should - * never be. But other regions can be maximized/restored. */ - if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) { uiItemS(layout); - - const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); - uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); + screen_area_menu_items(area, layout); } } @@ -4134,14 +4241,8 @@ void ED_screens_footer_tools_menu_create(bContext *C, uiLayout *layout, void *UN uiItemO(layout, but_flip_str, ICON_NONE, "SCREEN_OT_region_flip"); - /* File browser should be fullscreen all the time, top-bar should - * never be. But other regions can be maximized/restored... */ - if (!ELEM(area->spacetype, SPACE_FILE, SPACE_TOPBAR)) { - uiItemS(layout); - - const char *but_str = area->full ? IFACE_("Tile Area") : IFACE_("Maximize Area"); - uiItemO(layout, but_str, ICON_NONE, "SCREEN_OT_screen_full_area"); - } + uiItemS(layout); + screen_area_menu_items(area, layout); } void ED_screens_navigation_bar_tools_menu_create(bContext *C, uiLayout *layout, void *UNUSED(arg)) @@ -5461,6 +5562,7 @@ void ED_operatortypes_screen(void) WM_operatortype_append(SCREEN_OT_area_move); WM_operatortype_append(SCREEN_OT_area_split); WM_operatortype_append(SCREEN_OT_area_join); + WM_operatortype_append(SCREEN_OT_area_close); WM_operatortype_append(SCREEN_OT_area_options); WM_operatortype_append(SCREEN_OT_area_dupli); WM_operatortype_append(SCREEN_OT_area_swap); diff --git a/source/blender/editors/space_sequencer/sequencer_edit.c b/source/blender/editors/space_sequencer/sequencer_edit.c index 52cfeeecb5c..7279fdd2da2 100644 --- a/source/blender/editors/space_sequencer/sequencer_edit.c +++ b/source/blender/editors/space_sequencer/sequencer_edit.c @@ -2274,43 +2274,45 @@ void SEQUENCER_OT_swap(wmOperatorType *ot) static int sequencer_rendersize_exec(bContext *C, wmOperator *UNUSED(op)) { - int retval = OPERATOR_CANCELLED; Scene *scene = CTX_data_scene(C); Sequence *active_seq = SEQ_select_active_get(scene); StripElem *se = NULL; - if (active_seq == NULL) { + if (active_seq == NULL || active_seq->strip == NULL) { return OPERATOR_CANCELLED; } - if (active_seq->strip) { - switch (active_seq->type) { - case SEQ_TYPE_IMAGE: - se = SEQ_render_give_stripelem(active_seq, scene->r.cfra); - break; - case SEQ_TYPE_MOVIE: - se = active_seq->strip->stripdata; - break; - case SEQ_TYPE_SCENE: - case SEQ_TYPE_META: - case SEQ_TYPE_SOUND_RAM: - case SEQ_TYPE_SOUND_HD: - default: - break; - } + switch (active_seq->type) { + case SEQ_TYPE_IMAGE: + se = SEQ_render_give_stripelem(active_seq, scene->r.cfra); + break; + case SEQ_TYPE_MOVIE: + se = active_seq->strip->stripdata; + break; + default: + return OPERATOR_CANCELLED; } - if (se) { - /* Prevent setting the render size if sequence values aren't initialized. */ - if ((se->orig_width > 0) && (se->orig_height > 0)) { - scene->r.xsch = se->orig_width; - scene->r.ysch = se->orig_height; - WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); - retval = OPERATOR_FINISHED; - } + if (se == NULL) { + return OPERATOR_CANCELLED; + } + + /* Prevent setting the render size if sequence values aren't initialized. */ + if (se->orig_width <= 0 || se->orig_height <= 0) { + return OPERATOR_CANCELLED; } - return retval; + scene->r.xsch = se->orig_width; + scene->r.ysch = se->orig_height; + + active_seq->strip->transform->scale_x = active_seq->strip->transform->scale_y = 1.0f; + active_seq->strip->transform->xofs = active_seq->strip->transform->yofs = 0.0f; + + SEQ_relations_invalidate_cache_preprocessed(scene, active_seq); + WM_event_add_notifier(C, NC_SCENE | ND_RENDER_OPTIONS, scene); + WM_event_add_notifier(C, NC_SPACE | ND_SPACE_SEQUENCER, NULL); + + return OPERATOR_FINISHED; } void SEQUENCER_OT_rendersize(wmOperatorType *ot) diff --git a/source/blender/editors/space_view3d/view3d_placement.c b/source/blender/editors/space_view3d/view3d_placement.c index 1afdcdd2993..00e30f8afae 100644 --- a/source/blender/editors/space_view3d/view3d_placement.c +++ b/source/blender/editors/space_view3d/view3d_placement.c @@ -2061,7 +2061,7 @@ static void cursor_plane_draw(bContext *C, int x, int y, void *customdata) GPU_matrix_projection_set(rv3d->winmat); GPU_matrix_set(rv3d->viewmat); - const float scale_mod = U.gizmo_size * 2 * U.dpi_fac; + const float scale_mod = U.gizmo_size * 2 * U.dpi_fac / U.pixelsize; float final_scale = (scale_mod * pixel_size); diff --git a/source/blender/makesdna/DNA_gpencil_types.h b/source/blender/makesdna/DNA_gpencil_types.h index 8facdca2f9c..bf7b9d70fb6 100644 --- a/source/blender/makesdna/DNA_gpencil_types.h +++ b/source/blender/makesdna/DNA_gpencil_types.h @@ -47,7 +47,7 @@ struct Curve; #define GP_DEFAULT_CURVE_EDIT_CORNER_ANGLE M_PI_2 #define GPENCIL_MIN_FILL_FAC 0.05f -#define GPENCIL_MAX_FILL_FAC 5.0f +#define GPENCIL_MAX_FILL_FAC 8.0f /* ***************************************** */ /* GP Stroke Points */ diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c index 3c641c6ec1a..4fe19493614 100644 --- a/source/blender/makesrna/intern/rna_ID.c +++ b/source/blender/makesrna/intern/rna_ID.c @@ -577,7 +577,7 @@ static ID *rna_ID_copy(ID *id, Main *bmain) return newid; } -static void rna_ID_mark_asset(ID *id, bContext *C) +static void rna_ID_asset_mark(ID *id, bContext *C) { if (ED_asset_mark_id(C, id)) { WM_main_add_notifier(NC_ID | NA_EDITED, NULL); @@ -585,7 +585,7 @@ static void rna_ID_mark_asset(ID *id, bContext *C) } } -static void rna_ID_clear_asset(ID *id) +static void rna_ID_asset_clear(ID *id) { if (ED_asset_clear_id(id)) { WM_main_add_notifier(NC_ID | NA_EDITED, NULL); @@ -1734,14 +1734,14 @@ static void rna_def_ID(BlenderRNA *brna) parm = RNA_def_pointer(func, "id", "ID", "", "New copy of the ID"); RNA_def_function_return(func, parm); - func = RNA_def_function(srna, "mark_asset", "rna_ID_mark_asset"); + func = RNA_def_function(srna, "asset_mark", "rna_ID_asset_mark"); RNA_def_function_ui_description( func, "Enable easier reuse of the data-block through the Asset Browser, with the help of " "customizable metadata (like previews, descriptions and tags)"); RNA_def_function_flag(func, FUNC_USE_CONTEXT); - func = RNA_def_function(srna, "clear_asset", "rna_ID_clear_asset"); + func = RNA_def_function(srna, "asset_clear", "rna_ID_asset_clear"); RNA_def_function_ui_description( func, "Delete all asset metadata and turn the asset data-block back into a normal data-block"); diff --git a/source/blender/modifiers/intern/MOD_simpledeform.c b/source/blender/modifiers/intern/MOD_simpledeform.c index ea31bdc6e31..71ecc69eccb 100644 --- a/source/blender/modifiers/intern/MOD_simpledeform.c +++ b/source/blender/modifiers/intern/MOD_simpledeform.c @@ -21,10 +21,9 @@ * \ingroup modifiers */ -#include "BLI_utildefines.h" - #include "BLI_math.h" - +#include "BLI_task.h" +#include "BLI_utildefines.h" #include "BLT_translation.h" #include "DNA_defaults.h" @@ -57,6 +56,21 @@ #define BEND_EPS 0.000001f +ALIGN_STRUCT struct DeformUserData { + bool invert_vgroup; + char mode; + char deform_axis; + int lock_axis; + int vgroup; + int limit_axis; + float weight; + float smd_factor; + float smd_limit[2]; + float (*vertexCos)[3]; + const SpaceTransform *transf; + const MDeformVert *dvert; +}; + /* Re-maps the indices for X Y Z by shifting them up and wrapping, such that * X = Y, Y = Z, Z = X (for X axis), and X = Z, Y = X, Z = Y (for Y axis). This * exists because the deformations (excluding bend) are based on the Z axis. @@ -203,6 +217,88 @@ static void simpleDeform_bend(const float factor, } } +static void simple_helper(void *__restrict userdata, + const int iter, + const TaskParallelTLS *__restrict UNUSED(tls)) +{ + const struct DeformUserData *curr_deform_data = userdata; + float weight = BKE_defvert_array_find_weight_safe( + curr_deform_data->dvert, iter, curr_deform_data->vgroup); + const uint *axis_map = axis_map_table[(curr_deform_data->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? + curr_deform_data->deform_axis : + 2]; + const float base_limit[2] = {0.0f, 0.0f}; + + if (curr_deform_data->invert_vgroup) { + weight = 1.0f - weight; + } + + if (weight != 0.0f) { + float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; + + if (curr_deform_data->transf) { + BLI_space_transform_apply(curr_deform_data->transf, curr_deform_data->vertexCos[iter]); + } + + copy_v3_v3(co, curr_deform_data->vertexCos[iter]); + + /* Apply axis limits, and axis mappings */ + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) { + axis_limit(0, base_limit, co, dcut); + } + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) { + axis_limit(1, base_limit, co, dcut); + } + if (curr_deform_data->lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) { + axis_limit(2, base_limit, co, dcut); + } + axis_limit(curr_deform_data->limit_axis, curr_deform_data->smd_limit, co, dcut); + + /* apply the deform to a mapped copy of the vertex, and then re-map it back. */ + float co_remap[3]; + float dcut_remap[3]; + copy_v3_v3_map(co_remap, co, axis_map); + copy_v3_v3_map(dcut_remap, dcut, axis_map); + switch (curr_deform_data->mode) { + case MOD_SIMPLEDEFORM_MODE_TWIST: + simpleDeform_twist(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_BEND: + simpleDeform_bend(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_TAPER: + simpleDeform_taper(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + case MOD_SIMPLEDEFORM_MODE_STRETCH: + simpleDeform_stretch(curr_deform_data->smd_factor, + curr_deform_data->deform_axis, + dcut_remap, + co_remap); /* apply deform */ + break; + default: + return; /* No simple-deform mode? */ + } + copy_v3_v3_unmap(co, co_remap, axis_map); + + /* Use vertex weight has coef of linear interpolation */ + interp_v3_v3v3( + curr_deform_data->vertexCos[iter], curr_deform_data->vertexCos[iter], co, weight); + + if (curr_deform_data->transf) { + BLI_space_transform_invert(curr_deform_data->transf, curr_deform_data->vertexCos[iter]); + } + } +} + /* simple deform modifier */ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, const ModifierEvalContext *UNUSED(ctx), @@ -211,14 +307,9 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, float (*vertexCos)[3], int numVerts) { - const float base_limit[2] = {0.0f, 0.0f}; int i; float smd_limit[2], smd_factor; SpaceTransform *transf = NULL, tmp_transf; - void (*simpleDeform_callback)(const float factor, - const int axis, - const float dcut[3], - float co[3]) = NULL; /* Mode callback */ int vgroup; MDeformVert *dvert; @@ -300,23 +391,6 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, smd_factor = smd->factor / max_ff(FLT_EPSILON, smd_limit[1] - smd_limit[0]); } - switch (smd->mode) { - case MOD_SIMPLEDEFORM_MODE_TWIST: - simpleDeform_callback = simpleDeform_twist; - break; - case MOD_SIMPLEDEFORM_MODE_BEND: - simpleDeform_callback = simpleDeform_bend; - break; - case MOD_SIMPLEDEFORM_MODE_TAPER: - simpleDeform_callback = simpleDeform_taper; - break; - case MOD_SIMPLEDEFORM_MODE_STRETCH: - simpleDeform_callback = simpleDeform_stretch; - break; - default: - return; /* No simple-deform mode? */ - } - if (smd->mode == MOD_SIMPLEDEFORM_MODE_BEND) { if (fabsf(smd_factor) < BEND_EPS) { return; @@ -325,53 +399,26 @@ static void SimpleDeformModifier_do(SimpleDeformModifierData *smd, MOD_get_vgroup(ob, mesh, smd->vgroup_name, &dvert, &vgroup); const bool invert_vgroup = (smd->flag & MOD_SIMPLEDEFORM_FLAG_INVERT_VGROUP) != 0; - const uint *axis_map = - axis_map_table[(smd->mode != MOD_SIMPLEDEFORM_MODE_BEND) ? deform_axis : 2]; - - for (i = 0; i < numVerts; i++) { - float weight = BKE_defvert_array_find_weight_safe(dvert, i, vgroup); - - if (invert_vgroup) { - weight = 1.0f - weight; - } - - if (weight != 0.0f) { - float co[3], dcut[3] = {0.0f, 0.0f, 0.0f}; - if (transf) { - BLI_space_transform_apply(transf, vertexCos[i]); - } - - copy_v3_v3(co, vertexCos[i]); - - /* Apply axis limits, and axis mappings */ - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_X) { - axis_limit(0, base_limit, co, dcut); - } - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Y) { - axis_limit(1, base_limit, co, dcut); - } - if (lock_axis & MOD_SIMPLEDEFORM_LOCK_AXIS_Z) { - axis_limit(2, base_limit, co, dcut); - } - axis_limit(limit_axis, smd_limit, co, dcut); - - /* apply the deform to a mapped copy of the vertex, and then re-map it back. */ - float co_remap[3]; - float dcut_remap[3]; - copy_v3_v3_map(co_remap, co, axis_map); - copy_v3_v3_map(dcut_remap, dcut, axis_map); - simpleDeform_callback(smd_factor, deform_axis, dcut_remap, co_remap); /* apply deform */ - copy_v3_v3_unmap(co, co_remap, axis_map); - - /* Use vertex weight has coef of linear interpolation */ - interp_v3_v3v3(vertexCos[i], vertexCos[i], co, weight); - - if (transf) { - BLI_space_transform_invert(transf, vertexCos[i]); - } - } - } + /* Build our data. */ + const struct DeformUserData deform_pool_data = { + .mode = smd->mode, + .smd_factor = smd_factor, + .deform_axis = deform_axis, + .transf = transf, + .vertexCos = vertexCos, + .invert_vgroup = invert_vgroup, + .lock_axis = lock_axis, + .vgroup = vgroup, + .smd_limit[0] = smd_limit[0], + .smd_limit[1] = smd_limit[1], + .dvert = dvert, + .limit_axis = limit_axis, + }; + /* Do deformation. */ + TaskParallelSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(0, numVerts, (void *)&deform_pool_data, simple_helper, &settings); } /* SimpleDeform */ diff --git a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc index b30b2ff113f..460db230fdb 100644 --- a/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc +++ b/source/blender/nodes/geometry/nodes/node_geo_attribute_combine_xyz.cc @@ -112,6 +112,7 @@ static void combine_attributes(GeometryComponent &component, const GeoNodeExecPa const float z = attribute_z[i]; attribute_result->set(i, {x, y, z}); } + attribute_result.save(); } static void geo_node_attribute_combine_xyz_exec(GeoNodeExecParams params) diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c index b9ab0ce4c29..354aa9b6986 100644 --- a/source/blender/python/intern/bpy_rna.c +++ b/source/blender/python/intern/bpy_rna.c @@ -4207,6 +4207,10 @@ static void pyrna_dir_members_rna(PyObject *list, PointerRNA *ptr) iterprop = RNA_struct_iterator_property(ptr->type); RNA_PROP_BEGIN (ptr, itemptr, iterprop) { + /* Custom-properties are exposed using `__getitem__`, exclude from `__dir__`. */ + if (RNA_property_is_idprop(itemptr.data)) { + continue; + } nameptr = RNA_struct_name_get_alloc(&itemptr, name, sizeof(name), &namelen); if (nameptr) { diff --git a/source/blender/render/intern/engine.c b/source/blender/render/intern/engine.c index 817f09f5bfe..66d38eb19c7 100644 --- a/source/blender/render/intern/engine.c +++ b/source/blender/render/intern/engine.c @@ -143,12 +143,9 @@ RenderEngine *RE_engine_create(RenderEngineType *type) static void engine_depsgraph_free(RenderEngine *engine) { if (engine->depsgraph) { - /* Need GPU context since this might free GPU buffers. This function can - * only be called from a render thread. We do not currently support - * persistent data with GPU contexts for that reason. */ + /* Need GPU context since this might free GPU buffers. */ const bool use_gpu_context = (engine->type->flag & RE_USE_GPU_CONTEXT); if (use_gpu_context) { - BLI_assert(!BLI_thread_is_main()); DRW_render_context_enable(engine->re); } @@ -623,8 +620,8 @@ RenderData *RE_engine_get_render_data(Render *re) bool RE_engine_use_persistent_data(RenderEngine *engine) { - /* See engine_depsgraph_free() for why preserving the depsgraph for - * re-renders is not supported with GPU contexts. */ + /* Re-rendering is not supported with GPU contexts, since the GPU context + * is destroyed when the render thread exists. */ return (engine->re->r.mode & R_PERSISTENT_DATA) && !(engine->type->flag & RE_USE_GPU_CONTEXT); } diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index 614824ef7e2..d0ee7075516 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -3265,7 +3265,10 @@ static void wm_block_file_close_save(bContext *C, void *arg_block, void *arg_dat bool file_has_been_saved_before = BKE_main_blendfile_path(bmain)[0] != '\0'; if (file_has_been_saved_before) { - WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL); + if (WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_EXEC_DEFAULT, NULL) & + OPERATOR_CANCELLED) { + execute_callback = false; + } } else { WM_operator_name_call(C, "WM_OT_save_mainfile", WM_OP_INVOKE_DEFAULT, NULL); diff --git a/source/blender/windowmanager/wm_files.h b/source/blender/windowmanager/wm_files.h index 533084e96e8..c7fe07cad7f 100644 --- a/source/blender/windowmanager/wm_files.h +++ b/source/blender/windowmanager/wm_files.h @@ -45,7 +45,9 @@ void wm_homefile_read(struct bContext *C, void wm_file_read_report(bContext *C, struct Main *bmain); void wm_close_file_dialog(bContext *C, struct wmGenericCallback *post_action); -bool wm_operator_close_file_dialog_if_needed(bContext *C, wmOperator *op, wmGenericCallbackFn exec_fn); +bool wm_operator_close_file_dialog_if_needed(bContext *C, + wmOperator *op, + wmGenericCallbackFn exec_fn); bool wm_file_or_image_is_modified(const Main *bmain, const wmWindowManager *wm); void WM_OT_save_homefile(struct wmOperatorType *ot); |