From bfc9d426bb95e2bc0dd4541d6b4c5f802909149c Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Mon, 16 Apr 2018 16:27:55 +0200 Subject: Multi-Object Editing This adds initial multi-object editing support. - Selected objects are used when entering edit & pose modes. - Selection & tools work on all objects however many tools need porting See: T54641 for remaining tasks. Indentation will be done separately. See patch: D3101 --- source/blender/blenkernel/BKE_layer.h | 111 +++ source/blender/blenkernel/BKE_object.h | 2 + source/blender/blenkernel/CMakeLists.txt | 1 + source/blender/blenkernel/intern/editmesh.c | 2 + source/blender/blenkernel/intern/layer.c | 57 ++ source/blender/blenkernel/intern/layer_utils.c | 125 +++ source/blender/blenkernel/intern/object.c | 39 + source/blender/blenkernel/intern/object_update.c | 8 + source/blender/bmesh/intern/bmesh_mesh.c | 67 +- source/blender/bmesh/intern/bmesh_mesh.h | 1 + source/blender/draw/intern/draw_armature.c | 2 +- source/blender/draw/intern/draw_manager.c | 7 + source/blender/draw/modes/edit_curve_mode.c | 9 +- source/blender/draw/modes/edit_lattice_mode.c | 4 +- source/blender/draw/modes/edit_mesh_mode.c | 4 +- source/blender/draw/modes/edit_metaball_mode.c | 3 +- source/blender/draw/modes/pose_mode.c | 4 +- source/blender/editors/armature/CMakeLists.txt | 1 + source/blender/editors/armature/armature_add.c | 3 +- source/blender/editors/armature/armature_edit.c | 86 +- source/blender/editors/armature/armature_intern.h | 11 +- source/blender/editors/armature/armature_select.c | 221 +++-- .../blender/editors/armature/editarmature_undo.c | 81 +- source/blender/editors/armature/pose_edit.c | 3 +- source/blender/editors/armature/pose_select.c | 108 ++- source/blender/editors/curve/CMakeLists.txt | 1 + source/blender/editors/curve/editcurve_undo.c | 77 +- source/blender/editors/include/ED_armature.h | 13 +- source/blender/editors/include/ED_mesh.h | 3 + source/blender/editors/include/ED_object.h | 3 + source/blender/editors/include/ED_undo.h | 5 + source/blender/editors/include/ED_uvedit.h | 9 +- source/blender/editors/include/ED_view3d.h | 1 + source/blender/editors/lattice/CMakeLists.txt | 1 + source/blender/editors/lattice/editlattice_undo.c | 77 +- source/blender/editors/mesh/CMakeLists.txt | 1 + source/blender/editors/mesh/editmesh_select.c | 255 ++++-- source/blender/editors/mesh/editmesh_tools.c | 129 ++- source/blender/editors/mesh/editmesh_undo.c | 83 +- source/blender/editors/mesh/meshtools.c | 45 ++ source/blender/editors/metaball/CMakeLists.txt | 1 + source/blender/editors/metaball/editmball_undo.c | 77 +- source/blender/editors/object/object_edit.c | 162 ++-- source/blender/editors/object/object_modes.c | 3 + source/blender/editors/screen/screen_context.c | 66 +- source/blender/editors/space_view3d/space_view3d.c | 1 + .../blender/editors/space_view3d/view3d_select.c | 276 ++++--- source/blender/editors/transform/transform.c | 761 ++++++++++-------- source/blender/editors/transform/transform.h | 143 +++- .../editors/transform/transform_constraints.c | 50 +- .../editors/transform/transform_conversions.c | 894 +++++++++++++-------- .../blender/editors/transform/transform_generics.c | 450 +++++++---- source/blender/editors/transform/transform_snap.c | 98 ++- source/blender/editors/undo/ed_undo.c | 23 + source/blender/editors/uvedit/uvedit_draw.c | 18 +- source/blender/editors/uvedit/uvedit_intern.h | 11 + source/blender/editors/uvedit/uvedit_ops.c | 312 +++++-- source/blender/editors/uvedit/uvedit_unwrap_ops.c | 251 ++++-- 58 files changed, 3780 insertions(+), 1480 deletions(-) create mode 100644 source/blender/blenkernel/intern/layer_utils.c (limited to 'source') diff --git a/source/blender/blenkernel/BKE_layer.h b/source/blender/blenkernel/BKE_layer.h index 9c06ae4f40d..75fb4962bef 100644 --- a/source/blender/blenkernel/BKE_layer.h +++ b/source/blender/blenkernel/BKE_layer.h @@ -187,10 +187,20 @@ void BKE_visible_objects_iterator_begin(BLI_Iterator *iter, void *data_in); void BKE_visible_objects_iterator_next(BLI_Iterator *iter); void BKE_visible_objects_iterator_end(BLI_Iterator *iter); +struct ObjectsInModeIteratorData { + int object_mode; + struct ViewLayer *view_layer; + struct Base *base_active; +}; + void BKE_renderable_objects_iterator_begin(BLI_Iterator *iter, void *data_in); void BKE_renderable_objects_iterator_next(BLI_Iterator *iter); void BKE_renderable_objects_iterator_end(BLI_Iterator *iter); +void BKE_view_layer_objects_in_mode_iterator_begin(BLI_Iterator *iter, void *data_in); +void BKE_view_layer_objects_in_mode_iterator_next(BLI_Iterator *iter); +void BKE_view_layer_objects_in_mode_iterator_end(BLI_Iterator *iter); + void BKE_selected_bases_iterator_begin(BLI_Iterator *iter, void *data_in); void BKE_selected_bases_iterator_next(BLI_Iterator *iter); void BKE_selected_bases_iterator_end(BLI_Iterator *iter); @@ -217,6 +227,43 @@ void BKE_visible_bases_iterator_end(BLI_Iterator *iter); #define FOREACH_VISIBLE_OBJECT_END \ ITER_END + +#define FOREACH_BASE_IN_MODE_BEGIN(_view_layer, _object_mode, _instance) \ +{ \ + struct ObjectsInModeIteratorData data_ = { \ + .object_mode = _object_mode, \ + .view_layer = _view_layer, \ + .base_active = _view_layer->basact, \ + }; \ + ITER_BEGIN(BKE_view_layer_objects_in_mode_iterator_begin, \ + BKE_view_layer_objects_in_mode_iterator_next, \ + BKE_view_layer_objects_in_mode_iterator_end, \ + &data_, Base *, _instance) + +#define FOREACH_BASE_IN_MODE_END \ + ITER_END; \ +} ((void)0) + +#define FOREACH_BASE_IN_EDIT_MODE_BEGIN(_view_layer, _instance) \ + FOREACH_BASE_IN_MODE_BEGIN(_view_layer, OB_MODE_EDIT, _instance) + +#define FOREACH_BASE_IN_EDIT_MODE_END \ + FOREACH_BASE_IN_MODE_END + +#define FOREACH_OBJECT_IN_MODE_BEGIN(_view_layer, _object_mode, _instance) \ + FOREACH_BASE_IN_MODE_BEGIN(_view_layer, _object_mode, _base) { \ + Object *_instance = _base->object; + +#define FOREACH_OBJECT_IN_MODE_END \ + } FOREACH_BASE_IN_MODE_END + +#define FOREACH_OBJECT_IN_EDIT_MODE_BEGIN(_view_layer, _instance) \ + FOREACH_BASE_IN_EDIT_MODE_BEGIN(_view_layer, _base) { \ + Object *_instance = _base->object; + +#define FOREACH_OBJECT_IN_EDIT_MODE_END \ + } FOREACH_BASE_IN_EDIT_MODE_END + #define FOREACH_SELECTED_BASE_BEGIN(view_layer, _instance) \ ITER_BEGIN(BKE_selected_bases_iterator_begin, \ BKE_selected_bases_iterator_next, \ @@ -299,6 +346,70 @@ struct ObjectsRenderableIteratorData { ITER_END; \ } ((void)0) + +/* layer_utils.c */ + +struct ObjectsInModeParams { + int object_mode; + uint no_dup_data : 1; + + bool (*filter_fn)(struct Object *ob, void *user_data); + void *filter_userdata; +}; + +Base **BKE_view_layer_array_from_bases_in_mode_params( + struct ViewLayer *view_layer, uint *r_len, + const struct ObjectsInModeParams *params); + +struct Object **BKE_view_layer_array_from_objects_in_mode_params( + struct ViewLayer *view_layer, uint *len, + const struct ObjectsInModeParams *params); + +#define BKE_view_layer_array_from_objects_in_mode(view_layer, r_len, ...) \ + BKE_view_layer_array_from_objects_in_mode_params( \ + view_layer, r_len, \ + &(const struct ObjectsInModeParams)__VA_ARGS__) + +#define BKE_view_layer_array_from_bases_in_mode(view_layer, r_len, ...) \ + BKE_view_layer_array_from_bases_in_mode_params( \ + view_layer, r_len, \ + &(const struct ObjectsInModeParams)__VA_ARGS__) + +bool BKE_view_layer_filter_edit_mesh_has_uvs(struct Object *ob, void *user_data); +bool BKE_view_layer_filter_edit_mesh_has_edges(struct Object *ob, void *user_data); + +/* Utility macros that wrap common args (add more as needed). */ + +#define BKE_view_layer_array_from_objects_in_edit_mode(view_layer, r_len) \ + BKE_view_layer_array_from_objects_in_mode( \ + view_layer, r_len, { \ + .object_mode = OB_MODE_EDIT}); + +#define BKE_view_layer_array_from_bases_in_edit_mode(view_layer, r_len) \ + BKE_view_layer_array_from_bases_in_mode( \ + view_layer, r_len, { \ + .object_mode = OB_MODE_EDIT}); + +#define BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, r_len) \ + BKE_view_layer_array_from_objects_in_mode( \ + view_layer, r_len, { \ + .object_mode = OB_MODE_EDIT, \ + .no_dup_data = true}); + +#define BKE_view_layer_array_from_bases_in_edit_mode_unique_data(view_layer, r_len) \ + BKE_view_layer_array_from_bases_in_mode( \ + view_layer, r_len, { \ + .object_mode = OB_MODE_EDIT, \ + .no_dup_data = true}); + +#define BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, r_len) \ + BKE_view_layer_array_from_objects_in_mode( \ + view_layer, r_len, { \ + .object_mode = OB_MODE_EDIT, \ + .no_dup_data = true, \ + .filter_fn = BKE_view_layer_filter_edit_mesh_has_uvs}); + + #ifdef __cplusplus } #endif diff --git a/source/blender/blenkernel/BKE_object.h b/source/blender/blenkernel/BKE_object.h index c698db2e8f5..c75bbf849a8 100644 --- a/source/blender/blenkernel/BKE_object.h +++ b/source/blender/blenkernel/BKE_object.h @@ -83,7 +83,9 @@ void BKE_object_copy_proxy_drivers(struct Object *ob, struct Object *target); bool BKE_object_exists_check(const struct Object *obtest); bool BKE_object_is_in_editmode(const struct Object *ob); bool BKE_object_is_in_editmode_vgroup(const struct Object *ob); +bool BKE_object_is_in_editmode_and_selected(const struct Object *ob); bool BKE_object_is_in_wpaint_select_vert(const struct Object *ob); +bool BKE_object_has_mode_data(const struct Object *ob, eObjectMode object_mode); typedef enum eObjectVisibilityCheck { OB_VISIBILITY_CHECK_FOR_VIEWPORT, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 6a1c3ea883c..e8fd71c2b2d 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -167,6 +167,7 @@ set(SRC intern/pointcache.c intern/property.c intern/layer.c + intern/layer_utils.c intern/lightprobe.c intern/report.c intern/rigidbody.c diff --git a/source/blender/blenkernel/intern/editmesh.c b/source/blender/blenkernel/intern/editmesh.c index c95da3b2569..b63ab276b14 100644 --- a/source/blender/blenkernel/intern/editmesh.c +++ b/source/blender/blenkernel/intern/editmesh.c @@ -89,10 +89,12 @@ BMEditMesh *BKE_editmesh_from_object(Object *ob) { BLI_assert(ob->type == OB_MESH); /* sanity check */ +#if 0 /* disable in mutlti-object edit. */ #ifndef NDEBUG if (((Mesh *)ob->data)->edit_btmesh) { BLI_assert(((Mesh *)ob->data)->edit_btmesh->ob == ob); } +#endif #endif return ((Mesh *)ob->data)->edit_btmesh; } diff --git a/source/blender/blenkernel/intern/layer.c b/source/blender/blenkernel/intern/layer.c index 50c7dc0c02f..5f24dd481e2 100644 --- a/source/blender/blenkernel/intern/layer.c +++ b/source/blender/blenkernel/intern/layer.c @@ -26,6 +26,7 @@ #include +#include "BLI_array.h" #include "BLI_listbase.h" #include "BLI_string.h" #include "BLI_string_utf8.h" @@ -41,6 +42,7 @@ #include "BKE_main.h" #include "BKE_node.h" #include "BKE_workspace.h" +#include "BKE_object.h" #include "DEG_depsgraph.h" @@ -2235,6 +2237,61 @@ void BKE_renderable_objects_iterator_end(BLI_Iterator *UNUSED(iter)) /* Do nothing - iter->data was static allocated, we can't free it. */ } +/* -------------------------------------------------------------------- */ +/** \name BKE_view_layer_objects_in_mode_iterator + * \{ */ + +void BKE_view_layer_objects_in_mode_iterator_begin(BLI_Iterator *iter, void *data_in) +{ + struct ObjectsInModeIteratorData *data = data_in; + Base *base = data->base_active; + + /* when there are no objects */ + if (base == NULL) { + iter->valid = false; + return; + } + iter->data = data_in; + iter->current = base; +} + +void BKE_view_layer_objects_in_mode_iterator_next(BLI_Iterator *iter) +{ + struct ObjectsInModeIteratorData *data = iter->data; + Base *base = iter->current; + + if (base == data->base_active) { + /* first step */ + base = data->view_layer->object_bases.first; + if (base == data->base_active) { + base = base->next; + } + } + else { + base = base->next; + } + + while (base) { + if ((base->flag & BASE_SELECTED) != 0 && + (base->object->type == data->base_active->object->type) && + (base != data->base_active) && + (base->object->mode & data->object_mode)) + { + iter->current = base; + return; + } + base = base->next; + } + iter->valid = false; +} + +void BKE_view_layer_objects_in_mode_iterator_end(BLI_Iterator *UNUSED(iter)) +{ + /* do nothing */ +} + +/** \} */ + /* Evaluation */ /** diff --git a/source/blender/blenkernel/intern/layer_utils.c b/source/blender/blenkernel/intern/layer_utils.c new file mode 100644 index 00000000000..94bac8a33d6 --- /dev/null +++ b/source/blender/blenkernel/intern/layer_utils.c @@ -0,0 +1,125 @@ +/* + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * 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. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +/** \file blender/blenkernel/intern/layer_utils.c + * \ingroup bke + */ + +#include + +#include "BLI_array.h" +#include "BLI_listbase.h" + +#include "BKE_collection.h" +#include "BKE_editmesh.h" +#include "BKE_layer.h" + +#include "DNA_ID.h" +#include "DNA_layer_types.h" +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" +#include "DNA_scene_types.h" + +#include "MEM_guardedalloc.h" + +Base **BKE_view_layer_array_from_bases_in_mode_params( + ViewLayer *view_layer, uint *r_len, + const struct ObjectsInModeParams *params) +{ + if (params->no_dup_data) { + FOREACH_BASE_IN_MODE_BEGIN(view_layer, params->object_mode, base_iter) { + ID *id = base_iter->object->data; + if (id) { + id->tag |= LIB_TAG_DOIT; + } + } FOREACH_BASE_IN_MODE_END; + } + + Base **base_array = NULL; + BLI_array_declare(base_array); + + FOREACH_BASE_IN_MODE_BEGIN(view_layer, params->object_mode, base_iter) { + if (params->filter_fn) { + if (!params->filter_fn(base_iter->object, params->filter_userdata)) { + continue; + } + } + if (params->no_dup_data) { + ID *id = base_iter->object->data; + if (id) { + if (id->tag & LIB_TAG_DOIT) { + id->tag &= ~LIB_TAG_DOIT; + } + else { + continue; + } + } + } + BLI_array_append(base_array, base_iter); + } FOREACH_BASE_IN_MODE_END; + + if (base_array != NULL) { + base_array = MEM_reallocN(base_array, sizeof(*base_array) * BLI_array_len(base_array)); + } + *r_len = BLI_array_len(base_array); + return base_array; +} + +Object **BKE_view_layer_array_from_objects_in_mode_params( + ViewLayer *view_layer, uint *r_len, + const struct ObjectsInModeParams *params) +{ + Base **base_array = BKE_view_layer_array_from_bases_in_mode_params( + view_layer, r_len, params); + if (base_array != NULL) { + for (uint i = 0; i < *r_len; i++) { + ((Object **)base_array)[i] = base_array[i]->object; + } + } + return (Object **)base_array; +} + +bool BKE_view_layer_filter_edit_mesh_has_uvs(Object *ob, void *UNUSED(user_data)) +{ + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + BMEditMesh *em = me->edit_btmesh; + if (em != NULL) { + if (CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV) != -1) { + return true; + } + } + } + return false; +} + +bool BKE_view_layer_filter_edit_mesh_has_edges(Object *ob, void *UNUSED(user_data)) +{ + if (ob->type == OB_MESH) { + Mesh *me = ob->data; + BMEditMesh *em = me->edit_btmesh; + if (em != NULL) { + if (em->bm->totedge != 0) { + return true; + } + } + } + return false; +} diff --git a/source/blender/blenkernel/intern/object.c b/source/blender/blenkernel/intern/object.c index 875d716305f..081f9f15508 100644 --- a/source/blender/blenkernel/intern/object.c +++ b/source/blender/blenkernel/intern/object.c @@ -530,6 +530,15 @@ bool BKE_object_is_in_editmode(const Object *ob) return false; } +bool BKE_object_is_in_editmode_and_selected(const Object *ob) +{ + if ((ob->flag & SELECT) && (BKE_object_is_in_editmode(ob))) { + return true; + } + return false; +} + + bool BKE_object_is_in_editmode_vgroup(const Object *ob) { return (OB_TYPE_SUPPORT_VGROUP(ob->type) && @@ -548,6 +557,36 @@ bool BKE_object_is_in_wpaint_select_vert(const Object *ob) return false; } +bool BKE_object_has_mode_data(const struct Object *ob, eObjectMode object_mode) +{ + if (object_mode & OB_MODE_EDIT) { + if (BKE_object_is_in_editmode(ob)) { + return true; + } + } + else if (object_mode & OB_MODE_VERTEX_PAINT) { + if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_VERTEX_PAINT)) { + return true; + } + } + else if (object_mode & OB_MODE_WEIGHT_PAINT) { + if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_WEIGHT_PAINT)) { + return true; + } + } + else if (object_mode & OB_MODE_SCULPT) { + if (ob->sculpt && (ob->sculpt->mode_type == OB_MODE_SCULPT)) { + return true; + } + } + else if (object_mode & OB_MODE_POSE) { + if (ob->pose != NULL) { + return true; + } + } + return false; +} + /** * Return if the object is visible, as evaluated by depsgraph */ diff --git a/source/blender/blenkernel/intern/object_update.c b/source/blender/blenkernel/intern/object_update.c index c70e07e6c4c..35ab4024f62 100644 --- a/source/blender/blenkernel/intern/object_update.c +++ b/source/blender/blenkernel/intern/object_update.c @@ -172,7 +172,15 @@ void BKE_object_handle_data_update( switch (ob->type) { case OB_MESH: { +#if 0 BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? BKE_editmesh_from_object(ob) : NULL; +#else + BMEditMesh *em = (ob->mode & OB_MODE_EDIT) ? ((Mesh *)ob->data)->edit_btmesh : NULL; + if (em && em->ob != ob) { + em = NULL; + } +#endif + uint64_t data_mask = scene->customdata_mask | CD_MASK_BAREMESH; #ifdef WITH_FREESTYLE /* make sure Freestyle edge/face marks appear in DM for render (see T40315) */ diff --git a/source/blender/bmesh/intern/bmesh_mesh.c b/source/blender/bmesh/intern/bmesh_mesh.c index 9e03c28ba1b..8071637d95e 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.c +++ b/source/blender/bmesh/intern/bmesh_mesh.c @@ -1160,7 +1160,7 @@ void bmesh_edit_end(BMesh *bm, BMOpTypeFlag type_flag) } } -void BM_mesh_elem_index_ensure(BMesh *bm, const char htype) +void BM_mesh_elem_index_ensure_ex(BMesh *bm, const char htype, int elem_offset[4]) { const char htype_needed = bm->elem_index_dirty & htype; @@ -1173,15 +1173,15 @@ void BM_mesh_elem_index_ensure(BMesh *bm, const char htype) } if (htype & BM_VERT) { - if (bm->elem_index_dirty & BM_VERT) { + if ((bm->elem_index_dirty & BM_VERT) || (elem_offset && elem_offset[0])) { BMIter iter; BMElem *ele; - int index; - BM_ITER_MESH_INDEX (ele, &iter, bm, BM_VERTS_OF_MESH, index) { - BM_elem_index_set(ele, index); /* set_ok */ + int index = elem_offset ? elem_offset[0] : 0; + BM_ITER_MESH (ele, &iter, bm, BM_VERTS_OF_MESH) { + BM_elem_index_set(ele, index++); /* set_ok */ } - BLI_assert(index == bm->totvert); + BLI_assert(elem_offset || index == bm->totvert); } else { // printf("%s: skipping vert index calc!\n", __func__); @@ -1189,15 +1189,15 @@ void BM_mesh_elem_index_ensure(BMesh *bm, const char htype) } if (htype & BM_EDGE) { - if (bm->elem_index_dirty & BM_EDGE) { + if ((bm->elem_index_dirty & BM_EDGE) || (elem_offset && elem_offset[1])) { BMIter iter; BMElem *ele; - int index; - BM_ITER_MESH_INDEX (ele, &iter, bm, BM_EDGES_OF_MESH, index) { - BM_elem_index_set(ele, index); /* set_ok */ + int index = elem_offset ? elem_offset[1] : 0; + BM_ITER_MESH (ele, &iter, bm, BM_EDGES_OF_MESH) { + BM_elem_index_set(ele, index++); /* set_ok */ } - BLI_assert(index == bm->totedge); + BLI_assert(elem_offset || index == bm->totedge); } else { // printf("%s: skipping edge index calc!\n", __func__); @@ -1205,19 +1205,19 @@ void BM_mesh_elem_index_ensure(BMesh *bm, const char htype) } if (htype & (BM_FACE | BM_LOOP)) { - if (bm->elem_index_dirty & (BM_FACE | BM_LOOP)) { + if ((bm->elem_index_dirty & (BM_FACE | BM_LOOP)) || (elem_offset && (elem_offset[2] || elem_offset[3]))) { BMIter iter; BMElem *ele; const bool update_face = (htype & BM_FACE) && (bm->elem_index_dirty & BM_FACE); const bool update_loop = (htype & BM_LOOP) && (bm->elem_index_dirty & BM_LOOP); - int index; - int index_loop = 0; + int index_loop = elem_offset ? elem_offset[2] : 0; + int index = elem_offset ? elem_offset[3] : 0; - BM_ITER_MESH_INDEX (ele, &iter, bm, BM_FACES_OF_MESH, index) { + BM_ITER_MESH (ele, &iter, bm, BM_FACES_OF_MESH) { if (update_face) { - BM_elem_index_set(ele, index); /* set_ok */ + BM_elem_index_set(ele, index++); /* set_ok */ } if (update_loop) { @@ -1230,9 +1230,9 @@ void BM_mesh_elem_index_ensure(BMesh *bm, const char htype) } } - BLI_assert(index == bm->totface); + BLI_assert(elem_offset || !update_face || index == bm->totface); if (update_loop) { - BLI_assert(index_loop == bm->totloop); + BLI_assert(elem_offset || !update_loop || index_loop == bm->totloop); } } else { @@ -1242,6 +1242,37 @@ void BM_mesh_elem_index_ensure(BMesh *bm, const char htype) finally: bm->elem_index_dirty &= ~htype; + if (elem_offset) { + if (htype & BM_VERT) { + elem_offset[0] += bm->totvert; + if (elem_offset[0] != bm->totvert) { + bm->elem_index_dirty |= BM_VERT; + } + } + if (htype & BM_EDGE) { + elem_offset[1] += bm->totedge; + if (elem_offset[1] != bm->totedge) { + bm->elem_index_dirty |= BM_EDGE; + } + } + if (htype & BM_LOOP) { + elem_offset[2] += bm->totloop; + if (elem_offset[2] != bm->totloop) { + bm->elem_index_dirty |= BM_LOOP; + } + } + if (htype & BM_FACE) { + elem_offset[3] += bm->totface; + if (elem_offset[3] != bm->totface) { + bm->elem_index_dirty |= BM_FACE; + } + } + } +} + +void BM_mesh_elem_index_ensure(BMesh *bm, const char htype) +{ + BM_mesh_elem_index_ensure_ex(bm, htype, NULL); } diff --git a/source/blender/bmesh/intern/bmesh_mesh.h b/source/blender/bmesh/intern/bmesh_mesh.h index d449aac04f5..3ebe6535a8b 100644 --- a/source/blender/bmesh/intern/bmesh_mesh.h +++ b/source/blender/bmesh/intern/bmesh_mesh.h @@ -60,6 +60,7 @@ void BM_edges_sharp_from_angle_set(BMesh *bm, const float split_angle); void bmesh_edit_begin(BMesh *bm, const BMOpTypeFlag type_flag); void bmesh_edit_end(BMesh *bm, const BMOpTypeFlag type_flag); +void BM_mesh_elem_index_ensure_ex(BMesh *bm, const char htype, int elem_offset[4]); void BM_mesh_elem_index_ensure(BMesh *bm, const char hflag); void BM_mesh_elem_index_validate( BMesh *bm, const char *location, const char *func, diff --git a/source/blender/draw/intern/draw_armature.c b/source/blender/draw/intern/draw_armature.c index c14fe70e0c3..fe87e7f17fd 100644 --- a/source/blender/draw/intern/draw_armature.c +++ b/source/blender/draw/intern/draw_armature.c @@ -1228,7 +1228,7 @@ static void draw_armature_edit(Object *ob) const bool show_text = DRW_state_show_text(); - for (eBone = arm->edbo->first, index = 0; eBone; eBone = eBone->next, index++) { + for (eBone = arm->edbo->first, index = ob->select_color; eBone; eBone = eBone->next, index += 0x10000) { if (eBone->layer & arm->layer) { if ((eBone->flag & BONE_HIDDEN_A) == 0) { const int select_id = is_select ? index : (unsigned int)-1; diff --git a/source/blender/draw/intern/draw_manager.c b/source/blender/draw/intern/draw_manager.c index 6ba1225b687..420841e2efa 100644 --- a/source/blender/draw/intern/draw_manager.c +++ b/source/blender/draw/intern/draw_manager.c @@ -1554,7 +1554,14 @@ void DRW_draw_select_loop( drw_engines_cache_init(); if (use_obedit) { +#if 0 drw_engines_cache_populate(obact); +#else + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, obact->mode, ob_iter) { + drw_engines_cache_populate(ob_iter); + } + FOREACH_OBJECT_IN_MODE_END; +#endif } else { DEG_OBJECT_ITER_BEGIN( diff --git a/source/blender/draw/modes/edit_curve_mode.c b/source/blender/draw/modes/edit_curve_mode.c index 73a4fb1e9e6..b33ebd8ba60 100644 --- a/source/blender/draw/modes/edit_curve_mode.c +++ b/source/blender/draw/modes/edit_curve_mode.c @@ -28,6 +28,8 @@ #include "DNA_curve_types.h" +#include "BKE_object.h" + /* If builtin shaders are needed */ #include "GPU_shader.h" #include "GPU_batch.h" @@ -233,7 +235,12 @@ static void EDIT_CURVE_cache_populate(void *vedata, Object *ob) UNUSED_VARS(psl, stl); if (ob->type == OB_CURVE) { - if (ob == draw_ctx->object_edit) { +#if 0 + if (ob == draw_ctx->object_edit) +#else + if ((ob == draw_ctx->object_edit) || BKE_object_is_in_editmode_and_selected(ob)) +#endif + { Curve *cu = ob->data; /* Get geometry cache */ struct Gwn_Batch *geom; diff --git a/source/blender/draw/modes/edit_lattice_mode.c b/source/blender/draw/modes/edit_lattice_mode.c index 0268f4eb453..e8628711ffd 100644 --- a/source/blender/draw/modes/edit_lattice_mode.c +++ b/source/blender/draw/modes/edit_lattice_mode.c @@ -26,6 +26,8 @@ #include "DRW_engine.h" #include "DRW_render.h" +#include "BKE_object.h" + /* If builtin shaders are needed */ #include "GPU_shader.h" @@ -192,7 +194,7 @@ static void EDIT_LATTICE_cache_populate(void *vedata, Object *ob) UNUSED_VARS(psl); if (ob->type == OB_LATTICE) { - if (ob == draw_ctx->object_edit) { + if ((ob == draw_ctx->object_edit) || BKE_object_is_in_editmode_and_selected(ob)) { /* Get geometry cache */ struct Gwn_Batch *geom; diff --git a/source/blender/draw/modes/edit_mesh_mode.c b/source/blender/draw/modes/edit_mesh_mode.c index 4bd69941809..c465fa38f04 100644 --- a/source/blender/draw/modes/edit_mesh_mode.c +++ b/source/blender/draw/modes/edit_mesh_mode.c @@ -37,6 +37,8 @@ #include "edit_mesh_mode_intern.h" /* own include */ +#include "BKE_object.h" + extern struct GPUUniformBuffer *globals_ubo; /* draw_common.c */ extern struct GlobalsUboStorage ts; /* draw_common.c */ @@ -448,7 +450,7 @@ static void EDIT_MESH_cache_populate(void *vedata, Object *ob) struct Gwn_Batch *geom; if (ob->type == OB_MESH) { - if (ob == draw_ctx->object_edit) { + if ((ob == draw_ctx->object_edit) || BKE_object_is_in_editmode_and_selected(ob)) { const Mesh *me = ob->data; IDProperty *ces_mode_ed = BKE_layer_collection_engine_evaluated_get(ob, COLLECTION_MODE_EDIT, ""); bool do_occlude_wire = BKE_collection_engine_property_value_get_bool(ces_mode_ed, "show_occlude_wire"); diff --git a/source/blender/draw/modes/edit_metaball_mode.c b/source/blender/draw/modes/edit_metaball_mode.c index bcabeef5bc3..f7b7113a4d6 100644 --- a/source/blender/draw/modes/edit_metaball_mode.c +++ b/source/blender/draw/modes/edit_metaball_mode.c @@ -28,6 +28,7 @@ #include "DNA_meta_types.h" +#include "BKE_object.h" #include "BKE_mball.h" /* If builtin shaders are needed */ @@ -171,7 +172,7 @@ static void EDIT_METABALL_cache_populate(void *vedata, Object *ob) const DRWContextState *draw_ctx = DRW_context_state_get(); DRWShadingGroup *group = stl->g_data->group; - if (ob == draw_ctx->object_edit) { + if ((ob == draw_ctx->object_edit) || BKE_object_is_in_editmode_and_selected(ob)) { MetaBall *mb = ob->data; const bool is_select = DRW_state_is_select(); diff --git a/source/blender/draw/modes/pose_mode.c b/source/blender/draw/modes/pose_mode.c index 749c3e71368..1d3d31ab54d 100644 --- a/source/blender/draw/modes/pose_mode.c +++ b/source/blender/draw/modes/pose_mode.c @@ -139,7 +139,9 @@ bool DRW_pose_mode_armature(Object *ob, Object *active_ob) const DRWContextState *draw_ctx = DRW_context_state_get(); /* Pode armature is handled by pose mode engine. */ - if ((ob == active_ob) && ((draw_ctx->object_mode & OB_MODE_POSE) != 0)) { + if (((ob == active_ob) || (ob->base_flag & BASE_SELECTED)) && + ((draw_ctx->object_mode & OB_MODE_POSE) != 0)) + { return true; } diff --git a/source/blender/editors/armature/CMakeLists.txt b/source/blender/editors/armature/CMakeLists.txt index 4301fe6582f..8a40ea3b383 100644 --- a/source/blender/editors/armature/CMakeLists.txt +++ b/source/blender/editors/armature/CMakeLists.txt @@ -28,6 +28,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/clog ../../../../intern/guardedalloc ../../../../intern/eigen ../../../../intern/glew-mx diff --git a/source/blender/editors/armature/armature_add.c b/source/blender/editors/armature/armature_add.c index cb072bee345..36dded5ed5e 100644 --- a/source/blender/editors/armature/armature_add.c +++ b/source/blender/editors/armature/armature_add.c @@ -1074,7 +1074,6 @@ void ARMATURE_OT_bone_primitive_add(wmOperatorType *ot) static int armature_subdivide_exec(bContext *C, wmOperator *op) { Object *obedit = CTX_data_edit_object(C); - bArmature *arm = obedit->data; EditBone *newbone, *tbone; int cuts, i; @@ -1083,7 +1082,7 @@ static int armature_subdivide_exec(bContext *C, wmOperator *op) /* loop over all editable bones */ // XXX the old code did this in reverse order though! - CTX_DATA_BEGIN(C, EditBone *, ebone, selected_editable_bones) + CTX_DATA_BEGIN_WITH_ID(C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) { for (i = cuts + 1; i > 1; i--) { /* compute cut ratio first */ diff --git a/source/blender/editors/armature/armature_edit.c b/source/blender/editors/armature/armature_edit.c index 75b80627dff..2335e29aca8 100644 --- a/source/blender/editors/armature/armature_edit.c +++ b/source/blender/editors/armature/armature_edit.c @@ -48,6 +48,7 @@ #include "BKE_armature.h" #include "BKE_constraint.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_global.h" #include "BKE_report.h" #include "BKE_object.h" @@ -630,25 +631,39 @@ static void fill_add_joint(EditBone *ebo, short eb_tail, ListBase *points) /* bone adding between selected joints */ static int armature_fill_bones_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - bArmature *arm = (obedit) ? obedit->data : NULL; + Object *obedit_active = CTX_data_edit_object(C); Scene *scene = CTX_data_scene(C); View3D *v3d = CTX_wm_view3d(C); ListBase points = {NULL, NULL}; EditBone *newbone = NULL; int count; + bool mixed_object_error = false; /* sanity checks */ - if (ELEM(NULL, obedit, arm)) + if (ELEM(NULL, obedit_active, obedit_active->data)) { return OPERATOR_CANCELLED; + } /* loop over all bones, and only consider if visible */ - CTX_DATA_BEGIN(C, EditBone *, ebone, visible_bones) + bArmature *arm = NULL; + CTX_DATA_BEGIN_WITH_ID(C, EditBone *, ebone, visible_bones, bArmature *, arm_iter) { - if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL)) + bool check = false; + if (!(ebone->flag & BONE_CONNECTED) && (ebone->flag & BONE_ROOTSEL)) { fill_add_joint(ebone, 0, &points); - if (ebone->flag & BONE_TIPSEL) + check = true; + } + if (ebone->flag & BONE_TIPSEL) { fill_add_joint(ebone, 1, &points); + check = true; + } + + if (check) { + if (arm && (arm != arm_iter)) { + mixed_object_error = true; + } + arm = arm_iter; + } } CTX_DATA_END; @@ -658,12 +673,30 @@ static int armature_fill_bones_exec(bContext *C, wmOperator *op) * 3+) error (a smarter method involving finding chains needs to be worked out */ count = BLI_listbase_count(&points); - + if (count == 0) { BKE_report(op->reports, RPT_ERROR, "No joints selected"); return OPERATOR_CANCELLED; } - else if (count == 1) { + else if (mixed_object_error) { + BKE_report(op->reports, RPT_ERROR, "Bones for different objects selected"); + BLI_freelistN(&points); + return OPERATOR_CANCELLED; + } + + Object *obedit = NULL; + { + ViewLayer *view_layer = CTX_data_view_layer(C); + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, OB_MODE_EDIT, ob_iter) { + if (ob_iter->data == arm) { + obedit = ob_iter; + } + } + FOREACH_OBJECT_IN_MODE_END; + } + BLI_assert(obedit != NULL); + + if (count == 1) { EditBonePoint *ebp; float curs[3]; @@ -1301,18 +1334,23 @@ static bool armature_delete_ebone_cb(const char *bone_name, void *arm_p) /* only editmode! */ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) { - bArmature *arm; EditBone *curBone, *ebone_next; - Object *obedit = CTX_data_edit_object(C); - bool changed = false; - arm = obedit->data; + bool changed_multi = false; /* cancel if nothing selected */ if (CTX_DATA_COUNT(C, selected_bones) == 0) return OPERATOR_CANCELLED; - + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + bArmature *arm = obedit->data; + bool changed = false; + armature_select_mirrored(arm); - + BKE_pose_channels_remove(obedit, armature_delete_ebone_cb, arm); for (curBone = arm->edbo->first; curBone; curBone = ebone_next) { @@ -1325,14 +1363,20 @@ static int armature_delete_selected_exec(bContext *C, wmOperator *UNUSED(op)) } } } - - if (!changed) - return OPERATOR_CANCELLED; - - ED_armature_edit_sync_selection(arm->edbo); - BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + if (changed) { + changed_multi = true; + + ED_armature_edit_sync_selection(arm->edbo); + BKE_pose_tag_recalc(CTX_data_main(C), obedit->pose); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + } + } + + if (!changed_multi) { + return OPERATOR_CANCELLED; + } return OPERATOR_FINISHED; } diff --git a/source/blender/editors/armature/armature_intern.h b/source/blender/editors/armature/armature_intern.h index 0ba720a17d0..575d1597cc4 100644 --- a/source/blender/editors/armature/armature_intern.h +++ b/source/blender/editors/armature/armature_intern.h @@ -248,10 +248,15 @@ void armature_select_mirrored_ex(struct bArmature *arm, const int flag); void armature_select_mirrored(struct bArmature *arm); void armature_tag_unselect(struct bArmature *arm); -void *get_nearest_bone(struct bContext *C, const int xy[2], bool findunsel); +void *get_nearest_bone( + struct bContext *C, const int xy[2], bool findunsel, + struct Base **r_base); + void *get_bone_from_selectbuffer( - struct Base *base, struct Object *obedit, const unsigned int *buffer, short hits, - bool findunsel, bool do_nearest); + struct Base **bases, uint bases_len, + bool is_editmode, const unsigned int *buffer, short hits, + bool findunsel, bool do_nearest, + struct Base **r_base); int bone_looper(struct Object *ob, struct Bone *bone, void *data, int (*bone_func)(struct Object *, struct Bone *, void *)); diff --git a/source/blender/editors/armature/armature_select.c b/source/blender/editors/armature/armature_select.c index 587cafa6d48..63864e75edc 100644 --- a/source/blender/editors/armature/armature_select.c +++ b/source/blender/editors/armature/armature_select.c @@ -29,6 +29,8 @@ * \ingroup edarmature */ +#include "MEM_guardedalloc.h" + #include "DNA_armature_types.h" #include "DNA_object_types.h" #include "DNA_scene_types.h" @@ -40,6 +42,7 @@ #include "BKE_context.h" #include "BKE_action.h" #include "BKE_report.h" +#include "BKE_layer.h" #include "BIF_gl.h" @@ -63,26 +66,84 @@ /* **************** PoseMode & EditMode Selection Buffer Queries *************************** */ -/* only for opengl selection indices */ -Bone *ED_armature_bone_find_index(Object *ob, int index) +Base *ED_armature_base_and_ebone_from_select_buffer( + Base **bases, uint bases_len, int hit, EditBone **r_ebone) { - bPoseChannel *pchan; - if (ob->pose == NULL) return NULL; - index >>= 16; // bone selection codes use left 2 bytes - - pchan = BLI_findlink(&ob->pose->chanbase, index); - return pchan ? pchan->bone : NULL; + const uint hit_object = hit & 0xFFFF; + Base *base = NULL; + EditBone *ebone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint base_index = 0; base_index < bases_len; base_index++) { + if (bases[base_index]->object->select_color == hit_object) { + base = bases[base_index]; + break; + } + } + if (base != NULL) { + const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + bArmature *arm = base->object->data; + ebone = BLI_findlink(arm->edbo, hit_bone); + } + *r_ebone = ebone; + return base; +} + +Object *ED_armature_object_and_ebone_from_select_buffer( + Object **objects, uint objects_len, int hit, EditBone **r_ebone) +{ + const uint hit_object = hit & 0xFFFF; + Object *ob = NULL; + EditBone *ebone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + if (objects[ob_index]->select_color == hit_object) { + ob = objects[ob_index]; + break; + } + } + if (ob != NULL) { + const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + bArmature *arm = ob->data; + ebone = BLI_findlink(arm->edbo, hit_bone); + } + *r_ebone = ebone; + return ob; +} + +Base *ED_armature_base_and_bone_from_select_buffer( + Base **bases, uint bases_len, int hit, Bone **r_bone) +{ + const uint hit_object = hit & 0xFFFF; + Base *base = NULL; + Bone *bone = NULL; + /* TODO(campbell): optimize, eg: sort & binary search. */ + for (uint base_index = 0; base_index < bases_len; base_index++) { + if (bases[base_index]->object->select_color == hit_object) { + base = bases[base_index]; + break; + } + } + if (base != NULL) { + if (base->object->pose != NULL) { + const uint hit_bone = (hit & ~BONESEL_ANY) >> 16; + bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone);; + bone = pchan ? pchan->bone : NULL; + } + } + *r_bone = bone; + return base; } /* See if there are any selected bones in this buffer */ /* only bones from base are checked on */ void *get_bone_from_selectbuffer( - Base *base, Object *obedit, const unsigned int *buffer, short hits, - bool findunsel, bool do_nearest) + Base **bases, uint bases_len, bool is_editmode, const unsigned int *buffer, short hits, + bool findunsel, bool do_nearest, Base **r_base) { Bone *bone; EditBone *ebone; void *firstunSel = NULL, *firstSel = NULL, *data; + Base *firstunSel_base = NULL, *firstSel_base = NULL; unsigned int hitresult; short i; bool takeNext = false; @@ -93,15 +154,14 @@ void *get_bone_from_selectbuffer( if (!(hitresult & BONESEL_NOSEL)) { if (hitresult & BONESEL_ANY) { /* to avoid including objects in selection */ + Base *base = NULL; bool sel; - + hitresult &= ~(BONESEL_ANY); /* Determine what the current bone is */ - if (obedit == NULL || base->object != obedit) { - /* no singular posemode, so check for correct object */ - if (base->object->select_color == (hitresult & 0xFFFF)) { - bone = ED_armature_bone_find_index(base->object, hitresult); - + if (is_editmode == false) { + base = ED_armature_base_and_bone_from_select_buffer(bases, bases_len, hitresult, &bone); + if (bone != NULL) { if (findunsel) sel = (bone->flag & BONE_SELECTED); else @@ -115,14 +175,12 @@ void *get_bone_from_selectbuffer( } } else { - bArmature *arm = obedit->data; - - ebone = BLI_findlink(arm->edbo, hitresult); + base = ED_armature_base_and_ebone_from_select_buffer(bases, bases_len, hitresult, &ebone); if (findunsel) sel = (ebone->flag & BONE_SELECTED); else sel = !(ebone->flag & BONE_SELECTED); - + data = ebone; } @@ -131,11 +189,15 @@ void *get_bone_from_selectbuffer( if (do_nearest) { if (minsel > buffer[4 * i + 1]) { firstSel = data; + firstSel_base = base; minsel = buffer[4 * i + 1]; } } else { - if (!firstSel) firstSel = data; + if (!firstSel) { + firstSel = data; + firstSel_base = base; + } takeNext = 1; } } @@ -143,12 +205,19 @@ void *get_bone_from_selectbuffer( if (do_nearest) { if (minunsel > buffer[4 * i + 1]) { firstunSel = data; + firstunSel_base = base; minunsel = buffer[4 * i + 1]; } } else { - if (!firstunSel) firstunSel = data; - if (takeNext) return data; + if (!firstunSel) { + firstunSel = data; + firstunSel_base = base; + } + if (takeNext) { + *r_base = base; + return data; + } } } } @@ -156,16 +225,22 @@ void *get_bone_from_selectbuffer( } } - if (firstunSel) + if (firstunSel) { + *r_base = firstunSel_base; return firstunSel; - else + } + else { + *r_base = firstSel_base; return firstSel; + } } /* used by posemode as well editmode */ /* only checks scene->basact! */ /* x and y are mouse coords (area space) */ -void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel) +void *get_nearest_bone( + bContext *C, const int xy[2], bool findunsel, + Base **r_base) { EvaluationContext eval_ctx; ViewContext vc; @@ -182,8 +257,20 @@ void *get_nearest_bone(bContext *C, const int xy[2], bool findunsel) hits = view3d_opengl_select(&eval_ctx, &vc, buffer, MAXPICKBUF, &rect, VIEW3D_SELECT_PICK_NEAREST); + *r_base = NULL; + if (hits > 0) { - return get_bone_from_selectbuffer(vc.view_layer->basact, vc.obedit, buffer, hits, findunsel, true); + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_mode( + eval_ctx.view_layer, &bases_len, { + .object_mode = vc.obedit ? OB_MODE_EDIT : OB_MODE_POSE, + .no_dup_data = true}); + + void *bone = get_bone_from_selectbuffer( + bases, bases_len, vc.obedit != NULL, buffer, hits, findunsel, true, r_base); + + MEM_freeN(bases); + return bone; } return NULL; } @@ -197,16 +284,17 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv bArmature *arm; EditBone *bone, *curBone, *next; const bool extend = RNA_boolean_get(op->ptr, "extend"); - Object *obedit = CTX_data_edit_object(C); - arm = obedit->data; view3d_operator_needs_opengl(C); - bone = get_nearest_bone(C, event->mval, !extend); + Base *base = NULL; + bone = get_nearest_bone(C, event->mval, !extend, &base); if (!bone) return OPERATOR_CANCELLED; + arm = base->object->data; + /* Select parents */ for (curBone = bone; curBone; curBone = next) { if ((curBone->flag & BONE_UNSELECTABLE) == 0) { @@ -249,7 +337,7 @@ static int armature_select_linked_invoke(bContext *C, wmOperator *op, const wmEv ED_armature_edit_sync_selection(arm->edbo); - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, obedit); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); return OPERATOR_FINISHED; } @@ -295,7 +383,8 @@ static int selectbuffer_ret_hits_5(unsigned int *buffer, const int hits12, const /* note that BONE ROOT only gets drawn for root bones (or without IK) */ static EditBone *get_nearest_editbonepoint( const EvaluationContext *eval_ctx, ViewContext *vc, - bool findunsel, bool use_cycle, int *r_selmask) + bool findunsel, bool use_cycle, + Base **r_base, int *r_selmask) { bArmature *arm = (bArmature *)vc->obedit->data; EditBone *ebone_next_act = arm->act_edbone; @@ -371,9 +460,11 @@ static EditBone *get_nearest_editbonepoint( cache_end: view3d_opengl_select_cache_end(); + uint bases_len; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(eval_ctx->view_layer, &bases_len); + /* See if there are any selected bones in this group */ if (hits > 0) { - if (hits == 1) { if (!(buffer[3] & BONESEL_NOSEL)) besthitresult = buffer[3]; @@ -382,10 +473,11 @@ cache_end: for (i = 0; i < hits; i++) { hitresult = buffer[3 + (i * 4)]; if (!(hitresult & BONESEL_NOSEL)) { + Base *base = NULL; + base = ED_armature_base_and_ebone_from_select_buffer(bases, bases_len, hitresult, &ebone); + arm = base->object->data; + int dep; - - ebone = BLI_findlink(arm->edbo, hitresult & ~BONESEL_ANY); - /* clicks on bone points get advantage */ if (hitresult & (BONESEL_ROOT | BONESEL_TIP)) { /* but also the unselected one */ @@ -425,11 +517,13 @@ cache_end: } } } - + if (!(besthitresult & BONESEL_NOSEL)) { - - ebone = BLI_findlink(arm->edbo, besthitresult & ~BONESEL_ANY); - + Base *base = NULL; + base = ED_armature_base_and_ebone_from_select_buffer(bases, bases_len, hitresult, &ebone); + arm = base->object->data; + *r_base = base; + *r_selmask = 0; if (besthitresult & BONESEL_ROOT) *r_selmask |= BONE_ROOTSEL; @@ -441,6 +535,7 @@ cache_end: } } *r_selmask = 0; + *r_base = NULL; return NULL; } @@ -469,6 +564,23 @@ void ED_armature_edit_deselect_all_visible(Object *obedit) ED_armature_edit_sync_selection(arm->edbo); } + +void ED_armature_edit_deselect_all_multi(struct Object **objects, uint objects_len) +{ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + ED_armature_edit_deselect_all(obedit); + } +} + +void ED_armature_edit_deselect_all_visible_multi(struct Object **objects, uint objects_len) +{ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + ED_armature_edit_deselect_all_visible(obedit); + } +} + /* accounts for connected parents */ static int ebone_select_flag(EditBone *ebone) { @@ -483,11 +595,11 @@ static int ebone_select_flag(EditBone *ebone) /* context: editmode armature in view3d */ bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, bool deselect, bool toggle) { - Object *obedit = CTX_data_edit_object(C); EvaluationContext eval_ctx; ViewContext vc; EditBone *nearBone = NULL; int selmask; + Base *basact = NULL; CTX_data_eval_ctx(C, &eval_ctx); ED_view3d_viewcontext_init(C, &vc); @@ -498,12 +610,16 @@ bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, b return true; } - nearBone = get_nearest_editbonepoint(&eval_ctx, &vc, true, true, &selmask); + nearBone = get_nearest_editbonepoint(&eval_ctx, &vc, true, true, &basact, &selmask); if (nearBone) { - bArmature *arm = obedit->data; + ED_view3d_viewcontext_init_object(&vc, basact->object); + bArmature *arm = vc.obedit->data; if (!extend && !deselect && !toggle) { - ED_armature_edit_deselect_all(obedit); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(eval_ctx.view_layer, &objects_len); + ED_armature_edit_deselect_all_multi(objects, objects_len); + MEM_freeN(objects); } /* by definition the non-root connected bones have no root point drawn, @@ -581,9 +697,14 @@ bool ED_armature_edit_select_pick(bContext *C, const int mval[2], bool extend, b if (ebone_select_flag(nearBone)) { arm->act_edbone = nearBone; } + + if (eval_ctx.view_layer->basact != basact) { + eval_ctx.view_layer->basact = basact; + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, vc.scene); + } } - - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); + + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object); return true; } @@ -1296,17 +1417,23 @@ static int armature_shortest_path_pick_invoke(bContext *C, wmOperator *op, const EditBone *ebone_isect_parent = NULL; EditBone *ebone_isect_child[2]; bool changed; + Base *base_dst = NULL; view3d_operator_needs_opengl(C); ebone_src = arm->act_edbone; - ebone_dst = get_nearest_bone(C, event->mval, false); + ebone_dst = get_nearest_bone(C, event->mval, false, &base_dst); /* fallback to object selection */ if (ELEM(NULL, ebone_src, ebone_dst) || (ebone_src == ebone_dst)) { return OPERATOR_PASS_THROUGH; } + if (base_dst && base_dst->object != obedit) { + /* Disconnected, ignore. */ + return OPERATOR_CANCELLED; + } + ebone_isect_child[0] = ebone_src; ebone_isect_child[1] = ebone_dst; diff --git a/source/blender/editors/armature/editarmature_undo.c b/source/blender/editors/armature/editarmature_undo.c index 217de06d99b..f6f97af32b9 100644 --- a/source/blender/editors/armature/editarmature_undo.c +++ b/source/blender/editors/armature/editarmature_undo.c @@ -27,26 +27,34 @@ * \ingroup edarmature */ +#include "MEM_guardedalloc.h" + + +#include "CLG_log.h" + #include "DNA_armature_types.h" #include "DNA_object_types.h" -#include "MEM_guardedalloc.h" - #include "BLI_math.h" #include "BLI_array_utils.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_undo_system.h" #include "DEG_depsgraph.h" #include "ED_armature.h" #include "ED_object.h" +#include "ED_undo.h" #include "ED_util.h" #include "WM_types.h" #include "WM_api.h" +/** We only need this locally. */ +static CLG_LogRef LOG = {"ed.undo.armature"}; + /* -------------------------------------------------------------------- */ /** \name Undo Conversion * \{ */ @@ -121,13 +129,20 @@ static Object *editarm_object_from_context(bContext *C) /* -------------------------------------------------------------------- */ /** \name Implements ED Undo System + * + * \note This is similar for all edit-mode types. * \{ */ -typedef struct ArmatureUndoStep { - UndoStep step; - /* note: will split out into list for multi-object-editmode. */ +typedef struct ArmatureUndoStep_Elem { + struct ArmatureUndoStep_Elem *next, *prev; UndoRefID_Object obedit_ref; UndoArmature data; +} ArmatureUndoStep_Elem; + +typedef struct ArmatureUndoStep { + UndoStep step; + ArmatureUndoStep_Elem *elems; + uint elems_len; } ArmatureUndoStep; static bool armature_undosys_poll(bContext *C) @@ -138,10 +153,24 @@ static bool armature_undosys_poll(bContext *C) static bool armature_undosys_step_encode(struct bContext *C, UndoStep *us_p) { ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; - us->obedit_ref.ptr = editarm_object_from_context(C); - bArmature *arm = us->obedit_ref.ptr->data; - undoarm_from_editarm(&us->data, arm); - us->step.data_size = us->data.undo_size; + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); + us->elems_len = objects_len; + + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + ArmatureUndoStep_Elem *elem = &us->elems[i]; + + elem->obedit_ref.ptr = ob; + bArmature *arm = elem->obedit_ref.ptr->data; + undoarm_from_editarm(&elem->data, arm); + us->step.data_size += elem->data.undo_size; + } + MEM_freeN(objects); return true; } @@ -152,24 +181,46 @@ static void armature_undosys_step_decode(struct bContext *C, UndoStep *us_p, int BLI_assert(armature_undosys_poll(C)); ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; - Object *obedit = us->obedit_ref.ptr; - bArmature *arm = obedit->data; - undoarm_to_editarm(&us->data, arm); - DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + + for (uint i = 0; i < us->elems_len; i++) { + ArmatureUndoStep_Elem *elem = &us->elems[i]; + Object *obedit = elem->obedit_ref.ptr; + bArmature *arm = obedit->data; + if (arm->edbo == NULL) { + /* Should never fail, may not crash but can give odd behavior. */ + CLOG_ERROR(&LOG, "name='%s', failed to enter edit-mode for object '%s', undo state invalid", us_p->name, obedit->id.name); + continue; + } + undoarm_to_editarm(&elem->data, arm); + DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + } + + /* The first element is always active */ + ED_undo_object_set_active_or_warn(CTX_data_view_layer(C), us->elems[0].obedit_ref.ptr, us_p->name, &LOG); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } static void armature_undosys_step_free(UndoStep *us_p) { ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; - undoarm_free_data(&us->data); + + for (uint i = 0; i < us->elems_len; i++) { + ArmatureUndoStep_Elem *elem = &us->elems[i]; + undoarm_free_data(&elem->data); + } + MEM_freeN(us->elems); } static void armature_undosys_foreach_ID_ref( UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data) { ArmatureUndoStep *us = (ArmatureUndoStep *)us_p; - foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref)); + + for (uint i = 0; i < us->elems_len; i++) { + ArmatureUndoStep_Elem *elem = &us->elems[i]; + foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref)); + } } /* Export for ED_undo_sys. */ diff --git a/source/blender/editors/armature/pose_edit.c b/source/blender/editors/armature/pose_edit.c index 520ecc797aa..ea93e024f8e 100644 --- a/source/blender/editors/armature/pose_edit.c +++ b/source/blender/editors/armature/pose_edit.c @@ -1015,7 +1015,6 @@ static int armature_bone_layers_invoke(bContext *C, wmOperator *op, const wmEven static int armature_bone_layers_exec(bContext *C, wmOperator *op) { Object *ob = CTX_data_edit_object(C); - bArmature *arm = (ob) ? ob->data : NULL; PointerRNA ptr; int layers[32]; /* hardcoded for now - we can only have 32 armature layers, so this should be fine... */ @@ -1023,7 +1022,7 @@ static int armature_bone_layers_exec(bContext *C, wmOperator *op) RNA_boolean_get_array(op->ptr, "layers", layers); /* set layers of pchans based on the values set in the operator props */ - CTX_DATA_BEGIN (C, EditBone *, ebone, selected_editable_bones) + CTX_DATA_BEGIN_WITH_ID (C, EditBone *, ebone, selected_editable_bones, bArmature *, arm) { /* get pointer for pchan, and write flags this way */ RNA_pointer_create((ID *)arm, &RNA_EditBone, ebone, &ptr); diff --git a/source/blender/editors/armature/pose_select.c b/source/blender/editors/armature/pose_select.c index 1c23f71233d..b6f1e101291 100644 --- a/source/blender/editors/armature/pose_select.c +++ b/source/blender/editors/armature/pose_select.c @@ -45,6 +45,7 @@ #include "BKE_context.h" #include "BKE_object.h" #include "BKE_report.h" +#include "BKE_layer.h" #include "DEG_depsgraph.h" @@ -145,7 +146,9 @@ bool ED_armature_pose_select_pick_with_buffer( Object *ob_act = OBACT(view_layer); Object *obedit = OBEDIT_FROM_VIEW_LAYER(view_layer); - nearBone = get_bone_from_selectbuffer(base, obedit, buffer, hits, 1, do_nearest); + /* Callers happen to already get the active base */ + Base *base_dummy = NULL; + nearBone = get_bone_from_selectbuffer(&base, 1, obedit != NULL, buffer, hits, 1, do_nearest, &base_dummy); /* if the bone cannot be affected, don't do anything */ if ((nearBone) && !(nearBone->flag & BONE_UNSELECTABLE)) { @@ -166,7 +169,12 @@ bool ED_armature_pose_select_pick_with_buffer( } if (!extend && !deselect && !toggle) { - ED_pose_deselect_all(ob, SEL_DESELECT, true); + { + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + ED_pose_deselect_all_multi(objects, objects_len, SEL_DESELECT, true); + MEM_SAFE_FREE(objects); + } nearBone->flag |= (BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); arm->act_bone = nearBone; } @@ -252,6 +260,43 @@ void ED_pose_deselect_all(Object *ob, int select_mode, const bool ignore_visibil } } +static bool ed_pose_is_any_selected(Object *ob, bool ignore_visibility) +{ + bArmature *arm = ob->data; + for (bPoseChannel *pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { + if (ignore_visibility || PBONE_VISIBLE(arm, pchan->bone)) { + if (pchan->bone->flag & BONE_SELECTED) { + return true; + } + } + } + return false; +} + +static bool ed_pose_is_any_selected_multi(Object **objects, uint objects_len, bool ignore_visibility) +{ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + if (ed_pose_is_any_selected(ob_iter, ignore_visibility)) { + return true; + } + } + return false; +} + +void ED_pose_deselect_all_multi(Object **objects, uint objects_len, int select_mode, const bool ignore_visibility) +{ + if (select_mode == SEL_TOGGLE) { + select_mode = ed_pose_is_any_selected_multi( + objects, objects_len, ignore_visibility) ? SEL_DESELECT : SEL_SELECT; + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + ED_pose_deselect_all(ob_iter, select_mode, ignore_visibility); + } +} + /* ***************** Selections ********************** */ static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend) @@ -278,17 +323,18 @@ static void selectconnected_posebonechildren(Object *ob, Bone *bone, int extend) /* previously known as "selectconnected_posearmature" */ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (bArmature *)ob->data; Bone *bone, *curBone, *next = NULL; const bool extend = RNA_boolean_get(op->ptr, "extend"); view3d_operator_needs_opengl(C); - bone = get_nearest_bone(C, event->mval, !extend); + Base *base = NULL; + bone = get_nearest_bone(C, event->mval, !extend, &base); if (!bone) return OPERATOR_CANCELLED; + + bArmature *arm = base->object->data; /* Select parents */ for (curBone = bone; curBone; curBone = next) { @@ -310,14 +356,14 @@ static int pose_select_connected_invoke(bContext *C, wmOperator *op, const wmEve /* Select children */ for (curBone = bone->childbase.first; curBone; curBone = next) - selectconnected_posebonechildren(ob, curBone, extend); + selectconnected_posebonechildren(base->object, curBone, extend); /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); if (arm->flag & ARM_HAS_VIZ_DEPS) { /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&base->object->id, OB_RECALC_DATA); } return OPERATOR_FINISHED; @@ -354,27 +400,31 @@ static int pose_de_select_all_exec(bContext *C, wmOperator *op) int action = RNA_enum_get(op->ptr, "action"); Scene *scene = CTX_data_scene(C); - Object *ob = ED_object_context(C); - bArmature *arm = ob->data; int multipaint = scene->toolsettings->multipaint; if (action == SEL_TOGGLE) { action = CTX_DATA_COUNT(C, selected_pose_bones) ? SEL_DESELECT : SEL_SELECT; } + + Object *ob_prev = NULL; /* Set the flags */ - CTX_DATA_BEGIN(C, bPoseChannel *, pchan, visible_pose_bones) + CTX_DATA_BEGIN_WITH_ID(C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { + bArmature *arm = ob->data; pose_do_bone_select(pchan, action); + + if (ob_prev != ob) { + /* weightpaint or mask modifiers need depsgraph updates */ + if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) { + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + ob_prev = ob; + } } CTX_DATA_END; WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, NULL); - - /* weightpaint or mask modifiers need depsgraph updates */ - if (multipaint || (arm->flag & ARM_HAS_VIZ_DEPS)) { - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); - } return OPERATOR_FINISHED; } @@ -450,13 +500,13 @@ void POSE_OT_select_parent(wmOperatorType *ot) static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op)) { - Object *ob = BKE_object_pose_armature_get(CTX_data_active_object(C)); - bArmature *arm = (bArmature *)ob->data; bConstraint *con; int found = 0; - - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) + Object *ob_prev = NULL; + + CTX_DATA_BEGIN_WITH_ID (C, bPoseChannel *, pchan, visible_pose_bones, Object *, ob) { + bArmature *arm = ob->data; if (pchan->bone->flag & BONE_SELECTED) { for (con = pchan->constraints.first; con; con = con->next) { const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con); @@ -472,6 +522,16 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op if ((pchanc) && !(pchanc->bone->flag & BONE_UNSELECTABLE)) { pchanc->bone->flag |= BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL; found = 1; + + if (ob != ob_prev) { + /* updates */ + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); + if (arm->flag & ARM_HAS_VIZ_DEPS) { + /* mask modifier ('armature' mode), etc. */ + DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + } + ob_prev = ob; + } } } } @@ -487,14 +547,6 @@ static int pose_select_constraint_target_exec(bContext *C, wmOperator *UNUSED(op if (!found) return OPERATOR_CANCELLED; - /* updates */ - WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, ob); - - if (arm->flag & ARM_HAS_VIZ_DEPS) { - /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); - } - return OPERATOR_FINISHED; } diff --git a/source/blender/editors/curve/CMakeLists.txt b/source/blender/editors/curve/CMakeLists.txt index 85daa7e44e5..301d333ebdb 100644 --- a/source/blender/editors/curve/CMakeLists.txt +++ b/source/blender/editors/curve/CMakeLists.txt @@ -28,6 +28,7 @@ set(INC ../../makesdna ../../makesrna ../../windowmanager + ../../../../intern/clog ../../../../intern/guardedalloc ../../../../intern/glew-mx ../../../../extern/curve_fit_nd diff --git a/source/blender/editors/curve/editcurve_undo.c b/source/blender/editors/curve/editcurve_undo.c index 4eb2abaefad..ad17331853b 100644 --- a/source/blender/editors/curve/editcurve_undo.c +++ b/source/blender/editors/curve/editcurve_undo.c @@ -22,12 +22,14 @@ * \ingroup edcurve */ +#include "MEM_guardedalloc.h" + +#include "CLG_log.h" + #include "DNA_object_types.h" #include "DNA_scene_types.h" #include "DNA_anim_types.h" -#include "MEM_guardedalloc.h" - #include "BLI_blenlib.h" #include "BLI_ghash.h" #include "BLI_array_utils.h" @@ -35,6 +37,7 @@ #include "BKE_context.h" #include "BKE_curve.h" #include "BKE_fcurve.h" +#include "BKE_layer.h" #include "BKE_library.h" #include "BKE_animsys.h" #include "BKE_undo_system.h" @@ -42,6 +45,7 @@ #include "DEG_depsgraph.h" #include "ED_object.h" +#include "ED_undo.h" #include "ED_util.h" #include "ED_curve.h" @@ -50,6 +54,9 @@ #include "curve_intern.h" +/** We only need this locally. */ +static CLG_LogRef LOG = {"ed.undo.curve"}; + /* -------------------------------------------------------------------- */ /** \name Undo Conversion * \{ */ @@ -187,13 +194,19 @@ static Object *editcurve_object_from_context(bContext *C) /* -------------------------------------------------------------------- */ /** \name Implements ED Undo System + * + * \note This is similar for all edit-mode types. * \{ */ -typedef struct CurveUndoStep { - UndoStep step; - /* note: will split out into list for multi-object-editmode. */ +typedef struct CurveUndoStep_Elem { UndoRefID_Object obedit_ref; UndoCurve data; +} CurveUndoStep_Elem; + +typedef struct CurveUndoStep { + UndoStep step; + CurveUndoStep_Elem *elems; + uint elems_len; } CurveUndoStep; static bool curve_undosys_poll(bContext *C) @@ -205,9 +218,23 @@ static bool curve_undosys_poll(bContext *C) static bool curve_undosys_step_encode(struct bContext *C, UndoStep *us_p) { CurveUndoStep *us = (CurveUndoStep *)us_p; - us->obedit_ref.ptr = editcurve_object_from_context(C); - undocurve_from_editcurve(&us->data, us->obedit_ref.ptr->data, us->obedit_ref.ptr->shapenr); - us->step.data_size = us->data.undo_size; + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); + us->elems_len = objects_len; + + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + CurveUndoStep_Elem *elem = &us->elems[i]; + + elem->obedit_ref.ptr = ob; + undocurve_from_editcurve(&elem->data, ob->data, ob->shapenr); + us->step.data_size += elem->data.undo_size; + } + MEM_freeN(objects); return true; } @@ -218,23 +245,47 @@ static void curve_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UN BLI_assert(curve_undosys_poll(C)); CurveUndoStep *us = (CurveUndoStep *)us_p; - Object *obedit = us->obedit_ref.ptr; - undocurve_to_editcurve(&us->data, obedit->data, &obedit->shapenr); - DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + + for (uint i = 0; i < us->elems_len; i++) { + CurveUndoStep_Elem *elem = &us->elems[i]; + Object *obedit = elem->obedit_ref.ptr; + Curve *cu = obedit->data; + if (cu->editnurb == NULL) { + /* Should never fail, may not crash but can give odd behavior. */ + CLOG_ERROR(&LOG, "name='%s', failed to enter edit-mode for object '%s', undo state invalid", + us_p->name, obedit->id.name); + continue; + } + undocurve_to_editcurve(&elem->data, obedit->data, &obedit->shapenr); + DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + } + + /* The first element is always active */ + ED_undo_object_set_active_or_warn(CTX_data_view_layer(C), us->elems[0].obedit_ref.ptr, us_p->name, &LOG); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } static void curve_undosys_step_free(UndoStep *us_p) { CurveUndoStep *us = (CurveUndoStep *)us_p; - undocurve_free_data(&us->data); + + for (uint i = 0; i < us->elems_len; i++) { + CurveUndoStep_Elem *elem = &us->elems[i]; + undocurve_free_data(&elem->data); + } + MEM_freeN(us->elems); } static void curve_undosys_foreach_ID_ref( UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data) { CurveUndoStep *us = (CurveUndoStep *)us_p; - foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref)); + + for (uint i = 0; i < us->elems_len; i++) { + CurveUndoStep_Elem *elem = &us->elems[i]; + foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref)); + } } /* Export for ED_undo_sys. */ diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index 186ca5313e5..e6284cb1656 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -142,19 +142,29 @@ void ED_armature_edit_free(struct bArmature *arm); void ED_armature_edit_deselect_all(struct Object *obedit); void ED_armature_edit_deselect_all_visible(struct Object *obedit); +void ED_armature_edit_deselect_all_multi(struct Object **objects, uint objects_len); +void ED_armature_edit_deselect_all_visible_multi(struct Object **objects, uint objects_len); + bool ED_armature_pose_select_pick_with_buffer( struct ViewLayer *view_layer, struct Base *base, const unsigned int *buffer, short hits, bool extend, bool deselect, bool toggle, bool do_nearest); bool ED_armature_edit_select_pick( struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle); int join_armature_exec(struct bContext *C, struct wmOperator *op); -struct Bone *ED_armature_bone_find_index(struct Object *ob, int index); float ED_armature_ebone_roll_to_vector(const EditBone *bone, const float new_up_axis[3], const bool axis_only); EditBone *ED_armature_ebone_find_name(const struct ListBase *edbo, const char *name); EditBone *ED_armature_ebone_get_mirrored(const struct ListBase *edbo, EditBone *ebo); void ED_armature_edit_sync_selection(struct ListBase *edbo); void ED_armature_edit_validate_active(struct bArmature *arm); +struct Base *ED_armature_base_and_ebone_from_select_buffer( + struct Base **bases, uint bases_len, int hit, struct EditBone **r_ebone); +struct Object *ED_armature_object_and_ebone_from_select_buffer( + struct Object **objects, uint objects_len, int hit, struct EditBone **r_ebone); + +struct Base *ED_armature_base_and_bone_from_select_buffer( + struct Base **bases, uint bases_len, int hit, struct Bone **r_bone); + EditBone *ED_armature_ebone_add_primitive(struct Object *obedit_arm, float length, bool view_aligned); EditBone *ED_armature_ebone_add(struct bArmature *arm, const char *name); @@ -211,6 +221,7 @@ bool ED_object_posemode_exit(struct bContext *C, struct Object *ob); bool ED_object_posemode_enter_ex(struct Main *bmain, struct Object *ob); bool ED_object_posemode_enter(struct bContext *C, struct Object *ob); void ED_pose_deselect_all(struct Object *ob, int select_mode, const bool ignore_visibility); +void ED_pose_deselect_all_multi(struct Object **objects, uint objects_len, int select_mode, const bool ignore_visibility); void ED_pose_bone_select(struct Object *ob, struct bPoseChannel *pchan, bool select); void ED_pose_recalculate_paths(struct bContext *C, struct Scene *scene, struct Object *ob); struct Object *ED_pose_object_from_context(struct bContext *C); diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index 3217433204e..d3f2e1fff85 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -340,6 +340,9 @@ struct MDeformVert *ED_mesh_active_dvert_get_em(struct Object *ob, struct BMVert struct MDeformVert *ED_mesh_active_dvert_get_ob(struct Object *ob, int *r_index); struct MDeformVert *ED_mesh_active_dvert_get_only(struct Object *ob); +void EDBM_mesh_stats_multi(struct Object **objects, const uint objects_len, int totelem[3], int totelem_sel[3]); +void EDBM_mesh_elem_index_ensure_multi(struct Object **objects, const uint objects_len, const char htype); + #define ED_MESH_PICK_DEFAULT_VERT_SIZE 50 #define ED_MESH_PICK_DEFAULT_FACE_SIZE 3 diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 83119062203..bfc3325d7eb 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -124,10 +124,13 @@ enum { EM_WAITCURSOR = (1 << 1), EM_DO_UNDO = (1 << 2), EM_IGNORE_LAYER = (1 << 3), + EM_NO_CONTEXT = (1 << 4), }; void ED_object_editmode_exit_ex( struct bContext *C, struct Scene *scene, struct Object *obedit, int flag); void ED_object_editmode_exit(struct bContext *C, int flag); + +void ED_object_editmode_enter_ex(struct Scene *scene, struct Object *ob, int flag); void ED_object_editmode_enter(struct bContext *C, int flag); bool ED_object_editmode_load(struct Object *obedit); diff --git a/source/blender/editors/include/ED_undo.h b/source/blender/editors/include/ED_undo.h index b3814ab5899..43ffb091666 100644 --- a/source/blender/editors/include/ED_undo.h +++ b/source/blender/editors/include/ED_undo.h @@ -26,6 +26,7 @@ #define __ED_UNDO_H__ struct bContext; +struct CLG_LogRef; struct wmOperator; struct wmOperatorType; struct UndoStack; @@ -53,6 +54,10 @@ bool ED_undo_is_valid(const struct bContext *C, const char *undoname); struct UndoStack *ED_undo_stack_get(void); +/* helpers */ +void ED_undo_object_set_active_or_warn( + struct ViewLayer *view_layer, struct Object *ob, const char *info, struct CLG_LogRef *log); + /* undo_system_types.c */ void ED_undosys_type_init(void); void ED_undosys_type_free(void); diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 2a5ad494643..fd532e70478 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -119,17 +119,20 @@ void ED_uvedit_live_unwrap_end(short cancel); void ED_uvedit_live_unwrap(struct Scene *scene, struct Object *obedit); void ED_uvedit_pack_islands( -struct Scene *scene, struct Object *ob, struct BMesh *bm, bool selected, bool correct_aspect, bool do_rotate); + struct Scene *scene, struct Object *ob, struct BMesh *bm, bool selected, bool correct_aspect, bool do_rotate); +void ED_uvedit_pack_islands_multi( + struct Scene *scene, struct Object **objects, const uint objects_len, + bool selected, bool correct_aspect, bool do_rotate); void ED_uvedit_unwrap_cube_project( struct BMesh *bm, float cube_size, bool use_select, const float center[3]); /* single call up unwrap using scene settings, used for edge tag unwrapping */ -void ED_unwrap_lscm(struct Scene *scene, struct Object *obedit, const short sel); +void ED_unwrap_lscm(struct Scene *scene, struct Object *obedit, const short sel, const bool pack); /* uvedit_draw.c */ void ED_image_draw_cursor( -struct ARegion *ar, const float cursor[2]); + struct ARegion *ar, const float cursor[2]); void ED_uvedit_draw_main( struct SpaceImage *sima, struct ARegion *ar, struct Scene *scene, struct ViewLayer *view_layer, diff --git a/source/blender/editors/include/ED_view3d.h b/source/blender/editors/include/ED_view3d.h index 7f18c10f970..c5b99013610 100644 --- a/source/blender/editors/include/ED_view3d.h +++ b/source/blender/editors/include/ED_view3d.h @@ -352,6 +352,7 @@ int view3d_opengl_select( /* view3d_select.c */ float ED_view3d_select_dist_px(void); void ED_view3d_viewcontext_init(struct bContext *C, struct ViewContext *vc); +void ED_view3d_viewcontext_init_object(struct ViewContext *vc, struct Object *obact); void view3d_operator_needs_opengl(const struct bContext *C); void view3d_region_operator_needs_opengl(struct wmWindow *win, struct ARegion *ar); void view3d_opengl_read_pixels(struct ARegion *ar, int x, int y, int w, int h, int format, int type, void *data); diff --git a/source/blender/editors/lattice/CMakeLists.txt b/source/blender/editors/lattice/CMakeLists.txt index eaf837cf978..2207e0fa736 100644 --- a/source/blender/editors/lattice/CMakeLists.txt +++ b/source/blender/editors/lattice/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../makesrna ../../render/extern/include ../../windowmanager + ../../../../intern/clog ../../../../intern/guardedalloc ) diff --git a/source/blender/editors/lattice/editlattice_undo.c b/source/blender/editors/lattice/editlattice_undo.c index 58fa08e5aa9..cbd89016b44 100644 --- a/source/blender/editors/lattice/editlattice_undo.c +++ b/source/blender/editors/lattice/editlattice_undo.c @@ -33,6 +33,8 @@ #include "MEM_guardedalloc.h" +#include "CLG_log.h" + #include "BLI_utildefines.h" #include "BLI_array_utils.h" @@ -42,12 +44,14 @@ #include "DNA_scene_types.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_undo_system.h" #include "DEG_depsgraph.h" #include "ED_object.h" #include "ED_lattice.h" +#include "ED_undo.h" #include "ED_util.h" #include "WM_types.h" @@ -55,6 +59,9 @@ #include "lattice_intern.h" +/** We only need this locally. */ +static CLG_LogRef LOG = {"ed.undo.lattice"}; + /* -------------------------------------------------------------------- */ /** \name Undo Conversion * \{ */ @@ -124,13 +131,19 @@ static Object *editlatt_object_from_context(bContext *C) /* -------------------------------------------------------------------- */ /** \name Implements ED Undo System + * + * \note This is similar for all edit-mode types. * \{ */ -typedef struct LatticeUndoStep { - UndoStep step; - /* note: will split out into list for multi-object-editmode. */ +typedef struct LatticeUndoStep_Elem { UndoRefID_Object obedit_ref; UndoLattice data; +} LatticeUndoStep_Elem; + +typedef struct LatticeUndoStep { + UndoStep step; + LatticeUndoStep_Elem *elems; + uint elems_len; } LatticeUndoStep; static bool lattice_undosys_poll(bContext *C) @@ -141,10 +154,24 @@ static bool lattice_undosys_poll(bContext *C) static bool lattice_undosys_step_encode(struct bContext *C, UndoStep *us_p) { LatticeUndoStep *us = (LatticeUndoStep *)us_p; - us->obedit_ref.ptr = editlatt_object_from_context(C); - Lattice *lt = us->obedit_ref.ptr->data; - undolatt_from_editlatt(&us->data, lt->editlatt); - us->step.data_size = us->data.undo_size; + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); + us->elems_len = objects_len; + + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + LatticeUndoStep_Elem *elem = &us->elems[i]; + + elem->obedit_ref.ptr = ob; + Lattice *lt = ob->data; + undolatt_from_editlatt(&elem->data, lt->editlatt); + us->step.data_size += elem->data.undo_size; + } + MEM_freeN(objects); return true; } @@ -155,25 +182,47 @@ static void lattice_undosys_step_decode(struct bContext *C, UndoStep *us_p, int BLI_assert(lattice_undosys_poll(C)); LatticeUndoStep *us = (LatticeUndoStep *)us_p; - Object *obedit = us->obedit_ref.ptr; - Lattice *lt = obedit->data; - EditLatt *editlatt = lt->editlatt; - undolatt_to_editlatt(&us->data, editlatt); - DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + + for (uint i = 0; i < us->elems_len; i++) { + LatticeUndoStep_Elem *elem = &us->elems[i]; + Object *obedit = elem->obedit_ref.ptr; + Lattice *lt = obedit->data; + if (lt->editlatt == NULL) { + /* Should never fail, may not crash but can give odd behavior. */ + CLOG_ERROR(&LOG, "name='%s', failed to enter edit-mode for object '%s', undo state invalid", + us_p->name, obedit->id.name); + continue; + } + undolatt_to_editlatt(&elem->data, lt->editlatt); + DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + } + + /* The first element is always active */ + ED_undo_object_set_active_or_warn(CTX_data_view_layer(C), us->elems[0].obedit_ref.ptr, us_p->name, &LOG); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } static void lattice_undosys_step_free(UndoStep *us_p) { LatticeUndoStep *us = (LatticeUndoStep *)us_p; - undolatt_free_data(&us->data); + + for (uint i = 0; i < us->elems_len; i++) { + LatticeUndoStep_Elem *elem = &us->elems[i]; + undolatt_free_data(&elem->data); + } + MEM_freeN(us->elems); } static void lattice_undosys_foreach_ID_ref( UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data) { LatticeUndoStep *us = (LatticeUndoStep *)us_p; - foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref)); + + for (uint i = 0; i < us->elems_len; i++) { + LatticeUndoStep_Elem *elem = &us->elems[i]; + foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref)); + } } /* Export for ED_undo_sys. */ diff --git a/source/blender/editors/mesh/CMakeLists.txt b/source/blender/editors/mesh/CMakeLists.txt index 3877838ec54..eae6b7192d7 100644 --- a/source/blender/editors/mesh/CMakeLists.txt +++ b/source/blender/editors/mesh/CMakeLists.txt @@ -32,6 +32,7 @@ set(INC ../../makesrna ../../render/extern/include ../../windowmanager + ../../../../intern/clog ../../../../intern/guardedalloc ../../../../intern/glew-mx ) diff --git a/source/blender/editors/mesh/editmesh_select.c b/source/blender/editors/mesh/editmesh_select.c index 87937fd4146..20cebc9d4b9 100644 --- a/source/blender/editors/mesh/editmesh_select.c +++ b/source/blender/editors/mesh/editmesh_select.c @@ -45,6 +45,7 @@ #include "BKE_report.h" #include "BKE_paint.h" #include "BKE_editmesh.h" +#include "BKE_layer.h" #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" @@ -917,7 +918,7 @@ BMFace *EDBM_face_find_nearest(const struct EvaluationContext *eval_ctx, ViewCon */ static int unified_findnearest( const struct EvaluationContext *eval_ctx, ViewContext *vc, - BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa) + Base **r_base, BMVert **r_eve, BMEdge **r_eed, BMFace **r_efa) { BMEditMesh *em = vc->em; static short mval_prev[2] = {-1, -1}; @@ -934,32 +935,70 @@ static int unified_findnearest( BMEdge *eed = NULL; BMFace *efa = NULL; + /* TODO(campbell): perform selection as one pass + * instead of many smaller passes (which doesn't work for zbuf occlusion). */ + uint bases_len = 0; + Base **bases = BKE_view_layer_array_from_bases_in_edit_mode(eval_ctx->view_layer, &bases_len); /* no afterqueue (yet), so we check it now, otherwise the em_xxxofs indices are bad */ - ED_view3d_backbuf_validate(eval_ctx, vc); if ((dist > 0.0f) && em->selectmode & SCE_SELECT_FACE) { float dist_center = 0.0f; float *dist_center_p = (em->selectmode & (SCE_SELECT_EDGE | SCE_SELECT_VERTEX)) ? &dist_center : NULL; - efa = EDBM_face_find_nearest_ex(eval_ctx, vc, &dist, dist_center_p, true, use_cycle, &efa_zbuf); - if (efa && dist_center_p) { - dist = min_ff(dist_margin, dist_center); - } + + for (uint base_index = 0; base_index < bases_len; base_index++) { + Base *base_iter = bases[base_index]; + Object *obedit = base_iter->object; + ED_view3d_viewcontext_init_object(vc, obedit); + ED_view3d_backbuf_validate(eval_ctx, vc); + + BMFace *efa_test = EDBM_face_find_nearest_ex(eval_ctx, vc, &dist, dist_center_p, true, use_cycle, &efa_zbuf); + if (efa && dist_center_p) { + dist = min_ff(dist_margin, dist_center); + } + if (efa_test) { + *r_base = base_iter; + efa = efa_test; + } + } /* bases */ } if ((dist > 0.0f) && (em->selectmode & SCE_SELECT_EDGE)) { float dist_center = 0.0f; float *dist_center_p = (em->selectmode & SCE_SELECT_VERTEX) ? &dist_center : NULL; - eed = EDBM_edge_find_nearest_ex(eval_ctx, vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf); - if (eed && dist_center_p) { - dist = min_ff(dist_margin, dist_center); - } + + for (uint base_index = 0; base_index < bases_len; base_index++) { + Base *base_iter = bases[base_index]; + Object *obedit = base_iter->object; + ED_view3d_viewcontext_init_object(vc, obedit); + ED_view3d_backbuf_validate(eval_ctx, vc); + BMEdge *eed_test = EDBM_edge_find_nearest_ex(eval_ctx, vc, &dist, dist_center_p, true, use_cycle, &eed_zbuf); + if (eed && dist_center_p) { + dist = min_ff(dist_margin, dist_center); + } + if (eed_test) { + *r_base = base_iter; + eed = eed_test; + } + } /* bases */ } if ((dist > 0.0f) && em->selectmode & SCE_SELECT_VERTEX) { - eve = EDBM_vert_find_nearest_ex(eval_ctx, vc, &dist, true, use_cycle); + for (uint base_index = 0; base_index < bases_len; base_index++) { + Base *base_iter = bases[base_index]; + Object *obedit = base_iter->object; + ED_view3d_viewcontext_init_object(vc, obedit); + ED_view3d_backbuf_validate(eval_ctx, vc); + BMVert *eve_test = EDBM_vert_find_nearest_ex(eval_ctx, vc, &dist, true, use_cycle); + if (eve_test) { + *r_base = base_iter; + eve = eve_test; + } + } /* bases */ } + MEM_SAFE_FREE(bases); + /* return only one of 3 pointers, for frontbuffer redraws */ if (eve) { efa = NULL; eed = NULL; @@ -1804,27 +1843,43 @@ void MESH_OT_edgering_select(wmOperatorType *ot) static int edbm_select_all_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - const int action = RNA_enum_get(op->ptr, "action"); + ViewLayer *view_layer = CTX_data_view_layer(C); + int action = RNA_enum_get(op->ptr, "action"); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + if (action == SEL_TOGGLE) { + action = SEL_SELECT; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel) { + action = SEL_DESELECT; + break; + } + } + } - switch (action) { - case SEL_TOGGLE: - EDBM_select_toggle_all(em); - break; - case SEL_SELECT: - EDBM_flag_enable_all(em, BM_ELEM_SELECT); - break; - case SEL_DESELECT: - EDBM_flag_disable_all(em, BM_ELEM_SELECT); - break; - case SEL_INVERT: - EDBM_select_swap(em); - EDBM_selectmode_flush(em); - break; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + switch (action) { + case SEL_SELECT: + EDBM_flag_enable_all(em, BM_ELEM_SELECT); + break; + case SEL_DESELECT: + EDBM_flag_disable_all(em, BM_ELEM_SELECT); + break; + case SEL_INVERT: + EDBM_select_swap(em); + EDBM_selectmode_flush(em); + break; + } + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); } - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + MEM_SAFE_FREE(objects); return OPERATOR_FINISHED; } @@ -1896,6 +1951,8 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect { EvaluationContext eval_ctx; ViewContext vc; + + Base *basact = NULL; BMVert *eve = NULL; BMEdge *eed = NULL; BMFace *efa = NULL; @@ -1906,11 +1963,23 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect vc.mval[0] = mval[0]; vc.mval[1] = mval[1]; - if (unified_findnearest(&eval_ctx, &vc, &eve, &eed, &efa)) { + if (unified_findnearest(&eval_ctx, &vc, &basact, &eve, &eed, &efa)) { + ED_view3d_viewcontext_init_object(&vc, basact->object); /* Deselect everything */ - if (extend == false && deselect == false && toggle == false) - EDBM_flag_disable_all(vc.em, BM_ELEM_SELECT); + if (extend == false && deselect == false && toggle == false) { + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(eval_ctx.view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + EDBM_flag_disable_all(BKE_editmesh_from_object(ob_iter), BM_ELEM_SELECT); + if (basact->object != ob_iter) { + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); + } + } + MEM_SAFE_FREE(objects); + } if (efa) { if (extend) { @@ -2020,7 +2089,14 @@ bool EDBM_select_pick(bContext *C, const int mval[2], bool extend, bool deselect } + /* Changing active object is handy since it allows us to + * switch UV layers, vgroups for eg. */ + if (eval_ctx.view_layer->basact != basact) { + eval_ctx.view_layer->basact = basact; + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, vc.scene); + } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); + return true; } @@ -2242,11 +2318,14 @@ bool EDBM_selectmode_toggle( bContext *C, const short selectmode_new, const int action, const bool use_extend, const bool use_expand) { + EvaluationContext eval_ctx; ToolSettings *ts = CTX_data_tool_settings(C); Object *obedit = CTX_data_edit_object(C); BMEditMesh *em = NULL; bool ret = false; + CTX_data_eval_ctx(C, &eval_ctx); + if (obedit && obedit->type == OB_MESH) { em = BKE_editmesh_from_object(obedit); } @@ -2255,6 +2334,7 @@ bool EDBM_selectmode_toggle( return ret; } + bool only_update = false; switch (action) { case -1: /* already set */ @@ -2262,21 +2342,24 @@ bool EDBM_selectmode_toggle( case 0: /* disable */ /* check we have something to do */ if ((em->selectmode & selectmode_new) == 0) { - return false; + only_update = true; + break; } em->selectmode &= ~selectmode_new; break; case 1: /* enable */ /* check we have something to do */ if ((em->selectmode & selectmode_new) != 0) { - return false; + only_update = true; + break; } em->selectmode |= selectmode_new; break; case 2: /* toggle */ /* can't disable this flag if its the only one set */ if (em->selectmode == selectmode_new) { - return false; + only_update = true; + break; } em->selectmode ^= selectmode_new; break; @@ -2285,10 +2368,30 @@ bool EDBM_selectmode_toggle( break; } + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(eval_ctx.view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); + if (em_iter != em) { + em_iter->selectmode = em->selectmode; + } + } + + if (only_update) { + MEM_SAFE_FREE(objects); + return false; + } + if (use_extend == 0 || em->selectmode == 0) { if (use_expand) { const short selmode_max = highest_order_bit_s(ts->selectmode); - EDBM_selectmode_convert(em, selmode_max, selectmode_new); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); + EDBM_selectmode_convert(em_iter, selmode_max, selectmode_new); + } } } @@ -2297,24 +2400,18 @@ bool EDBM_selectmode_toggle( if (use_extend == 0 || em->selectmode == 0) { em->selectmode = SCE_SELECT_VERTEX; } - ts->selectmode = em->selectmode; - EDBM_selectmode_set(em); ret = true; break; case SCE_SELECT_EDGE: if (use_extend == 0 || em->selectmode == 0) { em->selectmode = SCE_SELECT_EDGE; } - ts->selectmode = em->selectmode; - EDBM_selectmode_set(em); ret = true; break; case SCE_SELECT_FACE: if (use_extend == 0 || em->selectmode == 0) { em->selectmode = SCE_SELECT_FACE; } - ts->selectmode = em->selectmode; - EDBM_selectmode_set(em); ret = true; break; default: @@ -2323,10 +2420,18 @@ bool EDBM_selectmode_toggle( } if (ret == true) { - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + ts->selectmode = em->selectmode; + em = NULL; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + BMEditMesh *em_iter = BKE_editmesh_from_object(ob_iter); + EDBM_selectmode_set(em_iter); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, ob_iter->data); + } WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL); } + MEM_SAFE_FREE(objects); return ret; } @@ -2528,7 +2633,7 @@ static bool select_linked_delimit_test( * Gets the default from the operator fallback to own last-used value * (selected based on mode) */ -static int select_linked_delimit_default_from_op(wmOperator *op, int select_mode) +static int select_linked_delimit_default_from_op(wmOperator *op, const int select_mode) { static char delimit_last_store[2] = {0, BMO_DELIM_SEAM}; int delimit_last_index = (select_mode & (SCE_SELECT_VERTEX | SCE_SELECT_EDGE)) == 0; @@ -2594,17 +2699,27 @@ static void select_linked_delimit_end(BMEditMesh *em) static int edbm_select_linked_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + +#ifdef USE_LINKED_SELECT_DEFAULT_HACK + const int delimit_init = select_linked_delimit_default_from_op(op, scene->toolsettings->selectmode); +#else + const int delimit_init = RNA_enum_get(op->ptr, "delimit"); +#endif + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; BMIter iter; BMWalker walker; -#ifdef USE_LINKED_SELECT_DEFAULT_HACK - int delimit = select_linked_delimit_default_from_op(op, em->selectmode); -#else - int delimit = RNA_enum_get(op->ptr, "delimit"); -#endif + int delimit = delimit_init; select_linked_delimit_validate(bm, &delimit); @@ -2761,6 +2876,10 @@ static int edbm_select_linked_exec(bContext *C, wmOperator *op) WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + } /* objects */ + + MEM_SAFE_FREE(objects); + return OPERATOR_FINISHED; } @@ -2902,11 +3021,9 @@ static void edbm_select_linked_pick_ex(BMEditMesh *em, BMElem *ele, bool sel, in static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event) { - Object *obedit = CTX_data_edit_object(C); EvaluationContext eval_ctx; ViewContext vc; - BMEditMesh *em; - BMesh *bm; + Base *basact = NULL; BMVert *eve; BMEdge *eed; BMFace *efa; @@ -2923,25 +3040,39 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE /* setup view context for argument to callbacks */ CTX_data_eval_ctx(C, &eval_ctx); em_setup_viewcontext(C, &vc); - em = vc.em; - bm = em->bm; - if (bm->totedge == 0) { - return OPERATOR_CANCELLED; + { + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(eval_ctx.view_layer, &objects_len); + bool has_edges = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + ED_view3d_viewcontext_init_object(&vc, ob_iter); + if (vc.em->bm->totedge) { + has_edges = true; + } + } + MEM_SAFE_FREE(objects); + if (has_edges == false) { + return OPERATOR_CANCELLED; + } } vc.mval[0] = event->mval[0]; vc.mval[1] = event->mval[1]; /* return warning! */ - if (unified_findnearest(&eval_ctx, &vc, &eve, &eed, &efa) == 0) { - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + if (unified_findnearest(&eval_ctx, &vc, &basact, &eve, &eed, &efa) == 0) { + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, basact->object->data); return OPERATOR_CANCELLED; } + ED_view3d_viewcontext_init_object(&vc, basact->object); + BMEditMesh *em = vc.em; + BMesh *bm = em->bm; #ifdef USE_LINKED_SELECT_DEFAULT_HACK - int delimit = select_linked_delimit_default_from_op(op, em->selectmode); + int delimit = select_linked_delimit_default_from_op(op, vc.scene->toolsettings->selectmode); #else int delimit = RNA_enum_get(op->ptr, "delimit"); #endif @@ -2954,9 +3085,11 @@ static int edbm_select_linked_pick_invoke(bContext *C, wmOperator *op, const wmE BM_mesh_elem_index_ensure(bm, ele->head.htype); index = EDBM_elem_to_index_any(em, ele); + /* TODO(MULTI_EDIT), index doesn't know which object, + * index selections isn't very common. */ RNA_int_set(op->ptr, "index", index); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, basact->object->data); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_tools.c b/source/blender/editors/mesh/editmesh_tools.c index bf70cc3fa7e..78d563c64e9 100644 --- a/source/blender/editors/mesh/editmesh_tools.c +++ b/source/blender/editors/mesh/editmesh_tools.c @@ -93,7 +93,14 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); const int cuts = RNA_int_get(op->ptr, "number_cuts"); float smooth = RNA_float_get(op->ptr, "smoothness"); @@ -116,6 +123,9 @@ static int edbm_subdivide_exec(bContext *C, wmOperator *op) RNA_int_get(op->ptr, "seed")); EDBM_update_generic(em, true, true); + } + + MEM_SAFE_FREE(objects); return OPERATOR_FINISHED; } @@ -367,16 +377,22 @@ enum { MESH_DELETE_ONLY_FACE = 4, }; -static void edbm_report_delete_info(ReportList *reports, BMesh *bm, const int totelem[3]) +static void edbm_report_delete_info(ReportList *reports, const int totelem_old[3], const int totelem_new[3]) { BKE_reportf(reports, RPT_INFO, "Removed: %d vertices, %d edges, %d faces", - totelem[0] - bm->totvert, totelem[1] - bm->totedge, totelem[2] - bm->totface); + totelem_old[0] - totelem_new[0], totelem_old[1] - totelem_new[1], totelem_old[2] - totelem_new[2]); } static int edbm_delete_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; BMEditMesh *em = BKE_editmesh_from_object(obedit); const int type = RNA_enum_get(op->ptr, "type"); @@ -412,6 +428,8 @@ static int edbm_delete_exec(bContext *C, wmOperator *op) EDBM_update_generic(em, true, true); + } /* objects */ + return OPERATOR_FINISHED; } @@ -467,17 +485,25 @@ static bool bm_face_is_loose(BMFace *f) static int edbm_delete_loose_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMesh *bm = em->bm; - BMIter iter; + ViewLayer *view_layer = CTX_data_view_layer(C); + int totelem_old_sel[3]; + int totelem_old[3]; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + EDBM_mesh_stats_multi(objects, objects_len, totelem_old, totelem_old_sel); - const bool use_verts = (RNA_boolean_get(op->ptr, "use_verts") && bm->totvertsel); - const bool use_edges = (RNA_boolean_get(op->ptr, "use_edges") && bm->totedgesel); - const bool use_faces = (RNA_boolean_get(op->ptr, "use_faces") && bm->totfacesel); + const bool use_verts = (RNA_boolean_get(op->ptr, "use_verts") && totelem_old_sel[0]); + const bool use_edges = (RNA_boolean_get(op->ptr, "use_edges") && totelem_old_sel[1]); + const bool use_faces = (RNA_boolean_get(op->ptr, "use_faces") && totelem_old_sel[2]); - const int totelem[3] = {bm->totvert, bm->totedge, bm->totface}; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMIter iter; BM_mesh_elem_hflag_disable_all(bm, BM_VERT | BM_EDGE | BM_FACE, BM_ELEM_TAG, false); @@ -520,8 +546,14 @@ static int edbm_delete_loose_exec(bContext *C, wmOperator *op) EDBM_flag_disable_all(em, BM_ELEM_SELECT); EDBM_update_generic(em, true, true); + } + + int totelem_new[3]; + EDBM_mesh_stats_multi(objects, objects_len, totelem_new, NULL); - edbm_report_delete_info(op->reports, bm, totelem); + edbm_report_delete_info(op->reports, totelem_old, totelem_new); + + MEM_SAFE_FREE(objects); return OPERATOR_FINISHED; } @@ -4096,11 +4128,18 @@ void MESH_OT_poke(wmOperatorType *ot) static int edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); - BMOperator bmop; const int quad_method = RNA_enum_get(op->ptr, "quad_method"); const int ngon_method = RNA_enum_get(op->ptr, "ngon_method"); + ViewLayer *view_layer = CTX_data_view_layer(C); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMOperator bmop; BMOIter oiter; BMFace *f; @@ -4117,11 +4156,15 @@ static int edbm_quads_convert_to_tris_exec(bContext *C, wmOperator *op) EDBM_selectmode_flush(em); + // XXX, TODO +#if 0 if (!EDBM_op_finish(em, &bmop, op, true)) { return OPERATOR_CANCELLED; } +#endif EDBM_update_generic(em, true, true); + } return OPERATOR_FINISHED; } @@ -4155,7 +4198,22 @@ void MESH_OT_quads_convert_to_tris(wmOperatorType *ot) static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + bool is_face_pair; + + { + int totelem_sel[3]; + EDBM_mesh_stats_multi(objects, objects_len, NULL, totelem_sel); + is_face_pair = (totelem_sel[2] == 2); + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); bool do_seam, do_sharp, do_uvs, do_vcols, do_materials; float angle_face_threshold, angle_shape_threshold; @@ -4164,7 +4222,7 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) /* When joining exactly 2 faces, no limit. * this is useful for one off joins while editing. */ prop = RNA_struct_find_property(op->ptr, "face_threshold"); - if ((em->bm->totfacesel == 2) && + if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) { angle_face_threshold = DEG2RADF(180.0f); @@ -4174,7 +4232,7 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) } prop = RNA_struct_find_property(op->ptr, "shape_threshold"); - if ((em->bm->totfacesel == 2) && + if (is_face_pair && (RNA_property_is_set(op->ptr, prop) == false)) { angle_shape_threshold = DEG2RADF(180.0f); @@ -4197,10 +4255,11 @@ static int edbm_tris_convert_to_quads_exec(bContext *C, wmOperator *op) BM_ELEM_SELECT, angle_face_threshold, angle_shape_threshold, do_seam, do_sharp, do_uvs, do_vcols, do_materials)) { - return OPERATOR_CANCELLED; + continue; } EDBM_update_generic(em, true, true); + } return OPERATOR_FINISHED; } @@ -4727,11 +4786,28 @@ void MESH_OT_dissolve_limited(wmOperatorType *ot) static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op) { - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); + int totelem_old[3] = {0, 0, 0}; + int totelem_new[3] = {0, 0, 0}; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + totelem_old[0] += bm->totvert; + totelem_old[1] += bm->totedge; + totelem_old[2] += bm->totface; + } /* objects */ + const float thresh = RNA_float_get(op->ptr, "threshold"); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; - const int totelem[3] = {bm->totvert, bm->totedge, bm->totface}; if (!EDBM_op_callf( em, op, @@ -4746,7 +4822,12 @@ static int edbm_dissolve_degenerate_exec(bContext *C, wmOperator *op) EDBM_update_generic(em, true, true); - edbm_report_delete_info(op->reports, bm, totelem); + totelem_new[0] += bm->totvert; + totelem_new[1] += bm->totedge; + totelem_new[2] += bm->totface; + } + + edbm_report_delete_info(op->reports, totelem_old, totelem_new); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/mesh/editmesh_undo.c b/source/blender/editors/mesh/editmesh_undo.c index ab7e13117a0..4d4b7a098b0 100644 --- a/source/blender/editors/mesh/editmesh_undo.c +++ b/source/blender/editors/mesh/editmesh_undo.c @@ -24,9 +24,12 @@ #include "MEM_guardedalloc.h" +#include "CLG_log.h" + #include "DNA_mesh_types.h" #include "DNA_object_types.h" #include "DNA_key_types.h" +#include "DNA_layer_types.h" #include "BLI_listbase.h" #include "BLI_array_utils.h" @@ -35,6 +38,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_context.h" #include "BKE_key.h" +#include "BKE_layer.h" #include "BKE_mesh.h" #include "BKE_editmesh.h" #include "BKE_undo_system.h" @@ -44,6 +48,7 @@ #include "ED_object.h" #include "ED_mesh.h" #include "ED_util.h" +#include "ED_undo.h" #include "WM_types.h" #include "WM_api.h" @@ -69,6 +74,9 @@ # include "BLI_task.h" #endif +/** We only need this locally. */ +static CLG_LogRef LOG = {"ed.undo.mesh"}; + /* -------------------------------------------------------------------- */ /** \name Undo Conversion * \{ */ @@ -668,16 +676,21 @@ static Object *editmesh_object_from_context(bContext *C) /* -------------------------------------------------------------------- */ /** \name Implements ED Undo System + * + * \note This is similar for all edit-mode types. * \{ */ +typedef struct MeshUndoStep_Elem { + struct MeshUndoStep_Elem *next, *prev; + UndoRefID_Object obedit_ref; + UndoMesh data; +} MeshUndoStep_Elem; + typedef struct MeshUndoStep { UndoStep step; - /* Use for all ID lookups (can be NULL). */ struct UndoIDPtrMap *id_map; - - /* note: will split out into list for multi-object-editmode. */ - UndoRefID_Object obedit_ref; - UndoMesh data; + MeshUndoStep_Elem *elems; + uint elems_len; } MeshUndoStep; static bool mesh_undosys_poll(bContext *C) @@ -688,10 +701,24 @@ static bool mesh_undosys_poll(bContext *C) static bool mesh_undosys_step_encode(struct bContext *C, UndoStep *us_p) { MeshUndoStep *us = (MeshUndoStep *)us_p; - us->obedit_ref.ptr = editmesh_object_from_context(C); - Mesh *me = us->obedit_ref.ptr->data; - undomesh_from_editmesh(&us->data, me->edit_btmesh, me->key); - us->step.data_size = us->data.undo_size; + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); + us->elems_len = objects_len; + + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + MeshUndoStep_Elem *elem = &us->elems[i]; + + elem->obedit_ref.ptr = ob; + Mesh *me = elem->obedit_ref.ptr->data; + undomesh_from_editmesh(&elem->data, me->edit_btmesh, me->key); + us->step.data_size += elem->data.undo_size; + } + MEM_freeN(objects); return true; } @@ -702,18 +729,37 @@ static void mesh_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNU BLI_assert(mesh_undosys_poll(C)); MeshUndoStep *us = (MeshUndoStep *)us_p; - Object *obedit = us->obedit_ref.ptr; - Mesh *me = obedit->data; - BMEditMesh *em = me->edit_btmesh; - undomesh_to_editmesh(&us->data, em, obedit->data); - DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + + for (uint i = 0; i < us->elems_len; i++) { + MeshUndoStep_Elem *elem = &us->elems[i]; + Object *obedit = elem->obedit_ref.ptr; + Mesh *me = obedit->data; + if (me->edit_btmesh == NULL) { + /* Should never fail, may not crash but can give odd behavior. */ + CLOG_ERROR(&LOG, "name='%s', failed to enter edit-mode for object '%s', undo state invalid", + us_p->name, obedit->id.name); + continue; + } + BMEditMesh *em = me->edit_btmesh; + undomesh_to_editmesh(&elem->data, em, obedit->data); + DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + } + + /* The first element is always active */ + ED_undo_object_set_active_or_warn(CTX_data_view_layer(C), us->elems[0].obedit_ref.ptr, us_p->name, &LOG); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } static void mesh_undosys_step_free(UndoStep *us_p) { MeshUndoStep *us = (MeshUndoStep *)us_p; - undomesh_free_data(&us->data); + + for (uint i = 0; i < us->elems_len; i++) { + MeshUndoStep_Elem *elem = &us->elems[i]; + undomesh_free_data(&elem->data); + } + MEM_freeN(us->elems); if (us->id_map != NULL) { BKE_undosys_ID_map_destroy(us->id_map); @@ -724,7 +770,12 @@ static void mesh_undosys_foreach_ID_ref( UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data) { MeshUndoStep *us = (MeshUndoStep *)us_p; - foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref)); + + for (uint i = 0; i < us->elems_len; i++) { + MeshUndoStep_Elem *elem = &us->elems[i]; + foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref)); + } + if (us->id_map != NULL) { BKE_undosys_ID_map_foreach_ID_ref(us->id_map, foreach_ID_ref_fn, user_data); } diff --git a/source/blender/editors/mesh/meshtools.c b/source/blender/editors/mesh/meshtools.c index 531a26a66a8..dec13273417 100644 --- a/source/blender/editors/mesh/meshtools.c +++ b/source/blender/editors/mesh/meshtools.c @@ -60,6 +60,7 @@ #include "BKE_report.h" #include "BKE_editmesh.h" #include "BKE_multires.h" +#include "BKE_layer.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -1297,3 +1298,47 @@ MDeformVert *ED_mesh_active_dvert_get_only(Object *ob) return NULL; } } + +void EDBM_mesh_stats_multi( + struct Object **objects, const uint objects_len, + int totelem[3], int totelem_sel[3]) +{ + if (totelem) { + totelem[0] = 0; + totelem[1] = 0; + totelem[2] = 0; + } + if (totelem_sel) { + totelem_sel[0] = 0; + totelem_sel[1] = 0; + totelem_sel[2] = 0; + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + if (totelem) { + totelem[0] += bm->totvert; + totelem[1] += bm->totedge; + totelem[2] += bm->totface; + } + if (totelem_sel) { + totelem_sel[0] += bm->totvertsel; + totelem_sel[1] += bm->totedgesel; + totelem_sel[2] += bm->totfacesel; + } + } +} + + +void EDBM_mesh_elem_index_ensure_multi(Object **objects, const uint objects_len, const char htype) +{ + int elem_offset[4] = {0, 0, 0, 0}; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BM_mesh_elem_index_ensure_ex(bm, htype, elem_offset); + } +} diff --git a/source/blender/editors/metaball/CMakeLists.txt b/source/blender/editors/metaball/CMakeLists.txt index 73f80774716..b0ae3122727 100644 --- a/source/blender/editors/metaball/CMakeLists.txt +++ b/source/blender/editors/metaball/CMakeLists.txt @@ -27,6 +27,7 @@ set(INC ../../makesrna ../../render/extern/include ../../windowmanager + ../../../../intern/clog ../../../../intern/guardedalloc ) diff --git a/source/blender/editors/metaball/editmball_undo.c b/source/blender/editors/metaball/editmball_undo.c index cc461c0c365..7045025e227 100644 --- a/source/blender/editors/metaball/editmball_undo.c +++ b/source/blender/editors/metaball/editmball_undo.c @@ -27,6 +27,8 @@ #include "MEM_guardedalloc.h" +#include "CLG_log.h" + #include "BLI_utildefines.h" #include "BLI_listbase.h" #include "BLI_array_utils.h" @@ -36,17 +38,22 @@ #include "DNA_object_types.h" #include "BKE_context.h" +#include "BKE_layer.h" #include "BKE_undo_system.h" #include "DEG_depsgraph.h" #include "ED_object.h" #include "ED_mball.h" +#include "ED_undo.h" #include "ED_util.h" #include "WM_types.h" #include "WM_api.h" +/** We only need this locally. */ +static CLG_LogRef LOG = {"ed.undo.mball"}; + /* -------------------------------------------------------------------- */ /** \name Undo Conversion * \{ */ @@ -130,13 +137,19 @@ static Object *editmball_object_from_context(bContext *C) /* -------------------------------------------------------------------- */ /** \name Implements ED Undo System + * + * \note This is similar for all edit-mode types. * \{ */ -typedef struct MBallUndoStep { - UndoStep step; - /* note: will split out into list for multi-object-editmode. */ +typedef struct MBallUndoStep_Elem { UndoRefID_Object obedit_ref; UndoMBall data; +} MBallUndoStep_Elem; + +typedef struct MBallUndoStep { + UndoStep step; + MBallUndoStep_Elem *elems; + uint elems_len; } MBallUndoStep; static bool mball_undosys_poll(bContext *C) @@ -147,36 +160,74 @@ static bool mball_undosys_poll(bContext *C) static bool mball_undosys_step_encode(struct bContext *C, UndoStep *us_p) { MBallUndoStep *us = (MBallUndoStep *)us_p; - us->obedit_ref.ptr = editmball_object_from_context(C); - MetaBall *mb = us->obedit_ref.ptr->data; - editmball_from_undomball(&us->data, mb); - us->step.data_size = us->data.undo_size; + + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + us->elems = MEM_callocN(sizeof(*us->elems) * objects_len, __func__); + us->elems_len = objects_len; + + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + MBallUndoStep_Elem *elem = &us->elems[i]; + + elem->obedit_ref.ptr = ob; + MetaBall *mb = ob->data; + editmball_from_undomball(&elem->data, mb); + us->step.data_size += elem->data.undo_size; + } + MEM_freeN(objects); return true; } static void mball_undosys_step_decode(struct bContext *C, UndoStep *us_p, int UNUSED(dir)) { + /* TODO(campbell): undo_system: use low-level API to set mode. */ ED_object_mode_set(C, OB_MODE_EDIT); + BLI_assert(mball_undosys_poll(C)); MBallUndoStep *us = (MBallUndoStep *)us_p; - Object *obedit = us->obedit_ref.ptr; - MetaBall *mb = obedit->data; - undomball_to_editmball(&us->data, mb); - DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + + for (uint i = 0; i < us->elems_len; i++) { + MBallUndoStep_Elem *elem = &us->elems[i]; + Object *obedit = elem->obedit_ref.ptr; + MetaBall *mb = obedit->data; + if (mb->editelems == NULL) { + /* Should never fail, may not crash but can give odd behavior. */ + CLOG_ERROR(&LOG, "name='%s', failed to enter edit-mode for object '%s', undo state invalid", us_p->name, obedit->id.name); + continue; + } + undomball_to_editmball(&elem->data, mb); + DEG_id_tag_update(&obedit->id, OB_RECALC_DATA); + } + + /* The first element is always active */ + ED_undo_object_set_active_or_warn(CTX_data_view_layer(C), us->elems[0].obedit_ref.ptr, us_p->name, &LOG); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, NULL); } static void mball_undosys_step_free(UndoStep *us_p) { MBallUndoStep *us = (MBallUndoStep *)us_p; - undomball_free_data(&us->data); + + for (uint i = 0; i < us->elems_len; i++) { + MBallUndoStep_Elem *elem = &us->elems[i]; + undomball_free_data(&elem->data); + } + MEM_freeN(us->elems); } static void mball_undosys_foreach_ID_ref( UndoStep *us_p, UndoTypeForEachIDRefFn foreach_ID_ref_fn, void *user_data) { MBallUndoStep *us = (MBallUndoStep *)us_p; - foreach_ID_ref_fn(user_data, ((UndoRefID *)&us->obedit_ref)); + + for (uint i = 0; i < us->elems_len; i++) { + MBallUndoStep_Elem *elem = &us->elems[i]; + foreach_ID_ref_fn(user_data, ((UndoRefID *)&elem->obedit_ref)); + } } /* Export for ED_undo_sys. */ diff --git a/source/blender/editors/object/object_edit.c b/source/blender/editors/object/object_edit.c index f4066360805..a17b1c122ad 100644 --- a/source/blender/editors/object/object_edit.c +++ b/source/blender/editors/object/object_edit.c @@ -89,6 +89,7 @@ #include "BKE_report.h" #include "BKE_object.h" #include "BKE_workspace.h" +#include "BKE_layer.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_build.h" @@ -277,9 +278,6 @@ bool ED_object_editmode_load(Object *obedit) void ED_object_editmode_exit_ex(bContext *C, Scene *scene, Object *obedit, int flag) { BLI_assert(C || !(flag & EM_DO_UNDO)); - /* Note! only in exceptional cases should 'EM_DO_UNDO' NOT be in the flag */ - /* Note! if 'EM_FREEDATA' isn't in the flag, use ED_object_editmode_load directly */ - ViewLayer *view_layer = CTX_data_view_layer(C); const bool freedata = (flag & EM_FREEDATA) != 0; if (flag & EM_WAITCURSOR) waitcursor(1); @@ -287,8 +285,8 @@ void ED_object_editmode_exit_ex(bContext *C, Scene *scene, Object *obedit, int f if (ED_object_editmode_load_ex(G.main, obedit, freedata) == false) { /* in rare cases (background mode) its possible active object * is flagged for editmode, without 'obedit' being set [#35489] */ - if (UNLIKELY(view_layer->basact && (view_layer->basact->object->mode & OB_MODE_EDIT))) { - view_layer->basact->object->mode &= ~OB_MODE_EDIT; + if (UNLIKELY(obedit && obedit->mode & OB_MODE_EDIT)) { + obedit->mode &= ~OB_MODE_EDIT; } if (flag & EM_WAITCURSOR) waitcursor(0); return; @@ -315,15 +313,18 @@ void ED_object_editmode_exit_ex(bContext *C, Scene *scene, Object *obedit, int f if (flag & EM_DO_UNDO) ED_undo_push(C, "Editmode"); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene); + if (C != NULL) { + WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene); + } + else { + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene); + } + obedit->mode &= ~OB_MODE_EDIT; } if (flag & EM_WAITCURSOR) waitcursor(0); - - /* This way we ensure scene's obedit is copied into all CoW scenes. */ - DEG_id_tag_update(&scene->id, 0); } void ED_object_editmode_exit(bContext *C, int flag) @@ -333,25 +334,12 @@ void ED_object_editmode_exit(bContext *C, int flag) ED_object_editmode_exit_ex(C, scene, obedit, flag); } -void ED_object_editmode_enter(bContext *C, int flag) +void ED_object_editmode_enter_ex(Scene *scene, Object *ob, int flag) { - Scene *scene = CTX_data_scene(C); - ViewLayer *view_layer = CTX_data_view_layer(C); - Object *ob; bool ok = false; - if (ID_IS_LINKED(scene)) return; - - if ((flag & EM_IGNORE_LAYER) == 0) { - ob = CTX_data_active_object(C); /* active layer checked here for view3d */ - - if (ob == NULL) return; - } - else { - ob = view_layer->basact->object; - } - if (ELEM(NULL, ob, ob->data)) return; + if (ID_IS_LINKED(ob)) return; /* this checks actual object->data, for cases when other scenes have it in editmode context */ if (BKE_object_is_in_editmode(ob)) @@ -366,11 +354,6 @@ void ED_object_editmode_enter(bContext *C, int flag) ob->restore_mode = ob->mode; - /* note, when switching scenes the object can have editmode data but - * not be scene->obedit: bug 22954, this avoids calling self eternally */ - if ((ob->restore_mode & OB_MODE_EDIT) == 0) - ED_object_mode_toggle(C, ob->mode); - ob->mode = OB_MODE_EDIT; if (ob->type == OB_MESH) { @@ -387,7 +370,7 @@ void ED_object_editmode_enter(bContext *C, int flag) BKE_editmesh_tessface_calc(em); } - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_MESH, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MESH, NULL); } else if (ob->type == OB_ARMATURE) { bArmature *arm = ob->data; @@ -409,45 +392,64 @@ void ED_object_editmode_enter(bContext *C, int flag) /* to ensure all goes in restposition and without striding */ DEG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME); /* XXX: should this be OB_RECALC_DATA? */ - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_ARMATURE, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_ARMATURE, scene); } else if (ob->type == OB_FONT) { ok = 1; ED_curve_editfont_make(ob); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_TEXT, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_TEXT, scene); } else if (ob->type == OB_MBALL) { ok = 1; ED_mball_editmball_make(ob); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_MBALL, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_MBALL, scene); } else if (ob->type == OB_LATTICE) { ok = 1; BKE_editlattice_make(ob); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_LATTICE, scene); } else if (ob->type == OB_SURF || ob->type == OB_CURVE) { ok = 1; ED_curve_editnurb_make(ob); - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene); + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_EDITMODE_CURVE, scene); } if (ok) { DEG_id_tag_update(&ob->id, OB_RECALC_DATA); - /* This way we ensure scene's obedit is copied into all CoW scenes. */ - DEG_id_tag_update(&scene->id, 0); } else { - ob->mode &= ~OB_MODE_EDIT; - WM_event_add_notifier(C, NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene); + if ((flag & EM_NO_CONTEXT) == 0) { + ob->mode &= ~OB_MODE_EDIT; + } + WM_main_add_notifier(NC_SCENE | ND_MODE | NS_MODE_OBJECT, scene); } - if (flag & EM_DO_UNDO) ED_undo_push(C, "Enter Editmode"); if (flag & EM_WAITCURSOR) waitcursor(0); + BLI_assert((flag & EM_DO_UNDO) == 0); +} + +void ED_object_editmode_enter(bContext *C, int flag) +{ + Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *ob; + + if ((flag & EM_IGNORE_LAYER) == 0) { + ob = CTX_data_active_object(C); /* active layer checked here for view3d */ + } + else { + ob = view_layer->basact->object; + } + if (ob == NULL) return; + if (ID_IS_LINKED(ob)) return; + + ED_object_editmode_enter_ex(scene, ob, flag & ~EM_DO_UNDO); + if (flag & EM_DO_UNDO) ED_undo_push(C, "Enter Editmode"); } static int editmode_toggle_exec(bContext *C, wmOperator *op) @@ -455,18 +457,43 @@ static int editmode_toggle_exec(bContext *C, wmOperator *op) const int mode_flag = OB_MODE_EDIT; const bool is_mode_set = (CTX_data_edit_object(C) != NULL); Scene *scene = CTX_data_scene(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + Object *obact = OBACT(view_layer); if (!is_mode_set) { - Object *ob = CTX_data_active_object(C); - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + if (!ED_object_mode_compat_set(C, obact, mode_flag, op->reports)) { return OPERATOR_CANCELLED; } } - if (!is_mode_set) + if (!is_mode_set) { ED_object_editmode_enter(C, EM_WAITCURSOR); - else + if (obact->mode & mode_flag) { + FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob) + { + if ((ob != obact) && (ob->type == obact->type)) { + if (ob->flag & SELECT) { + ED_object_editmode_enter_ex(scene, ob, EM_WAITCURSOR | EM_NO_CONTEXT); + } + } + } + FOREACH_SELECTED_OBJECT_END; + } + } + else { ED_object_editmode_exit(C, EM_FREEDATA | EM_WAITCURSOR); /* had EM_DO_UNDO but op flag calls undo too [#24685] */ + if ((obact->mode & mode_flag) == 0) { + FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob) + { + if ((ob != obact) && (ob->type == obact->type)) { + if (ob->flag & SELECT) { + ED_object_editmode_exit_ex(NULL, scene, ob, EM_FREEDATA | EM_WAITCURSOR); + } + } + } + FOREACH_SELECTED_OBJECT_END; + } + } ED_space_image_uv_sculpt_update(CTX_wm_manager(C), scene); @@ -510,27 +537,60 @@ void OBJECT_OT_editmode_toggle(wmOperatorType *ot) static int posemode_exec(bContext *C, wmOperator *op) { Base *base = CTX_data_active_base(C); - Object *ob = base->object; + Object *obact = base->object; const int mode_flag = OB_MODE_POSE; - bool is_mode_set = (ob->mode & mode_flag) != 0; + bool is_mode_set = (obact->mode & mode_flag) != 0; if (!is_mode_set) { - if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { + if (!ED_object_mode_compat_set(C, obact, mode_flag, op->reports)) { return OPERATOR_CANCELLED; } } - if (ob->type == OB_ARMATURE) { - if (ob == CTX_data_edit_object(C)) { + if (obact->type == OB_ARMATURE) { + if (obact == CTX_data_edit_object(C)) { ED_object_editmode_exit(C, EM_FREEDATA | EM_DO_UNDO); is_mode_set = false; } if (is_mode_set) { - ED_object_posemode_exit(C, ob); + bool ok = ED_object_posemode_exit(C, obact); + if (ok) { + struct Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob) + { + if ((ob != obact) && + (ob->type == OB_ARMATURE) && + (ob->mode & mode_flag)) + { + if (ob->flag & SELECT) { + ED_object_posemode_exit_ex(bmain, ob); + } + } + } + FOREACH_SELECTED_OBJECT_END; + } } else { - ED_object_posemode_enter(C, ob); + bool ok = ED_object_posemode_enter(C, obact); + if (ok) { + struct Main *bmain = CTX_data_main(C); + ViewLayer *view_layer = CTX_data_view_layer(C); + FOREACH_SELECTED_OBJECT_BEGIN(view_layer, ob) + { + if ((ob != obact) && + (ob->type == OB_ARMATURE) && + (ob->mode == OB_MODE_OBJECT) && + (!ID_IS_LINKED(ob))) + { + if (ob->flag & SELECT) { + ED_object_posemode_enter_ex(bmain, ob); + } + } + } + FOREACH_SELECTED_OBJECT_END; + } } return OPERATOR_FINISHED; } diff --git a/source/blender/editors/object/object_modes.c b/source/blender/editors/object/object_modes.c index f074a56fb86..f61e597e69e 100644 --- a/source/blender/editors/object/object_modes.c +++ b/source/blender/editors/object/object_modes.c @@ -177,6 +177,9 @@ bool ED_object_mode_generic_enter( struct bContext *C, eObjectMode object_mode) { Object *ob = CTX_data_active_object(C); + if (ob == NULL) { + return (object_mode == OB_MODE_OBJECT); + } if (ob->mode == object_mode) { return true; } diff --git a/source/blender/editors/screen/screen_context.c b/source/blender/editors/screen/screen_context.c index 343e615f76b..c023c5d90bc 100644 --- a/source/blender/editors/screen/screen_context.c +++ b/source/blender/editors/screen/screen_context.c @@ -30,6 +30,8 @@ #include #include +#include "MEM_guardedalloc.h" + #include "DNA_object_types.h" #include "DNA_armature_types.h" #include "DNA_gpencil_types.h" @@ -42,7 +44,6 @@ #include "BLI_utildefines.h" - #include "BKE_context.h" #include "BKE_object.h" #include "BKE_action.h" @@ -209,6 +210,12 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult const bool editable_bones = CTX_data_equals(member, "editable_bones"); if (arm && arm->edbo) { + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + arm = ob->data; + /* Attention: X-Axis Mirroring is also handled here... */ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { /* first and foremost, bone must be visible and selected */ @@ -241,6 +248,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } } + } + MEM_freeN(objects); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } @@ -251,6 +261,12 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult const bool selected_editable_bones = CTX_data_equals(member, "selected_editable_bones"); if (arm && arm->edbo) { + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + for (uint i = 0; i < objects_len; i++) { + Object *ob = objects[i]; + arm = ob->data; + /* Attention: X-Axis Mirroring is also handled here... */ for (ebone = arm->edbo->first; ebone; ebone = ebone->next) { /* first and foremost, bone must be visible and selected */ @@ -283,6 +299,9 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult } } } + } + MEM_freeN(objects); + CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } @@ -293,12 +312,24 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bPoseChannel *pchan; if (obpose && obpose->pose && arm) { - for (pchan = obpose->pose->chanbase.first; pchan; pchan = pchan->next) { - /* ensure that PoseChannel is on visible layer and is not hidden in PoseMode */ - if (PBONE_VISIBLE(arm, pchan->bone)) { - CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); + if (obpose != obact) { + for (pchan = obpose->pose->chanbase.first; pchan; pchan = pchan->next) { + /* ensure that PoseChannel is on visible layer and is not hidden in PoseMode */ + if (PBONE_VISIBLE(arm, pchan->bone)) { + CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); + } } } + else if (obact->mode & OB_MODE_POSE) { + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, obact->mode, ob_iter) { + for (pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { + /* ensure that PoseChannel is on visible layer and is not hidden in PoseMode */ + if (PBONE_VISIBLE(arm, pchan->bone)) { + CTX_data_list_add(result, &ob_iter->id, &RNA_PoseBone, pchan); + } + } + } FOREACH_OBJECT_IN_MODE_END; + } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } @@ -309,13 +340,28 @@ int ed_screen_context(const bContext *C, const char *member, bContextDataResult bPoseChannel *pchan; if (obpose && obpose->pose && arm) { - for (pchan = obpose->pose->chanbase.first; pchan; pchan = pchan->next) { - /* ensure that PoseChannel is on visible layer and is not hidden in PoseMode */ - if (PBONE_VISIBLE(arm, pchan->bone)) { - if (pchan->bone->flag & BONE_SELECTED) - CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); + if (obpose != obact) { + /* TODO(de-duplicate!) */ + for (pchan = obpose->pose->chanbase.first; pchan; pchan = pchan->next) { + /* ensure that PoseChannel is on visible layer and is not hidden in PoseMode */ + if (PBONE_VISIBLE(arm, pchan->bone)) { + if (pchan->bone->flag & BONE_SELECTED) + CTX_data_list_add(result, &obpose->id, &RNA_PoseBone, pchan); + } } } + else if (obact->mode & OB_MODE_POSE) { + /* TODO(de-duplicate!) */ + FOREACH_OBJECT_IN_MODE_BEGIN (view_layer, OB_MODE_POSE, ob_iter) { + for (pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { + /* ensure that PoseChannel is on visible layer and is not hidden in PoseMode */ + if (PBONE_VISIBLE(arm, pchan->bone)) { + if (pchan->bone->flag & BONE_SELECTED) + CTX_data_list_add(result, &ob_iter->id, &RNA_PoseBone, pchan); + } + } + } FOREACH_OBJECT_IN_MODE_END; + } CTX_data_type_set(result, CTX_DATA_TYPE_COLLECTION); return 1; } diff --git a/source/blender/editors/space_view3d/space_view3d.c b/source/blender/editors/space_view3d/space_view3d.c index 9fb602c81d6..744553dedb7 100644 --- a/source/blender/editors/space_view3d/space_view3d.c +++ b/source/blender/editors/space_view3d/space_view3d.c @@ -918,6 +918,7 @@ static void view3d_main_region_listener( ob_data = OBEDIT_FROM_VIEW_LAYER(view_layer)->data; } if (ob_data) { + BLI_assert(OB_DATA_SUPPORT_ID(GS(ob_data->name))); /* TODO(sergey): Notifiers shouldn't really be doing DEG tags. */ DEG_id_tag_update(ob_data, DEG_TAG_SELECT_UPDATE); } diff --git a/source/blender/editors/space_view3d/view3d_select.c b/source/blender/editors/space_view3d/view3d_select.c index 6833dac558d..cdbcc3664a7 100644 --- a/source/blender/editors/space_view3d/view3d_select.c +++ b/source/blender/editors/space_view3d/view3d_select.c @@ -47,6 +47,7 @@ #include "MEM_guardedalloc.h" +#include "BLI_array.h" #include "BLI_math.h" #include "BLI_lasso_2d.h" #include "BLI_rect.h" @@ -126,6 +127,21 @@ void ED_view3d_viewcontext_init(bContext *C, ViewContext *vc) vc->obedit = CTX_data_edit_object(C); } +void ED_view3d_viewcontext_init_object(ViewContext *vc, Object *obact) +{ + vc->obact = obact; + if (vc->obedit) { + BLI_assert(BKE_object_is_in_editmode(obact)); + vc->obedit = obact; + /* previous selections are now invalid. */ + vc->v3d->flag |= V3D_INVALID_BACKBUF; + + if (vc->em) { + vc->em = BKE_editmesh_from_object(vc->obedit); + } + } +} + /* ********************** view3d_select: selection manipulations ********************* */ /* local prototypes */ @@ -398,6 +414,7 @@ static void do_lasso_select_objects( ViewContext *vc, const int mcords[][2], const short moves, const bool extend, const bool select) { + bool is_pose_mode = vc->obact ? (vc->obact->mode & OB_MODE_POSE) : false; Base *base; if (extend == false && select) @@ -411,7 +428,10 @@ static void do_lasso_select_objects( ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); } } - if (vc->obact == base->object && (base->object->mode & OB_MODE_POSE)) { + if (is_pose_mode && + ((vc->obact == base->object) || (base->flag & BASE_SELECTED)) && + (base->object->mode & OB_MODE_POSE)) + { do_lasso_select_pose(vc, base->object, mcords, moves, select); } } @@ -838,6 +858,10 @@ static void view3d_lasso_select( } } else { /* Edit Mode */ + + FOREACH_OBJECT_IN_MODE_BEGIN (eval_ctx.view_layer, ob->mode, ob_iter) { + ED_view3d_viewcontext_init_object(vc, ob_iter); + switch (vc->obedit->type) { case OB_MESH: do_lasso_select_mesh(&eval_ctx, vc, mcords, moves, extend, select); @@ -861,6 +885,8 @@ static void view3d_lasso_select( } WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc->obedit->data); + } + FOREACH_OBJECT_IN_MODE_END; } } @@ -1385,7 +1411,10 @@ static bool ed_object_select_pick( /* signal for view3d_opengl_select to skip editmode objects */ vc.obedit = NULL; } - + + /* In pose mode we don't want to mess with object selection. */ + const bool is_pose_mode = (vc.obact && vc.obact->mode & OB_MODE_POSE); + /* always start list from basact in wire mode */ startbase = FIRSTBASE(view_layer); if (BASACT(view_layer) && BASACT(view_layer)->next) startbase = BASACT(view_layer)->next; @@ -1504,7 +1533,9 @@ static bool ed_object_select_pick( } } } - else if (ED_armature_pose_select_pick_with_buffer(view_layer, basact, buffer, hits, extend, deselect, toggle, do_nearest)) { + else if (ED_armature_pose_select_pick_with_buffer( + view_layer, basact, buffer, hits, extend, deselect, toggle, do_nearest)) + { /* then bone is found */ /* we make the armature selected: @@ -1561,8 +1592,11 @@ static bool ed_object_select_pick( } } else { - deselectall_except(view_layer, basact); - ED_object_base_select(basact, BA_SELECT); + /* When enabled, this puts other objects out of multi pose-mode. */ + if (is_pose_mode == false) { + deselectall_except(view_layer, basact); + ED_object_base_select(basact, BA_SELECT); + } } if ((oldbasact != basact) && (is_obedit == false)) { @@ -1907,20 +1941,28 @@ static int do_armature_box_select( const struct EvaluationContext *eval_ctx, ViewContext *vc, const rcti *rect, bool select, bool extend) { - bArmature *arm = vc->obedit->data; int a; unsigned int buffer[MAXPICKBUF]; int hits; hits = view3d_opengl_select(eval_ctx, vc, buffer, MAXPICKBUF, rect, VIEW3D_SELECT_ALL); - + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(eval_ctx->view_layer, &objects_len); + /* clear flag we use to detect point was affected */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) - ebone->flag &= ~BONE_DONE; - - if (extend == false && select) - ED_armature_edit_deselect_all_visible(vc->obedit); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + bArmature *arm = obedit->data; + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + ebone->flag &= ~BONE_DONE; + } + } + + if (extend == false && select) { + ED_armature_edit_deselect_all_visible_multi(objects, objects_len); + } /* first we only check points inside the border */ for (a = 0; a < hits; a++) { @@ -1929,7 +1971,9 @@ static int do_armature_box_select( if ((index & 0xFFFF0000) == 0) { continue; } - EditBone *ebone = BLI_findlink(arm->edbo, index & ~(BONESEL_ANY)); + + EditBone *ebone; + ED_armature_object_and_ebone_from_select_buffer(objects, objects_len, index, &ebone); if ((select == false) || ((ebone->flag & BONE_UNSELECTABLE) == 0)) { if (index & BONESEL_TIP) { ebone->flag |= BONE_DONE; @@ -1947,10 +1991,14 @@ static int do_armature_box_select( } /* now we have to flush tag from parents... */ - for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { - if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { - if (ebone->parent->flag & BONE_DONE) { - ebone->flag |= BONE_DONE; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + bArmature *arm = obedit->data; + for (EditBone *ebone = arm->edbo->first; ebone; ebone = ebone->next) { + if (ebone->parent && (ebone->flag & BONE_CONNECTED)) { + if (ebone->parent->flag & BONE_DONE) { + ebone->flag |= BONE_DONE; + } } } } @@ -1960,7 +2008,8 @@ static int do_armature_box_select( int index = buffer[(4 * a) + 3]; if (index != -1) { if (index & BONESEL_BONE) { - EditBone *ebone = BLI_findlink(arm->edbo, index & ~(BONESEL_ANY)); + EditBone *ebone; + ED_armature_object_and_ebone_from_select_buffer(objects, objects_len, index, &ebone); if ((select == false) || ((ebone->flag & BONE_UNSELECTABLE) == 0)) { if (!(ebone->flag & BONE_DONE)) { if (select) { @@ -1974,9 +2023,15 @@ static int do_armature_box_select( } } } - - ED_armature_edit_sync_selection(arm->edbo); - + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + bArmature *arm = obedit->data; + ED_armature_edit_sync_selection(arm->edbo); + } + + MEM_freeN(objects); + return hits > 0 ? OPERATOR_FINISHED : OPERATOR_CANCELLED; } @@ -2009,31 +2064,31 @@ static int opengl_bone_select_buffer_cmp(const void *sel_a_p, const void *sel_b_ static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, bool select, bool extend) { EvaluationContext eval_ctx; - Bone *bone; - Object *ob = vc->obact; unsigned int *vbuffer = NULL; /* selection buffer */ - unsigned int *col; /* color in buffer */ int bone_only; - int bone_selected = 0; int totobj = MAXPICKBUF; /* XXX solve later */ int hits; CTX_data_eval_ctx(C, &eval_ctx); - if ((ob) && (ob->mode & OB_MODE_POSE)) + if (vc->obact && (vc->obact->mode & OB_MODE_POSE)) bone_only = 1; else bone_only = 0; if (extend == false && select) { if (bone_only) { - CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) - { - if ((select == false) || ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)) { - pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + FOREACH_OBJECT_IN_MODE_BEGIN (eval_ctx.view_layer, OB_MODE_POSE, ob_iter) { + bArmature *arm = ob_iter->data; + for (bPoseChannel *pchan = ob_iter->pose->chanbase.first; pchan; pchan = pchan->next) { + if (PBONE_VISIBLE(arm, pchan->bone)) { + if ((select == false) || ((pchan->bone->flag & BONE_UNSELECTABLE) == 0)) { + pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL); + } + } } } - CTX_DATA_END; + FOREACH_OBJECT_IN_MODE_END; } else { object_deselect_all_visible(vc->view_layer); @@ -2053,60 +2108,77 @@ static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, b */ if (hits > 0) { /* no need to loop if there's no hit */ - Base *base; - col = vbuffer + 3; /* The draw order doesn't always match the order we populate the engine, see: T51695. */ qsort(vbuffer, hits, sizeof(uint[4]), opengl_bone_select_buffer_cmp); - /* - * Even though 'DRW_draw_select_loop' uses 'DEG_OBJECT_ITER_BEGIN', - * we can be sure the order remains the same between both. - */ - for (base = vc->view_layer->object_bases.first; base && hits; base = base->next) { + Base **bases = NULL; + BLI_array_declare(bases); + + for (Base *base = vc->view_layer->object_bases.first; base && hits; base = base->next) { if (BASE_SELECTABLE(base)) { - while (base->object->select_color == (*col & 0xFFFF)) { /* we got an object */ - if (*col & 0xFFFF0000) { /* we got a bone */ - bone = ED_armature_bone_find_index(base->object, *col & ~(BONESEL_ANY)); - if (bone) { - if (select) { - if ((bone->flag & BONE_UNSELECTABLE) == 0) { - bone->flag |= BONE_SELECTED; - bone_selected = 1; - } - } - else { - bArmature *arm = base->object->data; - bone->flag &= ~BONE_SELECTED; - if (arm->act_bone == bone) - arm->act_bone = NULL; - } + if ((base->object->select_color & 0x0000FFFF) != 0) { + BLI_array_append(bases, base); + } + } + } + + for (const uint *col = vbuffer + 3, *col_end = col + (hits * 4); col < col_end; col += 4) { + Bone *bone; + Base *base = ED_armature_base_and_bone_from_select_buffer(bases, BLI_array_len(bases), *col, &bone); + + if (base == NULL) { + continue; + } + /* Loop over contiguous bone hits for 'base'. */ + bool bone_selected = false; + for (; col != col_end; col += 4) { + /* should never fail */ + if (bone != NULL) { + if (select) { + if ((bone->flag & BONE_UNSELECTABLE) == 0) { + bone->flag |= BONE_SELECTED; + bone_selected = true; } } - else if (!bone_only) { - ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); + else { + bArmature *arm = base->object->data; + bone->flag &= ~BONE_SELECTED; + if (arm->act_bone == bone) + arm->act_bone = NULL; } - - col += 4; /* next color */ - hits--; - if (hits == 0) break; + } + else if (!bone_only) { + ED_object_base_select(base, select ? BA_SELECT : BA_DESELECT); + } + + /* Select the next bone if we're not switching bases. */ + if (col + 4 != col_end) { + if ((base->object->select_color & 0x0000FFFF) != (col[4] & 0x0000FFFF)) { + break; + } + const uint hit_bone = (col[4] & ~BONESEL_ANY) >> 16; + bPoseChannel *pchan = BLI_findlink(&base->object->pose->chanbase, hit_bone);; + bone = pchan ? pchan->bone : NULL; } } - + if (bone_selected) { if (base->object && (base->object->type == OB_ARMATURE)) { bArmature *arm = base->object->data; - + WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object); - - if (arm && (arm->flag & ARM_HAS_VIZ_DEPS)) { + + if (vc->obact && arm && (arm->flag & ARM_HAS_VIZ_DEPS)) { /* mask modifier ('armature' mode), etc. */ - DEG_id_tag_update(&ob->id, OB_RECALC_DATA); + DEG_id_tag_update(&vc->obact->id, OB_RECALC_DATA); } } } } - + + MEM_freeN(bases); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene); } MEM_freeN(vbuffer); @@ -2135,36 +2207,39 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op) WM_operator_properties_border_to_rcti(op, &rect); if (vc.obedit) { + + FOREACH_OBJECT_IN_MODE_BEGIN (eval_ctx.view_layer, vc.obedit->mode, ob_iter) { + ED_view3d_viewcontext_init_object(&vc, ob_iter); + switch (vc.obedit->type) { case OB_MESH: vc.em = BKE_editmesh_from_object(vc.obedit); - ret = do_mesh_box_select(&eval_ctx, &vc, &rect, select, extend); -// if (EM_texFaceCheck()) + ret |= do_mesh_box_select(&eval_ctx, &vc, &rect, select, extend); if (ret & OPERATOR_FINISHED) { WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); } break; case OB_CURVE: case OB_SURF: - ret = do_nurbs_box_select(&vc, &rect, select, extend); + ret |= do_nurbs_box_select(&vc, &rect, select, extend); if (ret & OPERATOR_FINISHED) { WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); } break; case OB_MBALL: - ret = do_meta_box_select(&eval_ctx, &vc, &rect, select, extend); + ret |= do_meta_box_select(&eval_ctx, &vc, &rect, select, extend); if (ret & OPERATOR_FINISHED) { WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); } break; case OB_ARMATURE: - ret = do_armature_box_select(&eval_ctx, &vc, &rect, select, extend); + ret |= do_armature_box_select(&eval_ctx, &vc, &rect, select, extend); if (ret & OPERATOR_FINISHED) { WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit); } break; case OB_LATTICE: - ret = do_lattice_box_select(&vc, &rect, select, extend); + ret |= do_lattice_box_select(&vc, &rect, select, extend); if (ret & OPERATOR_FINISHED) { WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data); } @@ -2173,25 +2248,34 @@ static int view3d_borderselect_exec(bContext *C, wmOperator *op) assert(!"border select on incorrect object type"); break; } + } + FOREACH_OBJECT_IN_MODE_END; } else { /* no editmode, unified for bones and objects */ if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) { - ret = ED_sculpt_mask_box_select(C, &vc, &rect, select, extend); + ret |= ED_sculpt_mask_box_select(C, &vc, &rect, select, extend); } else if (vc.obact && BKE_paint_select_face_test(vc.obact)) { - ret = do_paintface_box_select(&eval_ctx, &vc, &rect, select, extend); + ret |= do_paintface_box_select(&eval_ctx, &vc, &rect, select, extend); } else if (vc.obact && BKE_paint_select_vert_test(vc.obact)) { - ret = do_paintvert_box_select(&eval_ctx, &vc, &rect, select, extend); + ret |= do_paintvert_box_select(&eval_ctx, &vc, &rect, select, extend); } else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) { - ret = PE_border_select(C, &rect, select, extend); + ret |= PE_border_select(C, &rect, select, extend); } else { /* object mode with none active */ - ret = do_object_pose_box_select(C, &vc, &rect, select, extend); + ret |= do_object_pose_box_select(C, &vc, &rect, select, extend); } } + if (ret & OPERATOR_FINISHED) { + ret = OPERATOR_FINISHED; + } + else { + ret = OPERATOR_CANCELLED; + } + return ret; } @@ -2832,23 +2916,30 @@ static bool object_circle_select(ViewContext *vc, const bool select, const int m /* not a real operator, only for circle test */ static int view3d_circle_select_exec(bContext *C, wmOperator *op) { - Scene *scene = CTX_data_scene(C); - Object *obact = CTX_data_active_object(C); + ViewContext vc; + EvaluationContext eval_ctx; + CTX_data_eval_ctx(C, &eval_ctx); const int radius = RNA_int_get(op->ptr, "radius"); const bool select = !RNA_boolean_get(op->ptr, "deselect"); const int mval[2] = {RNA_int_get(op->ptr, "x"), RNA_int_get(op->ptr, "y")}; - if (CTX_data_edit_object(C) || BKE_paint_select_elem_test(obact) || + + ED_view3d_viewcontext_init(C, &vc); + + Object *obact = vc.obact; + Object *obedit = vc.obedit; + + if (obedit || BKE_paint_select_elem_test(obact) || (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE))) ) { - EvaluationContext eval_ctx; - ViewContext vc; - view3d_operator_needs_opengl(C); - - CTX_data_eval_ctx(C, &eval_ctx); - ED_view3d_viewcontext_init(C, &vc); + + FOREACH_OBJECT_IN_MODE_BEGIN (eval_ctx.view_layer, obact->mode, ob_iter) { + ED_view3d_viewcontext_init_object(&vc, ob_iter); + + obact = vc.obact; + obedit = vc.obedit; if (CTX_data_edit_object(C)) { obedit_circle_select(&eval_ctx, &vc, select, mval, (float)radius); @@ -2862,20 +2953,21 @@ static int view3d_circle_select_exec(bContext *C, wmOperator *op) paint_vertsel_circle_select(&eval_ctx, &vc, select, mval, (float)radius); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data); } - else if (obact->mode & OB_MODE_POSE) + else if (obact->mode & OB_MODE_POSE) { pose_circle_select(&vc, select, mval, (float)radius); - else + } + else { return PE_circle_select(C, select, mval, (float)radius); + } + } + FOREACH_OBJECT_IN_MODE_END; } else if (obact && obact->mode & OB_MODE_SCULPT) { return OPERATOR_CANCELLED; } else { - ViewContext vc; - ED_view3d_viewcontext_init(C, &vc); - if (object_circle_select(&vc, select, mval, (float)radius)) { - WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene); + WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc.scene); } } diff --git a/source/blender/editors/transform/transform.c b/source/blender/editors/transform/transform.c index d4c5f6053b4..8c4aeb1d136 100644 --- a/source/blender/editors/transform/transform.c +++ b/source/blender/editors/transform/transform.c @@ -110,7 +110,7 @@ static void drawEdgeSlide(TransInfo *t); static void drawVertSlide(TransInfo *t); static void postInputRotation(TransInfo *t, float values[3]); -static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around); +static void ElementRotation(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around); static void initSnapSpatial(TransInfo *t, float r_snap[3]); @@ -211,7 +211,8 @@ static bool transdata_check_local_center(TransInfo *t, short around) { return ((around == V3D_AROUND_LOCAL_ORIGINS) && ( (t->flag & (T_OBJECT | T_POSE)) || - (t->obedit && ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) || + /* implicit: (t->flag & T_EDIT) */ + (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE)) || (t->spacetype == SPACE_IPO) || (t->options & (CTX_MOVIECLIP | CTX_MASK | CTX_PAINT_CURVE))) ); @@ -220,7 +221,7 @@ static bool transdata_check_local_center(TransInfo *t, short around) bool transdata_check_local_islands(TransInfo *t, short around) { return ((around == V3D_AROUND_LOCAL_ORIGINS) && ( - (t->obedit && ELEM(t->obedit->type, OB_MESH)))); + (ELEM(t->obedit_type, OB_MESH)))); } /* ************************** SPACE DEPENDANT CODE **************************** */ @@ -245,6 +246,7 @@ void setTransformViewMatrices(TransInfo *t) } calculateCenter2D(t); + calculateCenterLocal(t, t->center_global); } void setTransformViewAspect(TransInfo *t, float r_aspect[3]) @@ -619,8 +621,12 @@ static void viewRedrawForce(const bContext *C, TransInfo *t) else { // XXX how to deal with lock? SpaceImage *sima = (SpaceImage *)t->sa->spacedata.first; - if (sima->lock) WM_event_add_notifier(C, NC_GEOM | ND_DATA, t->obedit->data); - else ED_area_tag_redraw(t->sa); + if (sima->lock) { + WM_event_add_notifier(C, NC_GEOM | ND_DATA, OBEDIT_FROM_VIEW_LAYER(t->view_layer)->data); + } + else { + ED_area_tag_redraw(t->sa); + } } } else if (t->spacetype == SPACE_CLIP) { @@ -1024,7 +1030,7 @@ int transformEvent(TransInfo *t, const wmEvent *event) handled = true; } else { - if (t->obedit && t->obedit->type == OB_MESH) { + if (t->obedit_type == OB_MESH) { if ((t->mode == TFM_TRANSLATION) && (t->spacetype == SPACE_VIEW3D)) { restoreTransObjects(t); resetTransModal(t); @@ -1570,7 +1576,7 @@ bool calculateTransformCenter(bContext *C, int centerMode, float cent3d[3], floa t->around = centerMode; // override userdefined mode - if (t->total == 0) { + if (t->data_len_all == 0) { success = false; } else { @@ -1971,7 +1977,7 @@ void saveTransform(bContext *C, TransInfo *t, wmOperator *op) ts->proportional_fcurve = proportional; else if (t->spacetype == SPACE_ACTION) ts->proportional_action = proportional; - else if (t->obedit) + else if (t->obedit_type != -1) ts->proportional = proportional; else if (t->options & CTX_MASK) ts->proportional_mask = (proportional != PROP_EDIT_OFF); @@ -2161,7 +2167,7 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve createTransData(C, t); // make TransData structs from selection - if (t->total == 0) { + if (t->data_len_all == 0) { postTrans(C, t); return 0; } @@ -2265,7 +2271,9 @@ bool initTransform(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve break; case TFM_BONESIZE: { /* used for both B-Bone width (bonesize) as for deform-dist (envelope) */ - bArmature *arm = t->poseobj->data; + /* Note: we have to pick one, use the active object. */ + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t); + bArmature *arm = tc->poseobj->data; if (arm->drawtype == ARM_ENVELOPE) { initBoneEnvelope(t); t->mode = TFM_BONE_ENVELOPE_DIST; @@ -2870,6 +2878,7 @@ static void constraintSizeLim(TransInfo *t, TransData *td) * \{ */ struct BendCustomData { + /* All values are in global space. */ float warp_sta[3]; float warp_end[3]; @@ -2910,9 +2919,9 @@ static void initBend(TransInfo *t) //copy_v3_v3(t->center, ED_view3d_cursor3d_get(t->scene, t->view)); if ((t->flag & T_OVERRIDE_CENTER) == 0) { - calculateCenterCursor(t, t->center); + calculateCenterCursor(t, t->center_global); } - calculateCenterGlobal(t, t->center, t->center_global); + calculateCenterLocal(t, t->center_global); t->val = 0.0f; @@ -2923,10 +2932,6 @@ static void initBend(TransInfo *t) ED_view3d_win_to_3d(t->sa->spacedata.first, t->ar, curs, mval_fl, data->warp_end); copy_v3_v3(data->warp_nor, t->viewinv[2]); - if (t->flag & T_EDIT) { - sub_v3_v3(data->warp_sta, t->obedit->obmat[3]); - sub_v3_v3(data->warp_end, t->obedit->obmat[3]); - } normalize_v3(data->warp_nor); /* tangent */ @@ -2953,10 +2958,9 @@ static eRedrawFlag handleEventBend(TransInfo *UNUSED(t), const wmEvent *event) static void Bend(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float vec[3]; - float pivot[3]; - float warp_end_radius[3]; + float pivot_global[3]; + float warp_end_radius_global[3]; int i; char str[UI_MAX_DRAW_STR]; const struct BendCustomData *data = t->custom.mode.data; @@ -3011,20 +3015,42 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) values.scale *= data->warp_init_dist; /* calc 'data->warp_end' from 'data->warp_end_init' */ - copy_v3_v3(warp_end_radius, data->warp_end); - dist_ensure_v3_v3fl(warp_end_radius, data->warp_sta, values.scale); + copy_v3_v3(warp_end_radius_global, data->warp_end); + dist_ensure_v3_v3fl(warp_end_radius_global, data->warp_sta, values.scale); /* done */ /* calculate pivot */ - copy_v3_v3(pivot, data->warp_sta); + copy_v3_v3(pivot_global, data->warp_sta); if (values.angle > 0.0f) { - madd_v3_v3fl(pivot, data->warp_tan, -values.scale * shell_angle_to_dist((float)M_PI_2 - values.angle)); + madd_v3_v3fl(pivot_global, data->warp_tan, -values.scale * shell_angle_to_dist((float)M_PI_2 - values.angle)); + } + else { + madd_v3_v3fl(pivot_global, data->warp_tan, +values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle)); + } + + /* TODO(campbell): xform, compensate object center. */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + + float warp_sta_local[3]; + float warp_end_local[3]; + float warp_end_radius_local[3]; + float pivot_local[3]; + + if (t->flag & T_EDIT) { + sub_v3_v3v3(warp_sta_local, data->warp_sta, tc->obedit->obmat[3]); + sub_v3_v3v3(warp_end_local, data->warp_end, tc->obedit->obmat[3]); + sub_v3_v3v3(warp_end_radius_local, warp_end_radius_global, tc->obedit->obmat[3]); + sub_v3_v3v3(pivot_local, pivot_global, tc->obedit->obmat[3]); } else { - madd_v3_v3fl(pivot, data->warp_tan, +values.scale * shell_angle_to_dist((float)M_PI_2 + values.angle)); + copy_v3_v3(warp_sta_local, data->warp_sta); + copy_v3_v3(warp_end_local, data->warp_end); + copy_v3_v3(warp_end_radius_local, warp_end_radius_global); + copy_v3_v3(pivot_local, pivot_global); } - for (i = 0; i < t->total; i++, td++) { + for (i = 0; i < tc->data_len; i++, td++) { float mat[3][3]; float delta[3]; float fac, fac_scaled; @@ -3043,34 +3069,35 @@ static void Bend(TransInfo *t, const int UNUSED(mval[2])) copy_v3_v3(vec, td->iloc); mul_m3_v3(td->mtx, vec); - fac = line_point_factor_v3(vec, data->warp_sta, warp_end_radius); + fac = line_point_factor_v3(vec, warp_sta_local, warp_end_radius_local); if (is_clamp) { CLAMP(fac, 0.0f, 1.0f); } fac_scaled = fac * td->factor; axis_angle_normalized_to_mat3(mat, data->warp_nor, values.angle * fac_scaled); - interp_v3_v3v3(delta, data->warp_sta, warp_end_radius, fac_scaled); - sub_v3_v3(delta, data->warp_sta); + interp_v3_v3v3(delta, warp_sta_local, warp_end_radius_local, fac_scaled); + sub_v3_v3(delta, warp_sta_local); /* delta is subtracted, rotation adds back this offset */ sub_v3_v3(vec, delta); - sub_v3_v3(vec, pivot); + sub_v3_v3(vec, pivot_local); mul_m3_v3(mat, vec); - add_v3_v3(vec, pivot); + add_v3_v3(vec, pivot_local); mul_m3_v3(td->smtx, vec); /* rotation */ if ((t->flag & T_POINTS) == 0) { - ElementRotation(t, td, mat, V3D_AROUND_LOCAL_ORIGINS); + ElementRotation(t, tc, td, mat, V3D_AROUND_LOCAL_ORIGINS); } /* location */ copy_v3_v3(td->loc, vec); } - + } + recalcData(t); ED_area_headerprint(t->sa, str); @@ -3141,7 +3168,6 @@ static eRedrawFlag handleEventShear(TransInfo *t, const wmEvent *event) static void applyShear(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float vec[3]; float smat[3][3], tmat[3][3], totmat[3][3], persmat[3][3], persinv[3][3]; float value; @@ -3184,7 +3210,9 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) mul_m3_m3m3(tmat, smat, persmat); mul_m3_m3m3(totmat, persinv, tmat); - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { const float *center, *co; if (td->flag & TD_NOACTION) @@ -3192,8 +3220,8 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) if (td->flag & TD_SKIP) continue; - - if (t->obedit) { + + if (t->flag & T_EDIT) { float mat3[3][3]; mul_m3_m3m3(mat3, totmat, td->mtx); mul_m3_m3m3(tmat, td->smtx, mat3); @@ -3207,7 +3235,7 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) co = td->loc; } else { - center = t->center; + center = tc->center_local; co = td->center; } @@ -3222,7 +3250,8 @@ static void applyShear(TransInfo *t, const int UNUSED(mval[2])) add_v3_v3v3(td->loc, td->iloc, vec); } - + } + recalcData(t); ED_area_headerprint(t->sa, str); @@ -3248,7 +3277,7 @@ static void initResize(TransInfo *t) t->num.val_flag[1] |= NUM_NULL_ONE; t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; - if (!t->obedit) { + if ((t->flag & T_EDIT) == 0) { t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; @@ -3332,7 +3361,7 @@ static void TransMat3ToSize(float mat[3][3], float smat[3][3], float size[3]) if (dot_v3v3(rmat[2], smat[2]) < 0.0f) size[2] = -size[2]; } -static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) +static void ElementResize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]) { float tmat[3][3], smat[3][3], center[3]; float vec[3]; @@ -3346,7 +3375,7 @@ static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) } if (t->con.applySize) { - t->con.applySize(t, td, tmat); + t->con.applySize(t, tc, td, tmat); } /* local constraint shouldn't alter center */ @@ -3358,11 +3387,11 @@ static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) copy_v3_v3(center, td->center); } else { - copy_v3_v3(center, t->center); + copy_v3_v3(center, tc->center_local); } } else { - copy_v3_v3(center, t->center); + copy_v3_v3(center, tc->center_local); } if (td->ext) { @@ -3435,7 +3464,6 @@ static void ElementResize(TransInfo *t, TransData *td, float mat[3][3]) static void applyResize(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td; float mat[3][3]; int i; char str[UI_MAX_DRAW_STR]; @@ -3460,32 +3488,38 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, t->values); if (t->con.applySize) { - t->con.applySize(t, NULL, mat); + t->con.applySize(t, NULL, NULL, mat); } copy_m3_m3(t->mat, mat); // used in manipulator headerResize(t, t->values, str); - - for (i = 0, td = t->data; i < t->total; i++, td++) { + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; - - ElementResize(t, td, mat); + + ElementResize(t, tc, td, mat); } - + } + /* evil hack - redo resize if cliping needed */ if (t->flag & T_CLIP_UV && clipUVTransform(t, t->values, 1)) { size_to_mat3(mat, t->values); - + if (t->con.applySize) - t->con.applySize(t, NULL, mat); - - for (i = 0, td = t->data; i < t->total; i++, td++) - ElementResize(t, td, mat); + t->con.applySize(t, NULL, NULL, mat); + + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) + ElementResize(t, tc, td, mat); /* In proportional edit it can happen that */ /* vertices in the radius of the brush end */ @@ -3494,6 +3528,7 @@ static void applyResize(TransInfo *t, const int UNUSED(mval[2])) if (t->flag & T_PROP_EDIT_ALL) { clipUVData(t); } + } } recalcData(t); @@ -3521,7 +3556,7 @@ static void initSkinResize(TransInfo *t) t->num.val_flag[1] |= NUM_NULL_ONE; t->num.val_flag[2] |= NUM_NULL_ONE; t->num.flag |= NUM_AFFECT_ALL; - if (!t->obedit) { + if ((t->flag & T_EDIT) == 0) { t->flag |= T_NO_ZERO; #ifdef USE_NUM_NO_ZERO t->num.val_flag[0] |= NUM_NO_ZERO; @@ -3545,7 +3580,6 @@ static void initSkinResize(TransInfo *t) static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td; float size[3], mat[3][3]; int i; char str[UI_MAX_DRAW_STR]; @@ -3569,8 +3603,10 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, size); headerResize(t, size, str); - - for (i = 0, td = t->data; i < t->total; i++, td++) { + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { float tmat[3][3], smat[3][3]; float fsize[3]; @@ -3589,14 +3625,15 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) } if (t->con.applySize) { - t->con.applySize(t, NULL, tmat); + t->con.applySize(t, NULL, NULL, tmat); } mat3_to_size(fsize, tmat); td->val[0] = td->ext->isize[0] * (1 + (fsize[0] - 1) * td->factor); td->val[1] = td->ext->isize[1] * (1 + (fsize[1] - 1) * td->factor); } - + } + recalcData(t); ED_area_headerprint(t->sa, str); @@ -3612,7 +3649,6 @@ static void applySkinResize(TransInfo *t, const int UNUSED(mval[2])) static void initToSphere(TransInfo *t) { - TransData *td = t->data; int i; t->mode = TFM_TOSPHERE; @@ -3632,13 +3668,16 @@ static void initToSphere(TransInfo *t) t->num.val_flag[0] |= NUM_NULL_ONE | NUM_NO_NEGATIVE; t->flag |= T_NO_CONSTRAINT; - + // Calculate average radius - for (i = 0; i < t->total; i++, td++) { - t->val += len_v3v3(t->center, td->iloc); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + t->val += len_v3v3(tc->center_local, td->iloc); } - - t->val /= (float)t->total; + } + + t->val /= (float)t->data_len_all; } static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) @@ -3647,8 +3686,7 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) float ratio, radius; int i; char str[UI_MAX_DRAW_STR]; - TransData *td = t->data; - + ratio = t->values[0]; snapGridIncrement(t, &ratio); @@ -3671,28 +3709,29 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) /* default header print */ BLI_snprintf(str, sizeof(str), IFACE_("To Sphere: %.4f %s"), ratio, t->proptext); } - - - for (i = 0; i < t->total; i++, td++) { + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { float tratio; if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; - - sub_v3_v3v3(vec, td->iloc, t->center); - + + sub_v3_v3v3(vec, td->iloc, tc->center_local); + radius = normalize_v3(vec); tratio = ratio * td->factor; mul_v3_fl(vec, radius * (1.0f - tratio) + t->val * tratio); - - add_v3_v3v3(td->loc, t->center, vec); + + add_v3_v3v3(td->loc, tc->center_local, vec); } - - + } + recalcData(t); ED_area_headerprint(t->sa, str); @@ -3709,7 +3748,7 @@ static void applyToSphere(TransInfo *t, const int UNUSED(mval[2])) static void postInputRotation(TransInfo *t, float values[3]) { if ((t->con.mode & CON_APPLY) && t->con.applyRot) { - t->con.applyRot(t, NULL, t->axis, values); + t->con.applyRot(t, NULL, NULL, t->axis, values); } } @@ -3754,7 +3793,7 @@ static void initRotation(TransInfo *t) * * Protected axis and other transform settings are taken into account. */ -static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], const float *center) +static void ElementRotation_ex(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const float *center) { float vec[3], totmat[3][3], smat[3][3]; float eul[3], fmat[3][3], quat[4]; @@ -3801,7 +3840,7 @@ static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], con float pmtx[3][3], imtx[3][3]; // Extract and invert armature object matrix - copy_m3_m4(pmtx, t->poseobj->obmat); + copy_m3_m4(pmtx, tc->poseobj->obmat); invert_m3_m3(imtx, pmtx); if ((td->flag & TD_NO_LOC) == 0) { @@ -3967,7 +4006,7 @@ static void ElementRotation_ex(TransInfo *t, TransData *td, float mat[3][3], con } } -static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const short around) +static void ElementRotation(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3], const short around) { const float *center; @@ -3976,22 +4015,23 @@ static void ElementRotation(TransInfo *t, TransData *td, float mat[3][3], const center = td->center; } else { - center = t->center; + center = tc->center_local; } - ElementRotation_ex(t, td, mat, center); + ElementRotation_ex(t, tc, td, mat, center); } static void applyRotationValue(TransInfo *t, float angle, float axis[3]) { - TransData *td = t->data; float mat[3][3]; int i; axis_angle_normalized_to_mat3(mat, axis, angle); - - for (i = 0; i < t->total; i++, td++) { - + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + if (td->flag & TD_NOACTION) break; @@ -3999,14 +4039,15 @@ static void applyRotationValue(TransInfo *t, float angle, float axis[3]) continue; if (t->con.applyRot) { - t->con.applyRot(t, td, axis, NULL); + t->con.applyRot(t, tc, td, axis, NULL); axis_angle_normalized_to_mat3(mat, axis, angle * td->factor); } else if (t->flag & T_PROP_EDIT) { axis_angle_normalized_to_mat3(mat, axis, angle * td->factor); } - - ElementRotation(t, td, mat, t->around); + + ElementRotation(t, tc, td, mat, t->around); + } } } @@ -4022,7 +4063,7 @@ static void applyRotation(TransInfo *t, const int UNUSED(mval[2])) snapGridIncrement(t, &final); if ((t->con.mode & CON_APPLY) && t->con.applyRot) { - t->con.applyRot(t, NULL, t->axis, NULL); + t->con.applyRot(t, NULL, NULL, t->axis, NULL); } else { /* reset axis if constraint is not set */ @@ -4091,7 +4132,6 @@ static void initTrackball(TransInfo *t) static void applyTrackballValue(TransInfo *t, const float axis1[3], const float axis2[3], float angles[2]) { - TransData *td = t->data; float mat[3][3]; float axis[3]; float angle; @@ -4102,7 +4142,9 @@ static void applyTrackballValue(TransInfo *t, const float axis1[3], const float angle = normalize_v3(axis); axis_angle_normalized_to_mat3(mat, axis, angle); - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -4113,7 +4155,8 @@ static void applyTrackballValue(TransInfo *t, const float axis1[3], const float axis_angle_normalized_to_mat3(mat, axis, td->factor * angle); } - ElementRotation(t, td, mat, t->around); + ElementRotation(t, tc, td, mat, t->around); + } } } @@ -4368,24 +4411,26 @@ static void headerTranslation(TransInfo *t, const float vec[3], char str[UI_MAX_ static void applyTranslationValue(TransInfo *t, const float vec[3]) { - TransData *td = t->data; + const bool apply_snap_align_rotation = usingSnappingNormal(t);// && (t->tsnap.status & POINT_INIT); float tvec[3]; /* The ideal would be "apply_snap_align_rotation" only when a snap point is found * so, maybe inside this function is not the best place to apply this rotation. * but you need "handle snapping rotation before doing the translation" (really?) */ - const bool apply_snap_align_rotation = usingSnappingNormal(t);// && (t->tsnap.status & POINT_INIT); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + float pivot[3]; if (apply_snap_align_rotation) { copy_v3_v3(pivot, t->tsnap.snapTarget); /* The pivot has to be in local-space (see T49494) */ if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; + Object *ob = tc->obedit ? tc->obedit : tc->poseobj; mul_m4_v3(ob->imat, pivot); } } - for (int i = 0; i < t->total; i++, td++) { + TransData *td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -4414,7 +4459,7 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) unit_m3(mat); } - ElementRotation_ex(t, td, mat, pivot); + ElementRotation_ex(t, tc, td, mat, pivot); if (td->loc) { use_rotate_offset = true; @@ -4424,7 +4469,7 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) if (t->con.applyVec) { float pvec[3]; - t->con.applyVec(t, td, vec, tvec, pvec); + t->con.applyVec(t, tc, td, vec, tvec, pvec); } else { copy_v3_v3(tvec, vec); @@ -4444,6 +4489,7 @@ static void applyTranslationValue(TransInfo *t, const float vec[3]) constraintTransLim(t, td); } + } } static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) @@ -4468,7 +4514,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) if (t->con.mode & CON_APPLY) { float pvec[3] = {0.0f, 0.0f, 0.0f}; - t->con.applyVec(t, NULL, t->values, value_final, pvec); + t->con.applyVec(t, NULL, NULL, t->values, value_final, pvec); headerTranslation(t, pvec, str); /* only so we have re-usable value with redo, see T46741. */ @@ -4512,7 +4558,7 @@ static void applyTranslation(TransInfo *t, const int UNUSED(mval[2])) static void initShrinkFatten(TransInfo *t) { // If not in mesh edit mode, fallback to Resize - if (t->obedit == NULL || t->obedit->type != OB_MESH) { + if ((t->flag & T_EDIT) == 0 || (t->obedit_type != OB_MESH)) { initResize(t); } else { @@ -4542,7 +4588,6 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) int i; char str[UI_MAX_DRAW_STR]; size_t ofs = 0; - TransData *td = t->data; distance = -t->values[0]; @@ -4579,7 +4624,9 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) WM_bool_as_string((t->flag & T_ALT_TRANSFORM) != 0)); /* done with header string */ - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { float tdistance; /* temp dist */ if (td->flag & TD_NOACTION) break; @@ -4595,6 +4642,7 @@ static void applyShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) madd_v3_v3v3fl(td->loc, td->iloc, td->axismtx[2], tdistance); } + } recalcData(t); @@ -4633,7 +4681,6 @@ static void initTilt(TransInfo *t) static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; int i; char str[UI_MAX_DRAW_STR]; @@ -4661,7 +4708,9 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Tilt: %.2f° %s"), RAD2DEGF(final), t->proptext); } - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -4672,6 +4721,7 @@ static void applyTilt(TransInfo *t, const int UNUSED(mval[2])) *td->val = td->ival + final * td->factor; } } + } recalcData(t); @@ -4713,7 +4763,6 @@ static void initCurveShrinkFatten(TransInfo *t) static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float ratio; int i; char str[UI_MAX_DRAW_STR]; @@ -4737,7 +4786,9 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %3f"), ratio); } - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -4751,6 +4802,7 @@ static void applyCurveShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (*td->val <= 0.0f) *td->val = 0.001f; } } + } recalcData(t); @@ -4792,7 +4844,6 @@ static void initMaskShrinkFatten(TransInfo *t) static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td; float ratio; int i; bool initial_feather = false; @@ -4821,7 +4872,9 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (ratio > 1.0f) { initial_feather = true; - for (td = t->data, i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -4831,10 +4884,13 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (td->ival >= 0.001f) initial_feather = false; } + } } /* apply shrink/fatten */ - for (td = t->data, i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (td = tc->data, i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -4852,6 +4908,7 @@ static void applyMaskShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (*td->val <= 0.0f) *td->val = 0.001f; } } + } recalcData(t); @@ -4893,7 +4950,6 @@ static void initGPShrinkFatten(TransInfo *t) static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float ratio; int i; char str[UI_MAX_DRAW_STR]; @@ -4917,7 +4973,9 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Shrink/Fatten: %3f"), ratio); } - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -4931,6 +4989,7 @@ static void applyGPShrinkFatten(TransInfo *t, const int UNUSED(mval[2])) if (*td->val <= 0.0f) *td->val = 0.001f; } } + } recalcData(t); @@ -4970,7 +5029,6 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) float distance; int i; char str[UI_MAX_DRAW_STR]; - TransData *td = t->data; distance = t->values[0]; @@ -4994,21 +5052,23 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) } if (t->con.applyRot && t->con.mode & CON_APPLY) { - t->con.applyRot(t, NULL, axis_global, NULL); + t->con.applyRot(t, NULL, NULL, axis_global, NULL); } - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; - sub_v3_v3v3(vec, t->center, td->center); + sub_v3_v3v3(vec, tc->center_local, td->center); if (t->con.applyRot && t->con.mode & CON_APPLY) { float axis[3]; copy_v3_v3(axis, axis_global); - t->con.applyRot(t, td, axis, NULL); + t->con.applyRot(t, tc, td, axis, NULL); mul_m3_v3(td->smtx, axis); if (isLockConstraint(t)) { @@ -5024,6 +5084,7 @@ static void applyPushPull(TransInfo *t, const int UNUSED(mval[2])) add_v3_v3v3(td->loc, td->iloc, vec); } + } recalcData(t); @@ -5060,7 +5121,6 @@ static void initBevelWeight(TransInfo *t) static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float weight; int i; char str[UI_MAX_DRAW_STR]; @@ -5094,7 +5154,9 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Bevel Weight: %.3f %s"), weight, t->proptext); } - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -5104,6 +5166,7 @@ static void applyBevelWeight(TransInfo *t, const int UNUSED(mval[2])) if (*td->val > 1.0f) *td->val = 1.0f; } } + } recalcData(t); @@ -5140,7 +5203,6 @@ static void initCrease(TransInfo *t) static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float crease; int i; char str[UI_MAX_DRAW_STR]; @@ -5174,7 +5236,9 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) BLI_snprintf(str, sizeof(str), IFACE_("Crease: %.3f %s"), crease, t->proptext); } - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -5187,6 +5251,7 @@ static void applyCrease(TransInfo *t, const int UNUSED(mval[2])) if (*td->val > 1.0f) *td->val = 1.0f; } } + } recalcData(t); @@ -5251,7 +5316,7 @@ static void headerBoneSize(TransInfo *t, const float vec[3], char str[UI_MAX_DRA } } -static void ElementBoneSize(TransInfo *t, TransData *td, float mat[3][3]) +static void ElementBoneSize(TransInfo *t, TransDataContainer *tc, TransData *td, float mat[3][3]) { float tmat[3][3], smat[3][3], oldy; float sizemat[3][3]; @@ -5260,7 +5325,7 @@ static void ElementBoneSize(TransInfo *t, TransData *td, float mat[3][3]) mul_m3_m3m3(tmat, td->smtx, smat); if (t->con.applySize) { - t->con.applySize(t, td, tmat); + t->con.applySize(t, tc, td, tmat); } /* we've tucked the scale in loc */ @@ -5273,7 +5338,6 @@ static void ElementBoneSize(TransInfo *t, TransData *td, float mat[3][3]) static void applyBoneSize(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float size[3], mat[3][3]; float ratio = t->values[0]; int i; @@ -5292,23 +5356,26 @@ static void applyBoneSize(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, size); if (t->con.applySize) { - t->con.applySize(t, NULL, mat); + t->con.applySize(t, NULL, NULL, mat); } copy_m3_m3(t->mat, mat); // used in manipulator headerBoneSize(t, size, str); - - for (i = 0; i < t->total; i++, td++) { + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; - - ElementBoneSize(t, td, mat); + + ElementBoneSize(t, tc, td, mat); } - + } + recalcData(t); ED_area_headerprint(t->sa, str); @@ -5344,7 +5411,6 @@ static void initBoneEnvelope(TransInfo *t) static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float ratio; int i; char str[UI_MAX_DRAW_STR]; @@ -5367,8 +5433,10 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) else { BLI_snprintf(str, sizeof(str), IFACE_("Envelope: %3f"), ratio); } - - for (i = 0; i < t->total; i++, td++) { + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -5383,7 +5451,8 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) *td->val = ratio; } } - + } + recalcData(t); ED_area_headerprint(t->sa, str); @@ -5397,9 +5466,9 @@ static void applyBoneEnvelope(TransInfo *t, const int UNUSED(mval[2])) * \{ */ static void slide_origdata_init_flag( - TransInfo *t, SlideOrigData *sod) + TransInfo *t, TransDataContainer *tc, SlideOrigData *sod) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; const bool has_layer_math = CustomData_has_math(&bm->ldata); const int cd_loop_mdisp_offset = CustomData_get_offset(&bm->ldata, CD_MDISPS); @@ -5420,10 +5489,10 @@ static void slide_origdata_init_flag( } static void slide_origdata_init_data( - TransInfo *t, SlideOrigData *sod) + TransDataContainer *tc, SlideOrigData *sod) { if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; sod->origfaces = BLI_ghash_ptr_new(__func__); @@ -5484,11 +5553,11 @@ static void slide_origdata_create_data_vert( } static void slide_origdata_create_data( - TransInfo *t, SlideOrigData *sod, + TransInfo *t, TransDataContainer *tc, SlideOrigData *sod, TransDataGenericSlideVert *sv_array, unsigned int v_stride, unsigned int v_num) { if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; unsigned int i; TransDataGenericSlideVert *sv; @@ -5520,15 +5589,15 @@ static void slide_origdata_create_data( } if (t->flag & T_MIRROR) { - TransData *td = t->data; + TransData *td = tc->data; TransDataGenericSlideVert *sv_mirror; - sod->sv_mirror = MEM_callocN(sizeof(*sv_mirror) * t->total, __func__); - sod->totsv_mirror = t->total; + sod->sv_mirror = MEM_callocN(sizeof(*sv_mirror) * tc->data_len, __func__); + sod->totsv_mirror = tc->data_len; sv_mirror = sod->sv_mirror; - for (i = 0; i < t->total; i++, td++) { + for (i = 0; i < tc->data_len; i++, td++) { BMVert *eve = td->extra; if (eve) { sv_mirror->v = eve; @@ -5682,12 +5751,12 @@ static void slide_origdata_interp_data_vert( } static void slide_origdata_interp_data( - TransInfo *t, SlideOrigData *sod, + Object *obedit, SlideOrigData *sod, TransDataGenericSlideVert *sv, unsigned int v_stride, unsigned int v_num, bool is_final) { if (sod->use_origfaces) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(obedit); BMesh *bm = em->bm; unsigned int i; const bool has_mdisps = (sod->cd_loop_mdisp_offset != -1); @@ -5750,7 +5819,7 @@ static void slide_origdata_free_date( static void calcEdgeSlideCustomPoints(struct TransInfo *t) { - EdgeSlideData *sld = t->custom.mode.data; + EdgeSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; setCustomPoints(t, &t->mouse, sld->mval_end, sld->mval_start); @@ -5947,11 +6016,11 @@ static BMLoop *get_next_loop(BMVert *v, BMLoop *l, * Calculate screenspace `mval_start` / `mval_end`, optionally slide direction. */ static void calcEdgeSlide_mval_range( - TransInfo *t, EdgeSlideData *sld, const int *sv_table, const int loop_nr, + TransInfo *t, TransDataContainer *tc, EdgeSlideData *sld, const int *sv_table, const int loop_nr, const float mval[2], const bool use_occlude_geometry, const bool use_calc_direction) { TransDataEdgeSlideVert *sv_array = sld->sv; - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; ARegion *ar = t->ar; View3D *v3d = NULL; @@ -5978,7 +6047,7 @@ static void calcEdgeSlide_mval_range( unit_m4(projectMat); } else { - ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); + ED_view3d_ob_project_mat_get(rv3d, tc->obedit, projectMat); } if (use_occlude_geometry) { @@ -6021,7 +6090,7 @@ static void calcEdgeSlide_mval_range( /* This test is only relevant if object is not wire-drawn! See [#32068]. */ if (use_occlude_geometry && - !BMBVH_EdgeVisible(bmbvh, e_other, t->depsgraph, ar, v3d, t->obedit)) + !BMBVH_EdgeVisible(bmbvh, e_other, t->depsgraph, ar, v3d, tc->obedit)) { continue; } @@ -6109,7 +6178,7 @@ static void calcEdgeSlide_mval_range( } static void calcEdgeSlide_even( - TransInfo *t, EdgeSlideData *sld, const float mval[2]) + TransInfo *t, TransDataContainer *tc, EdgeSlideData *sld, const float mval[2]) { TransDataEdgeSlideVert *sv = sld->sv; @@ -6134,7 +6203,7 @@ static void calcEdgeSlide_even( unit_m4(projectMat); } else { - ED_view3d_ob_project_mat_get(rv3d, t->obedit, projectMat); + ED_view3d_ob_project_mat_get(rv3d, tc->obedit, projectMat); } for (i = 0; i < sld->totsv; i++, sv++) { @@ -6154,9 +6223,9 @@ static void calcEdgeSlide_even( } } -static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool flipped, bool use_clamp) +static bool createEdgeSlideVerts_double_side(TransInfo *t, TransDataContainer *tc) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; BMIter iter; BMEdge *e; @@ -6171,13 +6240,9 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f View3D *v3d = NULL; RegionView3D *rv3d = NULL; - slide_origdata_init_flag(t, &sld->orig_data); + slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->use_even = use_even; sld->curr_sv_index = 0; - sld->flipped = flipped; - if (!use_clamp) - t->flag |= T_ALT_TRANSFORM; /*ensure valid selection*/ BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { @@ -6493,26 +6558,24 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f if (t->spacetype == SPACE_VIEW3D) { v3d = t->sa ? t->sa->spacedata.first : NULL; rv3d = t->ar ? t->ar->regiondata : NULL; - use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } - calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, true); + calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, true); /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(t, &sld->orig_data); - slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); + slide_origdata_init_data(tc, &sld->orig_data); + slide_origdata_create_data(t, tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); if (rv3d) { - calcEdgeSlide_even(t, sld, mval); + calcEdgeSlide_even(t, tc, sld, mval); } sld->em = em; - - sld->perc = 0.0f; - - t->custom.mode.data = sld; - + + tc->custom.mode.data = sld; + MEM_freeN(sv_table); return true; @@ -6522,9 +6585,9 @@ static bool createEdgeSlideVerts_double_side(TransInfo *t, bool use_even, bool f * A simple version of #createEdgeSlideVerts_double_side * Which assumes the longest unselected. */ -static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool flipped, bool use_clamp) +static bool createEdgeSlideVerts_single_side(TransInfo *t, TransDataContainer *tc) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; BMIter iter; BMEdge *e; @@ -6544,15 +6607,9 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f rv3d = t->ar ? t->ar->regiondata : NULL; } - slide_origdata_init_flag(t, &sld->orig_data); + slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->use_even = use_even; sld->curr_sv_index = 0; - /* happens to be best for single-sided */ - sld->flipped = !flipped; - if (!use_clamp) - t->flag |= T_ALT_TRANSFORM; - /* ensure valid selection */ { int i = 0, j = 0; @@ -6696,25 +6753,23 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f if (t->spacetype == SPACE_VIEW3D) { v3d = t->sa ? t->sa->spacedata.first : NULL; rv3d = t->ar ? t->ar->regiondata : NULL; - use_occlude_geometry = (v3d && t->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); + use_occlude_geometry = (v3d && TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->dt > OB_WIRE && v3d->drawtype > OB_WIRE); } - calcEdgeSlide_mval_range(t, sld, sv_table, loop_nr, mval, use_occlude_geometry, false); + calcEdgeSlide_mval_range(t, tc, sld, sv_table, loop_nr, mval, use_occlude_geometry, false); /* create copies of faces for customdata projection */ bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(t, &sld->orig_data); - slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); + slide_origdata_init_data(tc, &sld->orig_data); + slide_origdata_create_data(t, tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); if (rv3d) { - calcEdgeSlide_even(t, sld, mval); + calcEdgeSlide_even(t, tc, sld, mval); } sld->em = em; - sld->perc = 0.0f; - - t->custom.mode.data = sld; + tc->custom.mode.data = sld; MEM_freeN(sv_table); @@ -6723,14 +6778,16 @@ static bool createEdgeSlideVerts_single_side(TransInfo *t, bool use_even, bool f void projectEdgeSlideData(TransInfo *t, bool is_final) { - EdgeSlideData *sld = t->custom.mode.data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; SlideOrigData *sod = &sld->orig_data; if (sod->use_origfaces == false) { return; } - slide_origdata_interp_data(t, sod, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv, is_final); + slide_origdata_interp_data(tc->obedit, sod, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv, is_final); + } } void freeEdgeSlideTempFaces(EdgeSlideData *sld) @@ -6738,7 +6795,7 @@ void freeEdgeSlideTempFaces(EdgeSlideData *sld) slide_origdata_free_date(&sld->orig_data); } -void freeEdgeSlideVerts(TransInfo *UNUSED(t), TransCustomData *custom_data) +void freeEdgeSlideVerts(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data) { EdgeSlideData *sld = custom_data->data; @@ -6758,30 +6815,53 @@ void freeEdgeSlideVerts(TransInfo *UNUSED(t), TransCustomData *custom_data) static void initEdgeSlide_ex(TransInfo *t, bool use_double_side, bool use_even, bool flipped, bool use_clamp) { EdgeSlideData *sld; - bool ok; + bool ok = false; t->mode = TFM_EDGE_SLIDE; t->transform = applyEdgeSlide; t->handleEvent = handleEventEdgeSlide; + { + EdgeSlideParams *slp = MEM_callocN(sizeof(*slp), __func__); + slp->use_even = use_even; + slp->flipped = flipped; + /* happens to be best for single-sided */ + if (use_double_side == false) { + slp->flipped = !flipped; + } + slp->perc = 0.0f; + + if (!use_clamp) { + t->flag |= T_ALT_TRANSFORM; + } + + t->custom.mode.data = slp; + t->custom.mode.use_free = true; + } + if (use_double_side) { - ok = createEdgeSlideVerts_double_side(t, use_even, flipped, use_clamp); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + ok |= createEdgeSlideVerts_double_side(t, tc); + } } else { - ok = createEdgeSlideVerts_single_side(t, use_even, flipped, use_clamp); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + ok |= createEdgeSlideVerts_single_side(t, tc); + } } if (!ok) { t->state = TRANS_CANCEL; return; } - - sld = t->custom.mode.data; - - if (!sld) - return; - t->custom.mode.free_cb = freeEdgeSlideVerts; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + sld = tc->custom.mode.data; + if (!sld) { + continue; + } + tc->custom.mode.free_cb = freeEdgeSlideVerts; + } /* set custom point first if you want value to be initialized by init */ calcEdgeSlideCustomPoints(t); @@ -6808,20 +6888,20 @@ static void initEdgeSlide(TransInfo *t) static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEvent *event) { if (t->mode == TFM_EDGE_SLIDE) { - EdgeSlideData *sld = t->custom.mode.data; + EdgeSlideParams *slp = t->custom.mode.data; - if (sld) { + if (slp) { switch (event->type) { case EKEY: if (event->val == KM_PRESS) { - sld->use_even = !sld->use_even; + slp->use_even = !slp->use_even; calcEdgeSlideCustomPoints(t); return TREDRAW_HARD; } break; case FKEY: if (event->val == KM_PRESS) { - sld->flipped = !sld->flipped; + slp->flipped = !slp->flipped; calcEdgeSlideCustomPoints(t); return TREDRAW_HARD; } @@ -6835,6 +6915,7 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven } break; case EVT_MODAL_MAP: +#if 0 switch (event->val) { case TFM_MODAL_EDGESLIDE_DOWN: sld->curr_sv_index = ((sld->curr_sv_index - 1) + sld->totsv) % sld->totsv; @@ -6843,6 +6924,7 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven sld->curr_sv_index = (sld->curr_sv_index + 1) % sld->totsv; return TREDRAW_HARD; } +#endif break; case MOUSEMOVE: calcEdgeSlideCustomPoints(t); @@ -6857,12 +6939,13 @@ static eRedrawFlag handleEventEdgeSlide(struct TransInfo *t, const struct wmEven static void drawEdgeSlide(TransInfo *t) { - if ((t->mode == TFM_EDGE_SLIDE) && t->custom.mode.data) { - EdgeSlideData *sld = t->custom.mode.data; + if ((t->mode == TFM_EDGE_SLIDE) && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data) { + EdgeSlideParams *slp = t->custom.mode.data; + EdgeSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); /* Even mode */ - if ((sld->use_even == true) || (is_clamp == false)) { + if ((slp->use_even == true) || (is_clamp == false)) { View3D *v3d = t->view; const float line_size = UI_GetThemeValuef(TH_OUTLINE_WIDTH) + 0.5f; @@ -6873,16 +6956,16 @@ static void drawEdgeSlide(TransInfo *t) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); gpuPushMatrix(); - gpuMultMatrix(t->obedit->obmat); + gpuMultMatrix(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); unsigned int pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); - if (sld->use_even == true) { + if (slp->use_even == true) { float co_a[3], co_b[3], co_mark[3]; TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; - const float fac = (sld->perc + 1.0f) / 2.0f; + const float fac = (slp->perc + 1.0f) / 2.0f; const float ctrl_size = UI_GetThemeValuef(TH_FACEDOT_SIZE) + 1.5f; const float guide_size = ctrl_size - 0.5f; const int alpha_shade = -30; @@ -6906,7 +6989,7 @@ static void drawEdgeSlide(TransInfo *t) immUniformThemeColorShadeAlpha(TH_SELECT, -30, alpha_shade); glPointSize(ctrl_size); immBegin(GWN_PRIM_POINTS, 1); - if (sld->flipped) { + if (slp->flipped) { if (curr_sv->v_side[1]) immVertex3fv(pos, curr_sv->v_side[1]->co); } else { @@ -6932,6 +7015,7 @@ static void drawEdgeSlide(TransInfo *t) immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); immBegin(GWN_PRIM_LINES, sld->totsv * 2); + /* TODO(campbell): Loop over all verts */ sv = sld->sv; for (i = 0; i < sld->totsv; i++, sv++) { float a[3], b[3]; @@ -6972,28 +7056,32 @@ static void drawEdgeSlide(TransInfo *t) static void doEdgeSlide(TransInfo *t, float perc) { - EdgeSlideData *sld = t->custom.mode.data; - TransDataEdgeSlideVert *svlist = sld->sv, *sv; - int i; + EdgeSlideParams *slp = t->custom.mode.data; + EdgeSlideData *sld_active = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; - sld->perc = perc; - sv = svlist; + slp->perc = perc; - if (sld->use_even == false) { + if (slp->use_even == false) { const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); if (is_clamp) { const int side_index = (perc < 0.0f); const float perc_final = fabsf(perc); - for (i = 0; i < sld->totsv; i++, sv++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + TransDataEdgeSlideVert *sv = sld->sv; + for (int i = 0; i < sld->totsv; i++, sv++) { madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, sv->dir_side[side_index], perc_final); } - sld->curr_side_unclamp = side_index; + } } else { - const int side_index = sld->curr_side_unclamp; - const float perc_init = fabsf(perc) * ((sld->curr_side_unclamp == (perc < 0.0f)) ? 1 : -1); - for (i = 0; i < sld->totsv; i++, sv++) { + const float perc_init = fabsf(perc) * ((sld_active->curr_side_unclamp == (perc < 0.0f)) ? 1 : -1); + const int side_index = sld_active->curr_side_unclamp; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + TransDataEdgeSlideVert *sv = sld->sv; + for (int i = 0; i < sld->totsv; i++, sv++) { float dir_flip[3]; float perc_final = perc_init; if (!is_zero_v3(sv->dir_side[side_index])) { @@ -7005,6 +7093,7 @@ static void doEdgeSlide(TransInfo *t, float perc) } madd_v3_v3v3fl(sv->v->co, sv->v_co_orig, dir_flip, perc_final); } + } } } else { @@ -7016,20 +7105,23 @@ static void doEdgeSlide(TransInfo *t, float perc) * \note len_v3v3(curr_sv->dir_side[0], curr_sv->dir_side[1]) * is the same as the distance between the original vert locations, same goes for the lines below. */ - TransDataEdgeSlideVert *curr_sv = &sld->sv[sld->curr_sv_index]; - const float curr_length_perc = curr_sv->edge_len * (((sld->flipped ? perc : -perc) + 1.0f) / 2.0f); + TransDataEdgeSlideVert *curr_sv = &sld_active->sv[sld_active->curr_sv_index]; + const float curr_length_perc = curr_sv->edge_len * (((slp->flipped ? perc : -perc) + 1.0f) / 2.0f); float co_a[3]; float co_b[3]; - for (i = 0; i < sld->totsv; i++, sv++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + TransDataEdgeSlideVert *sv = sld->sv; + for (int i = 0; i < sld->totsv; i++, sv++) { if (sv->edge_len > FLT_EPSILON) { const float fac = min_ff(sv->edge_len, curr_length_perc) / sv->edge_len; add_v3_v3v3(co_a, sv->v_co_orig, sv->dir_side[0]); add_v3_v3v3(co_b, sv->v_co_orig, sv->dir_side[1]); - if (sld->flipped) { + if (slp->flipped) { interp_line_v3_v3v3v3(sv->v->co, co_b, sv->v_co_orig, co_a, fac); } else { @@ -7037,6 +7129,7 @@ static void doEdgeSlide(TransInfo *t, float perc) } } } + } } } @@ -7045,9 +7138,9 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) char str[UI_MAX_DRAW_STR]; size_t ofs = 0; float final; - EdgeSlideData *sld = t->custom.mode.data; - bool flipped = sld->flipped; - bool use_even = sld->use_even; + EdgeSlideParams *slp = t->custom.mode.data; + bool flipped = slp->flipped; + bool use_even = slp->use_even; const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num)); @@ -7099,7 +7192,7 @@ static void applyEdgeSlide(TransInfo *t, const int UNUSED(mval[2])) static void calcVertSlideCustomPoints(struct TransInfo *t) { - VertSlideData *sld = t->custom.mode.data; + VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; TransDataVertSlideVert *sv = &sld->sv[sld->curr_sv_index]; const float *co_orig_3d = sv->co_orig_3d; @@ -7134,7 +7227,8 @@ static void calcVertSlideCustomPoints(struct TransInfo *t) */ static void calcVertSlideMouseActiveVert(struct TransInfo *t, const int mval[2]) { - VertSlideData *sld = t->custom.mode.data; + /* Active object may have no selected vertices. */ + VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; float mval_fl[2] = {UNPACK2(mval)}; TransDataVertSlideVert *sv; @@ -7161,7 +7255,7 @@ static void calcVertSlideMouseActiveVert(struct TransInfo *t, const int mval[2]) */ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2]) { - VertSlideData *sld = t->custom.mode.data; + VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; float imval_fl[2] = {UNPACK2(t->mouse.imval)}; float mval_fl[2] = {UNPACK2(mval)}; @@ -7189,7 +7283,7 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2] float dir_dot; sub_v3_v3v3(tdir, sv->co_orig_3d, sv->co_link_orig_3d[j]); - mul_mat3_m4_v3(t->obedit->obmat, tdir); + mul_mat3_m4_v3(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, tdir); project_plane_v3_v3v3(tdir, tdir, t->viewinv[2]); normalize_v3(tdir); @@ -7207,9 +7301,9 @@ static void calcVertSlideMouseActiveEdges(struct TransInfo *t, const int mval[2] } } -static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool use_clamp) +static bool createVertSlideVerts(TransInfo *t, TransDataContainer *tc) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; BMIter iter; BMIter eiter; @@ -7219,13 +7313,9 @@ static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool VertSlideData *sld = MEM_callocN(sizeof(*sld), "sld"); int j; - slide_origdata_init_flag(t, &sld->orig_data); + slide_origdata_init_flag(t, tc, &sld->orig_data); - sld->use_even = use_even; sld->curr_sv_index = 0; - sld->flipped = flipped; - if (!use_clamp) - t->flag |= T_ALT_TRANSFORM; j = 0; BM_ITER_MESH (v, &iter, bm, BM_VERTS_OF_MESH) { @@ -7288,14 +7378,12 @@ static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool sld->totsv = j; bmesh_edit_begin(bm, BMO_OPTYPE_FLAG_UNTAN_MULTIRES); - slide_origdata_init_data(t, &sld->orig_data); - slide_origdata_create_data(t, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); + slide_origdata_init_data(tc, &sld->orig_data); + slide_origdata_create_data(t, tc, &sld->orig_data, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv); sld->em = em; - sld->perc = 0.0f; - - t->custom.mode.data = sld; + tc->custom.mode.data = sld; /* most likely will be set below */ unit_m4(sld->proj_mat); @@ -7307,9 +7395,12 @@ static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool rv3d = ar ? ar->regiondata : NULL; if (rv3d) { - ED_view3d_ob_project_mat_get(rv3d, t->obedit, sld->proj_mat); + ED_view3d_ob_project_mat_get(rv3d, tc->obedit, sld->proj_mat); } + } + /* XXX, calc vert slide across all objects */ + if (tc == t->data_container) { calcVertSlideMouseActiveVert(t, t->mval); calcVertSlideMouseActiveEdges(t, t->mval); } @@ -7319,14 +7410,13 @@ static bool createVertSlideVerts(TransInfo *t, bool use_even, bool flipped, bool void projectVertSlideData(TransInfo *t, bool is_final) { - VertSlideData *sld = t->custom.mode.data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + VertSlideData *sld = tc->custom.mode.data; SlideOrigData *sod = &sld->orig_data; - - if (sod->use_origfaces == false) { - return; + if (sod->use_origfaces == true) { + slide_origdata_interp_data(tc->obedit, sod, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv, is_final); + } } - - slide_origdata_interp_data(t, sod, (TransDataGenericSlideVert *)sld->sv, sizeof(*sld->sv), sld->totsv, is_final); } void freeVertSlideTempFaces(VertSlideData *sld) @@ -7334,7 +7424,7 @@ void freeVertSlideTempFaces(VertSlideData *sld) slide_origdata_free_date(&sld->orig_data); } -void freeVertSlideVerts(TransInfo *UNUSED(t), TransCustomData *custom_data) +void freeVertSlideVerts(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data) { VertSlideData *sld = custom_data->data; @@ -7361,23 +7451,38 @@ void freeVertSlideVerts(TransInfo *UNUSED(t), TransCustomData *custom_data) static void initVertSlide_ex(TransInfo *t, bool use_even, bool flipped, bool use_clamp) { - VertSlideData *sld; t->mode = TFM_VERT_SLIDE; t->transform = applyVertSlide; t->handleEvent = handleEventVertSlide; - if (!createVertSlideVerts(t, use_even, flipped, use_clamp)) { - t->state = TRANS_CANCEL; - return; + { + VertSlideParams *slp = MEM_callocN(sizeof(*slp), __func__); + slp->use_even = use_even; + slp->flipped = flipped; + slp->perc = 0.0f; + + if (!use_clamp) { + t->flag |= T_ALT_TRANSFORM; + } + + t->custom.mode.data = slp; + t->custom.mode.use_free = true; } - sld = t->custom.mode.data; + bool ok = false; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + ok |= createVertSlideVerts(t, tc); + VertSlideData *sld = tc->custom.mode.data; + if (sld) { + tc->custom.mode.free_cb = freeVertSlideVerts; + } + } - if (!sld) + if (ok == false) { + t->state = TRANS_CANCEL; return; - - t->custom.mode.free_cb = freeVertSlideVerts; + } /* set custom point first if you want value to be initialized by init */ calcVertSlideCustomPoints(t); @@ -7404,14 +7509,14 @@ static void initVertSlide(TransInfo *t) static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEvent *event) { if (t->mode == TFM_VERT_SLIDE) { - VertSlideData *sld = t->custom.mode.data; + VertSlideParams *slp = t->custom.mode.data; - if (sld) { + if (slp) { switch (event->type) { case EKEY: if (event->val == KM_PRESS) { - sld->use_even = !sld->use_even; - if (sld->flipped) { + slp->use_even = !slp->use_even; + if (slp->flipped) { calcVertSlideCustomPoints(t); } return TREDRAW_HARD; @@ -7419,7 +7524,7 @@ static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEven break; case FKEY: if (event->val == KM_PRESS) { - sld->flipped = !sld->flipped; + slp->flipped = !slp->flipped; calcVertSlideCustomPoints(t); return TREDRAW_HARD; } @@ -7464,8 +7569,8 @@ static eRedrawFlag handleEventVertSlide(struct TransInfo *t, const struct wmEven static void drawVertSlide(TransInfo *t) { - if ((t->mode == TFM_VERT_SLIDE) && t->custom.mode.data) { - VertSlideData *sld = t->custom.mode.data; + if ((t->mode == TFM_VERT_SLIDE) && TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data) { + VertSlideData *sld = TRANS_DATA_CONTAINER_FIRST_OK(t)->custom.mode.data; const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); /* Non-Prop mode */ @@ -7485,12 +7590,12 @@ static void drawVertSlide(TransInfo *t) glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_ONE, GL_ONE_MINUS_SRC_ALPHA); gpuPushMatrix(); - gpuMultMatrix(t->obedit->obmat); + gpuMultMatrix(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); glLineWidth(line_size); const uint shdr_pos = GWN_vertformat_attr_add(immVertexFormat(), "pos", GWN_COMP_F32, 3, GWN_FETCH_FLOAT); - + immBindBuiltinProgram(GPU_SHADER_3D_UNIFORM_COLOR); immUniformThemeColorShadeAlpha(TH_EDGE_SELECT, 80, alpha_shade); @@ -7540,13 +7645,13 @@ static void drawVertSlide(TransInfo *t) mval_ofs[0] = t->mval[0] - t->mouse.imval[0]; mval_ofs[1] = t->mval[1] - t->mouse.imval[1]; - mul_v3_m4v3(co_orig_3d, t->obedit->obmat, curr_sv->co_orig_3d); + mul_v3_m4v3(co_orig_3d, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat, curr_sv->co_orig_3d); zfac = ED_view3d_calc_zfac(t->ar->regiondata, co_orig_3d, NULL); ED_view3d_win_to_delta(t->ar, mval_ofs, co_dest_3d, zfac); - invert_m4_m4(t->obedit->imat, t->obedit->obmat); - mul_mat3_m4_v3(t->obedit->imat, co_dest_3d); + invert_m4_m4(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat, TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->obmat); + mul_mat3_m4_v3(TRANS_DATA_CONTAINER_FIRST_OK(t)->obedit->imat, co_dest_3d); add_v3_v3(co_dest_3d, curr_sv->co_orig_3d); @@ -7581,7 +7686,8 @@ static void drawVertSlide(TransInfo *t) static void doVertSlide(TransInfo *t, float perc) { - VertSlideData *sld = t->custom.mode.data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + VertSlideData *sld = tc->custom.mode.data; TransDataVertSlideVert *svlist = sld->sv, *sv; int i; @@ -7618,6 +7724,7 @@ static void doVertSlide(TransInfo *t, float perc) } } } + } } static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) @@ -7625,9 +7732,9 @@ static void applyVertSlide(TransInfo *t, const int UNUSED(mval[2])) char str[UI_MAX_DRAW_STR]; size_t ofs = 0; float final; - VertSlideData *sld = t->custom.mode.data; - const bool flipped = sld->flipped; - const bool use_even = sld->use_even; + VertSlideData *slp = t->custom.mode.data; + const bool flipped = slp->flipped; + const bool use_even = slp->use_even; const bool is_clamp = !(t->flag & T_ALT_TRANSFORM); const bool is_constrained = !(is_clamp == false || hasNumInput(&t->num)); @@ -7700,7 +7807,6 @@ static void initBoneRoll(TransInfo *t) static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; int i; char str[UI_MAX_DRAW_STR]; @@ -7726,7 +7832,9 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) } /* set roll values */ - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -7735,6 +7843,7 @@ static void applyBoneRoll(TransInfo *t, const int UNUSED(mval[2])) *(td->val) = td->ival - final; } + } recalcData(t); @@ -7767,7 +7876,6 @@ static void initBakeTime(TransInfo *t) static void applyBakeTime(TransInfo *t, const int mval[2]) { - TransData *td = t->data; float time; int i; char str[UI_MAX_DRAW_STR]; @@ -7811,7 +7919,9 @@ static void applyBakeTime(TransInfo *t, const int mval[2]) BLI_snprintf(str, sizeof(str), IFACE_("Time: %.3f %s"), time, t->proptext); } - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -7824,6 +7934,7 @@ static void applyBakeTime(TransInfo *t, const int mval[2]) if (td->ext->quat && *td->val > *td->ext->quat) *td->val = *td->ext->quat; } } + } recalcData(t); @@ -7844,14 +7955,13 @@ static void initMirror(TransInfo *t) initMouseInputMode(t, &t->mouse, INPUT_NONE); t->flag |= T_NULL_ONE; - if (!t->obedit) { + if ((t->flag & T_EDIT) == 0) { t->flag |= T_NO_ZERO; } } static void applyMirror(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td; float size[3], mat[3][3]; int i; char str[UI_MAX_DRAW_STR]; @@ -7869,19 +7979,22 @@ static void applyMirror(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, size); if (t->con.applySize) { - t->con.applySize(t, NULL, mat); + t->con.applySize(t, NULL, NULL, mat); } BLI_snprintf(str, sizeof(str), IFACE_("Mirror%s"), t->con.text); - for (i = 0, td = t->data; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; - ElementResize(t, td, mat); + ElementResize(t, tc, td, mat); + } } recalcData(t); @@ -7893,14 +8006,17 @@ static void applyMirror(TransInfo *t, const int UNUSED(mval[2])) size_to_mat3(mat, size); - for (i = 0, td = t->data; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->flag & TD_SKIP) continue; - ElementResize(t, td, mat); + ElementResize(t, tc, td, mat); + } } recalcData(t); @@ -7931,14 +8047,15 @@ static void initAlign(TransInfo *t) static void applyAlign(TransInfo *t, const int UNUSED(mval[2])) { - TransData *td = t->data; float center[3]; int i; - /* saving original center */ - copy_v3_v3(center, t->center); - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* saving original center */ + copy_v3_v3(center, tc->center_local); + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { float mat[3][3], invmat[3][3]; if (td->flag & TD_NOACTION) @@ -7949,11 +8066,11 @@ static void applyAlign(TransInfo *t, const int UNUSED(mval[2])) /* around local centers */ if (t->flag & (T_OBJECT | T_POSE)) { - copy_v3_v3(t->center, td->center); + copy_v3_v3(tc->center_local, td->center); } else { if (t->settings->selectmode & SCE_SELECT_FACE) { - copy_v3_v3(t->center, td->center); + copy_v3_v3(tc->center_local, td->center); } } @@ -7961,11 +8078,11 @@ static void applyAlign(TransInfo *t, const int UNUSED(mval[2])) mul_m3_m3m3(mat, t->spacemtx, invmat); - ElementRotation(t, td, mat, t->around); + ElementRotation(t, tc, td, mat, t->around); } - /* restoring original center */ - copy_v3_v3(t->center, center); + copy_v3_v3(tc->center_local, center); + } recalcData(t); @@ -8027,10 +8144,11 @@ static void headerSeqSlide(TransInfo *t, const float val[2], char str[UI_MAX_DRA static void applySeqSlideValue(TransInfo *t, const float val[2]) { - TransData *td = t->data; int i; - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; @@ -8039,6 +8157,7 @@ static void applySeqSlideValue(TransInfo *t, const float val[2]) madd_v2_v2v2fl(td->loc, td->iloc, val, td->factor); } + } } static void applySeqSlide(TransInfo *t, const int mval[2]) @@ -8050,7 +8169,7 @@ static void applySeqSlide(TransInfo *t, const int mval[2]) if (t->con.mode & CON_APPLY) { float pvec[3] = {0.0f, 0.0f, 0.0f}; float tvec[3]; - t->con.applyVec(t, NULL, t->values, tvec, pvec); + t->con.applyVec(t, NULL, NULL, t->values, tvec, pvec); copy_v3_v3(t->values, tvec); } else { @@ -8270,18 +8389,19 @@ static void headerTimeTranslate(TransInfo *t, char str[UI_MAX_DRAW_STR]) static void applyTimeTranslateValue(TransInfo *t) { - TransData *td = t->data; - TransData2D *td2d = t->data2d; Scene *scene = t->scene; int i; - + const short autosnap = getAnimEdit_SnapMode(t); const double secf = FPS; float deltax, val /* , valprev */; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + TransData2D *td2d = tc->data_2d; /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ - for (i = 0; i < t->total; i++, td++, td2d++) { + for (i = 0; i < tc->data_len; i++, td++, td2d++) { /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from * (this is only valid when not in NLA) @@ -8321,6 +8441,7 @@ static void applyTimeTranslateValue(TransInfo *t) /* apply nearest snapping */ doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); } + } } static void applyTimeTranslate(TransInfo *t, const int mval[2]) @@ -8386,9 +8507,9 @@ static void initTimeSlide(TransInfo *t) float min = 999999999.0f, max = -999999999.0f; int i; - - TransData *td = t->data; - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { AnimData *adt = (t->spacetype != SPACE_NLA) ? td->extra : NULL; float val = *(td->val); @@ -8399,6 +8520,7 @@ static void initTimeSlide(TransInfo *t) if (min > val) min = val; if (max < val) max = val; } + } if (min == max) { /* just use the current frame ranges */ @@ -8450,7 +8572,6 @@ static void headerTimeSlide(TransInfo *t, const float sval, char str[UI_MAX_DRAW static void applyTimeSlideValue(TransInfo *t, float sval) { - TransData *td = t->data; int i; const float *range = t->custom.mode.data; float minx = range[0]; @@ -8465,7 +8586,9 @@ static void applyTimeSlideValue(TransInfo *t, float sval) } /* it doesn't matter whether we apply to t->data or t->data2d, but t->data2d is more convenient */ - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from * (this is only valid when not in NLA) @@ -8506,6 +8629,7 @@ static void applyTimeSlideValue(TransInfo *t, float sval) } } } + } } static void applyTimeSlide(TransInfo *t, const int mval[2]) @@ -8563,8 +8687,8 @@ static void initTimeScale(TransInfo *t) /* recalculate center2d to use CFRA and mouse Y, since that's * what is used in time scale */ if ((t->flag & T_OVERRIDE_CENTER) == 0) { - t->center[0] = t->scene->r.cfra; - projectFloatView(t, t->center, center); + t->center_global[0] = t->scene->r.cfra; + projectFloatView(t, t->center_global, center); center[1] = t->mouse.imval[1]; } @@ -8605,15 +8729,15 @@ static void headerTimeScale(TransInfo *t, char str[UI_MAX_DRAW_STR]) static void applyTimeScaleValue(TransInfo *t) { Scene *scene = t->scene; - TransData *td = t->data; - TransData2D *td2d = t->data2d; int i; const short autosnap = getAnimEdit_SnapMode(t); const double secf = FPS; - - for (i = 0; i < t->total; i++, td++, td2d++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + TransData2D *td2d = tc->data_2d; + for (i = 0; i < tc->data_len; i++, td++, td2d++) { /* it is assumed that td->extra is a pointer to the AnimData, * whose active action is where this keyframe comes from * (this is only valid when not in NLA) @@ -8639,6 +8763,7 @@ static void applyTimeScaleValue(TransInfo *t) /* apply nearest snapping */ doAnimEdit_SnapFrame(t, td, td2d, adt, autosnap); } + } } static void applyTimeScale(TransInfo *t, const int UNUSED(mval[2])) @@ -8666,7 +8791,7 @@ bool checkUseAxisMatrix(TransInfo *t) /* currently only checks for editmode */ if (t->flag & T_EDIT) { if ((t->around == V3D_AROUND_LOCAL_ORIGINS) && - (ELEM(t->obedit->type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) + (ELEM(t->obedit_type, OB_MESH, OB_CURVE, OB_MBALL, OB_ARMATURE))) { /* not all editmode supports axis-matrix */ return true; diff --git a/source/blender/editors/transform/transform.h b/source/blender/editors/transform/transform.h index fe05207e645..ba496a0c744 100644 --- a/source/blender/editors/transform/transform.h +++ b/source/blender/editors/transform/transform.h @@ -47,6 +47,7 @@ struct Depsgraph; struct TransInfo; +struct TransDataContainer; struct TransData; struct TransformOrientation; struct TransSnap; @@ -109,7 +110,7 @@ typedef struct TransSnap { * \note Return value can be anything, * where the smallest absolute value defines whats closest. */ - float (*distance)(struct TransInfo *, const float p1[3], const float p2[3]); + float (*distance)(struct TransInfo *t, const float p1[3], const float p2[3]); /** * Re-usable snap context data. @@ -127,14 +128,16 @@ typedef struct TransCon { /* the one in TransInfo is not garanty to stay the same (Rotates change it) */ int mode; /* Mode flags of the Constraint */ void (*drawExtra)(struct TransInfo *t); + + /* Note: if 'tc' is NULL, 'td' must also be NULL. */ /* For constraints that needs to draw differently from the other * uses this instead of the generic draw function */ - void (*applyVec)(struct TransInfo *t, struct TransData *td, const float in[3], float out[3], float pvec[3]); + void (*applyVec)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, const float in[3], float out[3], float pvec[3]); /* Apply function pointer for linear vectorial transformation */ /* The last three parameters are pointers to the in/out/printable vectors */ - void (*applySize)(struct TransInfo *t, struct TransData *td, float smat[3][3]); + void (*applySize)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, float smat[3][3]); /* Apply function pointer for size transformation */ - void (*applyRot)(struct TransInfo *t, struct TransData *td, float vec[3], float *angle); + void (*applyRot)(struct TransInfo *t, struct TransDataContainer *tc, struct TransData *td, float vec[3], float *angle); /* Apply function pointer for rotation transformation */ } TransCon; @@ -270,10 +273,6 @@ typedef struct EdgeSlideData { SlideOrigData orig_data; - float perc; - - bool use_even; - bool flipped; int curr_sv_index; @@ -281,6 +280,12 @@ typedef struct EdgeSlideData { int curr_side_unclamp; } EdgeSlideData; +typedef struct EdgeSlideParams { + float perc; + + bool use_even; + bool flipped; +} EdgeSlideParams; typedef struct TransDataVertSlideVert { /* TransDataGenericSlideVert */ @@ -313,6 +318,13 @@ typedef struct VertSlideData { float proj_mat[4][4]; } VertSlideData; +typedef struct VertSlideParams { + float perc; + + bool use_even; + bool flipped; +} VertSlideParams; + typedef struct BoneInitData { struct EditBone *bone; float tail[3]; @@ -373,16 +385,68 @@ typedef struct MouseInput { typedef struct TransCustomData { void *data; - void (*free_cb)(struct TransInfo *, struct TransCustomData *); + void (*free_cb)(struct TransInfo *, struct TransDataContainer *tc, struct TransCustomData *custom_data); unsigned int use_free : 1; } TransCustomData; typedef struct TransCenterData { - float local[3], global[3]; + float global[3]; unsigned int is_set : 1; } TransCenterData; +/** + * Rule of thumb for choosing between mode/type: + * - If transform mode uses the data, assign to `mode` + * (typically in transform.c). + * - If conversion uses the data as an extension to the #TransData, assign to `type` + * (typically in transform_conversion.c). + */ +typedef struct TransCustomDataContainer { + /** Owned by the mode (grab, scale, bend... ).*/ + union { + TransCustomData mode, first_elem; + }; + TransCustomData type; +} TransCustomDataContainer; +#define TRANS_CUSTOM_DATA_ELEM_MAX (sizeof(TransCustomDataContainer) / sizeof(TransCustomData)) + +typedef struct TransDataContainer { + /** + * Use for cases we care about the active, eg: active vert of active mesh. + * if set this will _always_ be the first item in the array. + */ + bool is_active; + + /** Transformed data (array). */ + TransData *data; + /** Total number of transformed data. */ + int data_len; + + /** Transformed data extension (array). */ + TransDataExtension *data_ext; + /** Transformed data for 2d (array). */ + TransData2D *data_2d; + + struct Object *obedit; + /** Normalized editmode matrix ('T_EDIT' only). */ + float obedit_mat[3][3]; + + /** if 't->flag & T_POSE', this denotes pose object */ + struct Object *poseobj; + + /** Center of transformation (in local-space), Calculated from #TransInfo.center_global. */ + float center_local[3]; + + TransCustomDataContainer custom; +} TransDataContainer; + typedef struct TransInfo { + TransDataContainer *data_container; + int data_container_len; + /** Combine length of all #TransDataContainer.data_len + * Use to check if nothing is selected or if we have a single selection. */ + int data_len_all; + int mode; /* current mode */ int flag; /* generic flags for special behaviors */ int modifiers; /* special modifiers, by function, not key */ @@ -393,10 +457,6 @@ typedef struct TransInfo { /* transform function pointer */ eRedrawFlag (*handleEvent)(struct TransInfo *, const struct wmEvent *); /* event handler function pointer RETURN 1 if redraw is needed */ - int total; /* total number of transformed data */ - TransData *data; /* transformed data (array) */ - TransDataExtension *ext; /* transformed data extension (array) */ - TransData2D *data2d; /* transformed data for 2d (array) */ TransCon con; /* transformed constraint */ TransSnap tsnap; NumInput num; /* numerical input */ @@ -406,7 +466,6 @@ typedef struct TransInfo { char proptext[20]; /* proportional falloff text */ float aspect[3]; /* spaces using non 1:1 aspect, (uv's, f-curve, movie-clip... etc) * use for conversion and snapping. */ - float center[3]; /* center of transformation (in local-space) */ float center_global[3]; /* center of transformation (in global-space) */ float center2d[2]; /* center in screen coordinates */ /* Lazy initialize center data for when we need other center values. @@ -425,6 +484,7 @@ typedef struct TransInfo { short around; char spacetype; /* spacetype where transforming is */ char helpline; /* helpline modes (not to be confused with hotline) */ + short obedit_type; /* Avoid looking inside TransDataContainer obedit. */ float vec[3]; /* translation, to show for widget */ float mat[3][3]; /* rot/rescale, to show for widget */ @@ -432,25 +492,6 @@ typedef struct TransInfo { float spacemtx[3][3]; /* orientation matrix of the current space */ char spacename[64]; /* name of the current space, MAX_NAME */ - struct Object *poseobj; /* if t->flag & T_POSE, this denotes pose object */ - - /** - * Rule of thumb for choosing between mode/type: - * - If transform mode uses the data, assign to `mode` - * (typically in transform.c). - * - If conversion uses the data as an extension to the #TransData, assign to `type` - * (typically in transform_conversion.c). - */ - struct { - /* owned by the mode (grab, scale, bend... )*/ - union { - TransCustomData mode, first_elem; - }; - /* owned by the type (mesh, armature, nla...) */ - TransCustomData type; - } custom; -#define TRANS_CUSTOM_DATA_ELEM_MAX (sizeof(((TransInfo *)NULL)->custom) / sizeof(TransCustomData)) - /*************** NEW STUFF *********************/ short launch_event; /* event type used to launch transform */ @@ -484,12 +525,13 @@ typedef struct TransInfo { struct ReportList *reports; /* assign from the operator, or can be NULL */ int mval[2]; /* current mouse position */ float zfac; /* use for 3d view */ - struct Object *obedit; - float obedit_mat[3][3]; /* normalized editmode matrix (T_EDIT only) */ void *draw_handle_apply; void *draw_handle_view; void *draw_handle_pixel; void *draw_handle_cursor; + + /** Typically for mode settings. */ + TransCustomDataContainer custom; } TransInfo; @@ -648,7 +690,7 @@ void flushTransSeq(TransInfo *t); void flushTransTracking(TransInfo *t); void flushTransMasking(TransInfo *t); void flushTransPaintCurve(TransInfo *t); -void restoreBones(TransInfo *t); +void restoreBones(TransDataContainer *tc); /*********************** transform_manipulator.c ********** */ @@ -758,6 +800,7 @@ void setInputPostFct(MouseInput *mi, void (*post)(struct TransInfo *t, float val /*********************** Generics ********************************/ +void initTransDataContainers_FromObjectData(TransInfo *t); void initTransInfo(struct bContext *C, TransInfo *t, struct wmOperator *op, const struct wmEvent *event); void postTrans(struct bContext *C, TransInfo *t); void resetTransModal(TransInfo *t); @@ -773,9 +816,7 @@ void restoreTransObjects(TransInfo *t); void recalcData(TransInfo *t); void calculateCenter2D(TransInfo *t); -void calculateCenterGlobal( - TransInfo *t, const float center_local[3], - float r_center_global[3]); +void calculateCenterLocal(TransInfo *t, const float center_global[3]); const TransCenterData *transformCenter_from_type(TransInfo *t, int around); void calculateCenter(TransInfo *t); @@ -816,11 +857,11 @@ int getTransformOrientation_ex(const struct bContext *C, float normal[3], float int getTransformOrientation(const struct bContext *C, float normal[3], float plane[3]); void freeEdgeSlideTempFaces(EdgeSlideData *sld); -void freeEdgeSlideVerts(TransInfo *t, TransCustomData *custom_data); +void freeEdgeSlideVerts(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data); void projectEdgeSlideData(TransInfo *t, bool is_final); void freeVertSlideTempFaces(VertSlideData *sld); -void freeVertSlideVerts(TransInfo *t, TransCustomData *custom_data); +void freeVertSlideVerts(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data); void projectVertSlideData(TransInfo *t, bool is_final); @@ -831,4 +872,22 @@ bool checkUseAxisMatrix(TransInfo *t); #define TRANSFORM_SNAP_MAX_PX 100.0f #define TRANSFORM_DIST_INVALID -FLT_MAX +/* Temp macros. */ + +/* This is to be replaced, just to get things compiling early on. */ +#define TRANS_DATA_CONTAINER_FIRST_EVIL(t) (&(t)->data_container[0]) +#define TRANS_DATA_CONTAINER_FIRST_OK(t) (&(t)->data_container[0]) +/* For cases we _know_ there is only one handle. */ +#define TRANS_DATA_CONTAINER_FIRST_SINGLE(t) (BLI_assert((t)->data_container_len == 1), (&(t)->data_container[0])) + +#define FOREACH_TRANS_DATA_CONTAINER(t, th) \ + for (TransDataContainer *tc = t->data_container, *tc_end = t->data_container + t->data_container_len; \ + th != tc_end; \ + th++) + +#define FOREACH_TRANS_DATA_CONTAINER_INDEX(t, th, i) \ + for (TransDataContainer *tc = ((i = 0), t->data_container), *tc_end = t->data_container + t->data_container_len; \ + th != tc_end; \ + th++, i++) + #endif diff --git a/source/blender/editors/transform/transform_constraints.c b/source/blender/editors/transform/transform_constraints.c index f612dc0e474..bfaac4933f0 100644 --- a/source/blender/editors/transform/transform_constraints.c +++ b/source/blender/editors/transform/transform_constraints.c @@ -333,7 +333,8 @@ static void planeProjection(TransInfo *t, const float in[3], float out[3]) * */ -static void applyAxisConstraintVec(TransInfo *t, TransData *td, const float in[3], float out[3], float pvec[3]) +static void applyAxisConstraintVec( + TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, const float in[3], float out[3], float pvec[3]) { copy_v3_v3(out, in); if (!td && t->con.mode & CON_APPLY) { @@ -380,7 +381,8 @@ static void applyAxisConstraintVec(TransInfo *t, TransData *td, const float in[3 * Further down, that vector is mapped to each data's space. */ -static void applyObjectConstraintVec(TransInfo *t, TransData *td, const float in[3], float out[3], float pvec[3]) +static void applyObjectConstraintVec( + TransInfo *t, TransDataContainer *tc, TransData *td, const float in[3], float out[3], float pvec[3]) { copy_v3_v3(out, in); if (t->con.mode & CON_APPLY) { @@ -428,7 +430,7 @@ static void applyObjectConstraintVec(TransInfo *t, TransData *td, const float in mul_m3_v3(td->axismtx, out); if (t->flag & T_EDIT) { - mul_m3_v3(t->obedit_mat, out); + mul_m3_v3(tc->obedit_mat, out); } } } @@ -438,7 +440,8 @@ static void applyObjectConstraintVec(TransInfo *t, TransData *td, const float in * Generic callback for constant spatial constraints applied to resize motion */ -static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3]) +static void applyAxisConstraintSize( + TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, float smat[3][3]) { if (!td && t->con.mode & CON_APPLY) { float tmat[3][3]; @@ -462,7 +465,8 @@ static void applyAxisConstraintSize(TransInfo *t, TransData *td, float smat[3][3 * Callback for object based spatial constraints applied to resize motion */ -static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3][3]) +static void applyObjectConstraintSize( + TransInfo *t, TransDataContainer *tc, TransData *td, float smat[3][3]) { if (td && t->con.mode & CON_APPLY) { float tmat[3][3]; @@ -482,7 +486,7 @@ static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3] mul_m3_m3m3(tmat, smat, imat); if (t->flag & T_EDIT) { - mul_m3_m3m3(smat, t->obedit_mat, smat); + mul_m3_m3m3(smat, tc->obedit_mat, smat); } mul_m3_m3m3(smat, td->axismtx, tmat); } @@ -502,7 +506,7 @@ static void applyObjectConstraintSize(TransInfo *t, TransData *td, float smat[3] * (ie: not doing counterclockwise rotations when the mouse moves clockwise). */ -static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle) +static void applyAxisConstraintRot(TransInfo *t, TransDataContainer *UNUSED(tc), TransData *td, float vec[3], float *angle) { if (!td && t->con.mode & CON_APPLY) { int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2); @@ -544,7 +548,8 @@ static void applyAxisConstraintRot(TransInfo *t, TransData *td, float vec[3], fl * (ie: not doing counterclockwise rotations when the mouse moves clockwise). */ -static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3], float *angle) +static void applyObjectConstraintRot( + TransInfo *t, TransDataContainer *tc, TransData *td, float vec[3], float *angle) { if (t->con.mode & CON_APPLY) { int mode = t->con.mode & (CON_AXIS0 | CON_AXIS1 | CON_AXIS2); @@ -553,11 +558,11 @@ static void applyObjectConstraintRot(TransInfo *t, TransData *td, float vec[3], /* on setup call, use first object */ if (td == NULL) { - td = t->data; + td = tc->data; } if (t->flag & T_EDIT) { - mul_m3_m3m3(tmp_axismtx, t->obedit_mat, td->axismtx); + mul_m3_m3m3(tmp_axismtx, tc->obedit_mat, td->axismtx); axismtx = tmp_axismtx; } else { @@ -607,20 +612,21 @@ void setConstraint(TransInfo *t, float space[3][3], int mode, const char text[]) /* applies individual td->axismtx constraints */ void setAxisMatrixConstraint(TransInfo *t, int mode, const char text[]) { - if (t->total == 1) { + TransDataContainer *tc = t->data_container; + if (t->data_len_all == 1) { float axismtx[3][3]; if (t->flag & T_EDIT) { - mul_m3_m3m3(axismtx, t->obedit_mat, t->data->axismtx); + mul_m3_m3m3(axismtx, tc->obedit_mat, tc->data->axismtx); } else { - copy_m3_m3(axismtx, t->data->axismtx); + copy_m3_m3(axismtx, tc->data->axismtx); } setConstraint(t, axismtx, mode, text); } else { BLI_strncpy(t->con.text + 1, text, sizeof(t->con.text) - 1); - copy_m3_m3(t->con.mtx, t->data->axismtx); + copy_m3_m3(t->con.mtx, tc->data->axismtx); t->con.mode = mode; getConstraintMatrix(t); @@ -638,7 +644,9 @@ void setLocalConstraint(TransInfo *t, int mode, const char text[]) { /* edit-mode now allows local transforms too */ if (t->flag & T_EDIT) { - setConstraint(t, t->obedit_mat, mode, text); + /* Use the active (first) edit object. */ + TransDataContainer *tc = t->data_container; + setConstraint(t, tc->obedit_mat, mode, text); } else { setAxisMatrixConstraint(t, mode, text); @@ -836,11 +844,12 @@ static void drawObjectConstraint(TransInfo *t) * Without drawing the first light, users have little clue what they are doing. */ short options = DRAWLIGHT; - TransData *td = t->data; int i; float tmp_axismtx[3][3]; - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { float co[3]; float (*axismtx)[3]; @@ -863,13 +872,13 @@ static void drawObjectConstraint(TransInfo *t) axismtx = td->axismtx; } else if (t->flag & T_EDIT) { - mul_v3_m4v3(co, t->obedit->obmat, td->center); + mul_v3_m4v3(co, tc->obedit->obmat, td->center); - mul_m3_m3m3(tmp_axismtx, t->obedit_mat, td->axismtx); + mul_m3_m3m3(tmp_axismtx, tc->obedit_mat, td->axismtx); axismtx = tmp_axismtx; } else if (t->flag & T_POSE) { - mul_v3_m4v3(co, t->poseobj->obmat, td->center); + mul_v3_m4v3(co, tc->poseobj->obmat, td->center); axismtx = td->axismtx; } else { @@ -888,6 +897,7 @@ static void drawObjectConstraint(TransInfo *t) } options &= ~DRAWLIGHT; } + } } /*--------------------- START / STOP CONSTRAINTS ---------------------- */ diff --git a/source/blender/editors/transform/transform_conversions.c b/source/blender/editors/transform/transform_conversions.c index fff124c8995..936985d4cb9 100644 --- a/source/blender/editors/transform/transform_conversions.c +++ b/source/blender/editors/transform/transform_conversions.c @@ -130,7 +130,7 @@ */ static void transform_around_single_fallback(TransInfo *t) { - if ((t->total == 1) && + if ((t->data_len_all == 1) && (ELEM(t->around, V3D_AROUND_CENTER_BOUNDS, V3D_AROUND_CENTER_MEAN, V3D_AROUND_ACTIVE)) && (ELEM(t->mode, TFM_RESIZE, TFM_ROTATION, TFM_TRACKBALL))) { @@ -171,27 +171,31 @@ static int trans_data_compare_rdist(const void *a, const void *b) void sort_trans_data_dist(TransInfo *t) { - TransData *start = t->data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *start = tc->data; int i; - for (i = 0; i < t->total && start->flag & TD_SELECTED; i++) + for (i = 0; i < tc->data_len && start->flag & TD_SELECTED; i++) { start++; - - if (i < t->total) { + } + + if (i < tc->data_len) { if (t->flag & T_PROP_CONNECTED) - qsort(start, t->total - i, sizeof(TransData), trans_data_compare_dist); + qsort(start, tc->data_len - i, sizeof(TransData), trans_data_compare_dist); else - qsort(start, t->total - i, sizeof(TransData), trans_data_compare_rdist); + qsort(start, tc->data_len - i, sizeof(TransData), trans_data_compare_rdist); + } } } static void sort_trans_data(TransInfo *t) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData *sel, *unsel; TransData temp; - unsel = t->data; - sel = t->data; - sel += t->total - 1; + unsel = tc->data; + sel = tc->data; + sel += tc->data_len - 1; while (sel > unsel) { while (unsel->flag & TD_SELECTED) { unsel++; @@ -211,13 +215,13 @@ static void sort_trans_data(TransInfo *t) sel--; unsel++; } + } } /* distance calculated from not-selected vertex to nearest selected vertex * warning; this is loops inside loop, has minor N^2 issues, but by sorting list it is OK */ static void set_prop_dist(TransInfo *t, const bool with_dist) { - TransData *tob; int a; float _proj_vec[3]; @@ -234,7 +238,9 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) } } - for (a = 0, tob = t->data; a < t->total; a++, tob++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *tob = tc->data; + for (a = 0; a < tc->data_len; a++, tob++) { tob->rdist = 0.0f; // init, it was mallocced @@ -245,7 +251,7 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) tob->rdist = -1.0f; // signal for next loop - for (i = 0, td = t->data; i < t->total; i++, td++) { + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { if (td->flag & TD_SELECTED) { if (use_island) { sub_v3_v3v3(vec, tob->iloc, td->iloc); @@ -279,6 +285,7 @@ static void set_prop_dist(TransInfo *t, const bool with_dist) } } } + } } /* ************************** CONVERSIONS ************************* */ @@ -296,26 +303,32 @@ static void createTransTexspace(TransInfo *t) ob = OBACT(view_layer); if (ob == NULL) { // Shouldn't logically happen, but still... - t->total = 0; + t->data_len_all = 0; return; } id = ob->data; if (id == NULL || !ELEM(GS(id->name), ID_ME, ID_CU, ID_MB)) { BKE_report(t->reports, RPT_ERROR, "Unsupported object type for text-space transform"); - t->total = 0; + t->data_len_all = 0; return; } if (BKE_object_obdata_is_libdata(ob)) { BKE_report(t->reports, RPT_ERROR, "Linked data can't text-space transform"); - t->total = 0; + t->data_len_all = 0; return; } - t->total = 1; - td = t->data = MEM_callocN(sizeof(TransData), "TransTexspace"); - td->ext = t->ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace"); + + { + TransDataContainer *tc = t->data_container = MEM_callocN(sizeof(*t->data_container), __func__); + tc->data_len = 1; + td = tc->data = MEM_callocN(sizeof(TransData), "TransTexspace"); + td->ext = tc->data_ext = MEM_callocN(sizeof(TransDataExtension), "TransTexspace"); + } + + t->data_len_all = 1; td->flag = TD_SELECTED; copy_v3_v3(td->center, ob->obmat[3]); @@ -340,7 +353,9 @@ static void createTransTexspace(TransInfo *t) static void createTransEdge(TransInfo *t) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); TransData *td = NULL; BMEdge *eed; BMIter iter; @@ -356,29 +371,31 @@ static void createTransEdge(TransInfo *t) } } - if (countsel == 0) - return; + if (countsel == 0) { + tc->data_len = 0; + continue; + } if (is_prop_edit) { - t->total = count; + tc->data_len = count; } else { - t->total = countsel; + tc->data_len = countsel; } - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransCrease"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransCrease"); - copy_m3_m4(mtx, t->obedit->obmat); + copy_m3_m4(mtx, tc->obedit->obmat); pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); /* create data we need */ if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(t->obedit), ME_CDFLAG_EDGE_BWEIGHT); + BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_EDGE_BWEIGHT); cd_edge_float_offset = CustomData_get_offset(&em->bm->edata, CD_BWEIGHT); } else { //if (t->mode == TFM_CREASE) { BLI_assert(t->mode == TFM_CREASE); - BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(t->obedit), ME_CDFLAG_EDGE_CREASE); + BM_mesh_cd_flag_ensure(em->bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_EDGE_CREASE); cd_edge_float_offset = CustomData_get_offset(&em->bm->edata, CD_CREASE); } @@ -408,6 +425,7 @@ static void createTransEdge(TransInfo *t) td++; } } + } } /* ********************* pose mode ************* */ @@ -523,7 +541,7 @@ static short apply_targetless_ik(Object *ob) return apply; } -static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransData *td) +static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, TransDataContainer *tc, TransData *td) { Bone *bone = pchan->bone; float pmat[3][3], omat[3][3]; @@ -634,7 +652,7 @@ static void add_pose_transdata(TransInfo *t, bPoseChannel *pchan, Object *ob, Tr normalize_m3(td->axismtx); if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { - bArmature *arm = t->poseobj->data; + bArmature *arm = tc->poseobj->data; if ((t->mode == TFM_BONE_ENVELOPE_DIST) || (arm->drawtype == ARM_ENVELOPE)) { td->loc = NULL; @@ -828,15 +846,20 @@ void transform_autoik_update(TransInfo *t, short mode) } } - /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */ - if (ELEM(NULL, t->poseobj, t->poseobj->pose)) - return; - /* apply to all pose-channels */ bool changed = false; - for (pchan = t->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) { + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + /* sanity checks (don't assume t->poseobj is set, or that it is an armature) */ + if (ELEM(NULL, tc->poseobj, tc->poseobj->pose)) { + continue; + } + + for (pchan = tc->poseobj->pose->chanbase.first; pchan; pchan = pchan->next) { changed |= pchan_autoik_adjust(pchan, *chainlen); } + } if (changed) { /* TODO(sergey): Consider doing partial update only. */ @@ -1045,9 +1068,28 @@ static short pose_grab_with_ik(Object *ob) } -/* only called with pose mode active object now */ -static void createTransPose(TransInfo *t, Object *ob) +/** + * When objects array is NULL, use 't->data_container' as is. + */ +static void createTransPose(TransInfo *t, Object **objects, uint objects_len) { + if (objects != NULL) { + if (t->data_container) { + MEM_freeN(t->data_container); + } + t->data_container = MEM_callocN(sizeof(*t->data_container) * objects_len, __func__); + t->data_container_len = objects_len; + int th_index; + FOREACH_TRANS_DATA_CONTAINER_INDEX (t, tc, th_index) { + tc->poseobj = objects[th_index]; + } + } + + t->data_len_all = 0; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob = tc->poseobj; + bArmature *arm; bPoseChannel *pchan; TransData *td; @@ -1055,11 +1097,12 @@ static void createTransPose(TransInfo *t, Object *ob) short ik_on = 0; int i; - t->total = 0; /* check validity of state */ - arm = BKE_armature_from_object(ob); - if ((arm == NULL) || (ob->pose == NULL)) return; + arm = BKE_armature_from_object(tc->poseobj); + if ((arm == NULL) || (ob->pose == NULL)) { + continue; + } if (arm->flag & ARM_RESTPOS) { if (ELEM(t->mode, TFM_DUMMY, TFM_BONESIZE) == 0) { @@ -1075,45 +1118,51 @@ static void createTransPose(TransInfo *t, Object *ob) } /* set flags and count total (warning, can change transform to rotate) */ - t->total = count_set_pose_transflags(&t->mode, t->around, ob); + tc->data_len = count_set_pose_transflags(&t->mode, t->around, ob); - if (t->total == 0) return; + if (tc->data_len == 0) { + continue; + } - t->flag |= T_POSE; - t->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */ - - /* disable PET, its not usable in pose mode yet [#32444] */ - t->flag &= ~T_PROP_EDIT_ALL; + tc->poseobj = ob; /* we also allow non-active objects to be transformed, in weightpaint */ /* init trans data */ - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransPoseBone"); - tdx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "TransPoseBoneExt"); - for (i = 0; i < t->total; i++, td++, tdx++) { + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransPoseBone"); + tdx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransPoseBoneExt"); + for (i = 0; i < tc->data_len; i++, td++, tdx++) { td->ext = tdx; td->val = NULL; } /* use pose channels to fill trans data */ - td = t->data; + td = tc->data; for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) { if (pchan->bone->flag & BONE_TRANSFORM) { - add_pose_transdata(t, pchan, ob, td); + add_pose_transdata(t, pchan, ob, tc, td); td++; } } - if (td != (t->data + t->total)) { + if (td != (tc->data + tc->data_len)) { BKE_report(t->reports, RPT_DEBUG, "Bone selection count error"); } /* initialize initial auto=ik chainlen's? */ - if (ik_on) transform_autoik_update(t, 0); + if (ik_on) { + transform_autoik_update(t, 0); + } + } + + t->flag |= T_POSE; + /* disable PET, its not usable in pose mode yet [#32444] */ + t->flag &= ~T_PROP_EDIT_ALL; + } -void restoreBones(TransInfo *t) +void restoreBones(TransDataContainer *tc) { - bArmature *arm = t->obedit->data; - BoneInitData *bid = t->custom.type.data; + bArmature *arm = tc->obedit->data; + BoneInitData *bid = tc->custom.type.data; EditBone *ebo; while (bid->bone) { @@ -1154,8 +1203,9 @@ void restoreBones(TransInfo *t) /* ********************* armature ************** */ static void createTransArmatureVerts(TransInfo *t) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { EditBone *ebo, *eboflip; - bArmature *arm = t->obedit->data; + bArmature *arm = tc->obedit->data; ListBase *edbo = arm->edbo; TransData *td, *td_old; float mtx[3][3], smtx[3][3], bonemat[3][3]; @@ -1164,46 +1214,48 @@ static void createTransArmatureVerts(TransInfo *t) int oldtot; BoneInitData *bid; - t->total = 0; + tc->data_len = 0; for (ebo = edbo->first; ebo; ebo = ebo->next) { - oldtot = t->total; + oldtot = tc->data_len; if (EBONE_VISIBLE(arm, ebo) && !(ebo->flag & BONE_EDITMODE_LOCKED)) { if (ELEM(t->mode, TFM_BONESIZE, TFM_BONE_ENVELOPE_DIST)) { if (ebo->flag & BONE_SELECTED) - t->total++; + tc->data_len++; } else if (t->mode == TFM_BONE_ROLL) { if (ebo->flag & BONE_SELECTED) - t->total++; + tc->data_len++; } else { if (ebo->flag & BONE_TIPSEL) - t->total++; + tc->data_len++; if (ebo->flag & BONE_ROOTSEL) - t->total++; + tc->data_len++; } } - if (mirror && (oldtot < t->total)) { + if (mirror && (oldtot < tc->data_len)) { eboflip = ED_armature_ebone_get_mirrored(arm->edbo, ebo); if (eboflip) total_mirrored++; } } - if (!t->total) return; + if (!tc->data_len) { + continue; + } transform_around_single_fallback(t); - copy_m3_m4(mtx, t->obedit->obmat); + copy_m3_m4(mtx, tc->obedit->obmat); pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransEditBone"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransEditBone"); if (mirror) { - t->custom.type.data = bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData"); - t->custom.type.use_free = true; + tc->custom.type.data = bid = MEM_mallocN((total_mirrored + 1) * sizeof(BoneInitData), "BoneInitData"); + tc->custom.type.use_free = true; } i = 0; @@ -1226,7 +1278,7 @@ static void createTransArmatureVerts(TransInfo *t) td->loc = NULL; td->ext = NULL; - td->ob = t->obedit; + td->ob = tc->obedit; td++; } @@ -1241,7 +1293,7 @@ static void createTransArmatureVerts(TransInfo *t) td->loc = NULL; td->ext = NULL; - td->ob = t->obedit; + td->ob = tc->obedit; td++; } @@ -1272,7 +1324,7 @@ static void createTransArmatureVerts(TransInfo *t) normalize_m3(td->axismtx); td->ext = NULL; - td->ob = t->obedit; + td->ob = tc->obedit; td++; } @@ -1287,7 +1339,7 @@ static void createTransArmatureVerts(TransInfo *t) td->flag = TD_SELECTED; td->ext = NULL; - td->ob = t->obedit; + td->ob = tc->obedit; td++; } @@ -1326,7 +1378,7 @@ static void createTransArmatureVerts(TransInfo *t) td->ext = NULL; td->val = NULL; - td->ob = t->obedit; + td->ob = tc->obedit; td++; } @@ -1348,7 +1400,7 @@ static void createTransArmatureVerts(TransInfo *t) td->ext = NULL; td->val = NULL; - td->ob = t->obedit; + td->ob = tc->obedit; td++; } @@ -1375,13 +1427,15 @@ static void createTransArmatureVerts(TransInfo *t) /* trick to terminate iteration */ bid[total_mirrored].bone = NULL; } + } } /* ********************* meta elements ********* */ static void createTransMBallVerts(TransInfo *t) { - MetaBall *mb = (MetaBall *)t->obedit->data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + MetaBall *mb = (MetaBall *)tc->obedit->data; MetaElem *ml; TransData *td; TransDataExtension *tx; @@ -1396,15 +1450,17 @@ static void createTransMBallVerts(TransInfo *t) } /* note: in prop mode we need at least 1 selected */ - if (countsel == 0) return; + if (countsel == 0) { + continue; + } - if (is_prop_edit) t->total = count; - else t->total = countsel; + if (is_prop_edit) tc->data_len = count; + else tc->data_len = countsel; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(MBall EditMode)"); - tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "MetaElement_TransExtension"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(MBall EditMode)"); + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "MetaElement_TransExtension"); - copy_m3_m4(mtx, t->obedit->obmat); + copy_m3_m4(mtx, tc->obedit->obmat); pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); for (ml = mb->editelems->first; ml; ml = ml->next) { @@ -1449,6 +1505,7 @@ static void createTransMBallVerts(TransInfo *t) tx++; } } + } } /* ********************* curve/surface ********* */ @@ -1555,7 +1612,9 @@ static int bezt_select_to_transform_triple_flag( static void createTransCurveVerts(TransInfo *t) { - Curve *cu = t->obedit->data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + Curve *cu = tc->obedit->data; TransData *td = NULL; Nurb *nu; BezTriple *bezt; @@ -1599,18 +1658,21 @@ static void createTransCurveVerts(TransInfo *t) } } /* note: in prop mode we need at least 1 selected */ - if (countsel == 0) return; + if (countsel == 0) { + tc->data_len = 0; + continue; + } - if (is_prop_edit) t->total = count; - else t->total = countsel; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Curve EditMode)"); + if (is_prop_edit) tc->data_len = count; + else tc->data_len = countsel; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Curve EditMode)"); transform_around_single_fallback(t); - copy_m3_m4(mtx, t->obedit->obmat); + copy_m3_m4(mtx, tc->obedit->obmat); pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - td = t->data; + td = tc->data; for (nu = nurbs->first; nu; nu = nu->next) { if (nu->type == CU_BEZIER) { TransData *head, *tail; @@ -1819,7 +1881,7 @@ static void createTransCurveVerts(TransInfo *t) calc_distanceCurveVerts(head, tail - 1); } } - + } #undef SEL_F1 #undef SEL_F2 #undef SEL_F3 @@ -1829,7 +1891,9 @@ static void createTransCurveVerts(TransInfo *t) static void createTransLatticeVerts(TransInfo *t) { - Lattice *latt = ((Lattice *)t->obedit->data)->editlatt->latt; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + Lattice *latt = ((Lattice *)tc->obedit->data)->editlatt->latt; TransData *td = NULL; BPoint *bp; float mtx[3][3], smtx[3][3]; @@ -1850,14 +1914,14 @@ static void createTransLatticeVerts(TransInfo *t) /* note: in prop mode we need at least 1 selected */ if (countsel == 0) return; - if (is_prop_edit) t->total = count; - else t->total = countsel; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Lattice EditMode)"); + if (is_prop_edit) tc->data_len = count; + else tc->data_len = countsel; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Lattice EditMode)"); - copy_m3_m4(mtx, t->obedit->obmat); + copy_m3_m4(mtx, tc->obedit->obmat); pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); - td = t->data; + td = tc->data; bp = latt->def; a = latt->pntsu * latt->pntsv * latt->pntsw; while (a--) { @@ -1884,11 +1948,14 @@ static void createTransLatticeVerts(TransInfo *t) } bp++; } + } } /* ******************* particle edit **************** */ static void createTransParticleVerts(bContext *C, TransInfo *t) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = NULL; TransDataExtension *tx; Object *ob = CTX_data_active_object(C); @@ -1936,13 +2003,13 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) /* note: in prop mode we need at least 1 selected */ if (hasselected == 0) return; - t->total = count; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Particle Mode)"); + tc->data_len = count; + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Particle Mode)"); if (t->mode == TFM_BAKE_TIME) - tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "Particle_TransExtension"); + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "Particle_TransExtension"); else - tx = t->ext = NULL; + tx = tc->data_ext = NULL; unit_m4(mat); @@ -2003,10 +2070,13 @@ static void createTransParticleVerts(bContext *C, TransInfo *t) if (is_prop_edit && head != tail) calc_distanceCurveVerts(head, tail - 1); } + } } void flushTransParticles(TransInfo *t) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Scene *scene = t->scene; ViewLayer *view_layer = t->view_layer; Object *ob = OBACT(view_layer); @@ -2025,7 +2095,7 @@ void flushTransParticles(TransInfo *t) /* we do transform in world space, so flush world space position * back to particle local space (only for hair particles) */ - td = t->data; + td = tc->data; for (i = 0, point = edit->points; i < edit->totpoint; i++, point++, td++) { if (!(point->flag & PEP_TRANSFORM)) continue; @@ -2050,6 +2120,7 @@ void flushTransParticles(TransInfo *t) } PE_update_object(&t->eval_ctx, scene, OBACT(view_layer), 1); + } } /* ********************* mesh ****************** */ @@ -2459,10 +2530,12 @@ static void VertsToTransData(TransInfo *t, TransData *td, TransDataExtension *tx static void createTransEditVerts(TransInfo *t) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *tob = NULL; TransDataExtension *tx = NULL; - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); - Mesh *me = t->obedit->data; + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + Mesh *me = tc->obedit->data; BMesh *bm = em->bm; BMVert *eve; BMIter iter; @@ -2486,8 +2559,11 @@ static void createTransEditVerts(TransInfo *t) int *dists_index = NULL; if (t->flag & T_MIRROR) { - EDBM_verts_mirror_cache_begin(em, 0, false, (t->flag & T_PROP_EDIT) == 0, use_topology); - mirror = 1; + /* TODO(campbell): xform: We need support for many mirror objects at once! */ + if (tc->is_active) { + EDBM_verts_mirror_cache_begin(em, 0, false, (t->flag & T_PROP_EDIT) == 0, use_topology); + mirror = 1; + } } /** @@ -2501,7 +2577,7 @@ static void createTransEditVerts(TransInfo *t) } if (t->mode == TFM_BWEIGHT) { - BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(t->obedit), ME_CDFLAG_VERT_BWEIGHT); + BM_mesh_cd_flag_ensure(bm, BKE_mesh_from_object(tc->obedit), ME_CDFLAG_VERT_BWEIGHT); cd_vert_bweight_offset = CustomData_get_offset(&bm->vdata, CD_BWEIGHT); } @@ -2513,7 +2589,7 @@ static void createTransEditVerts(TransInfo *t) } } - t->total = count; + tc->data_len = count; /* allocating scratch arrays */ if (prop_mode & T_PROP_CONNECTED) { @@ -2524,20 +2600,19 @@ static void createTransEditVerts(TransInfo *t) } } else { - t->total = bm->totvertsel; + tc->data_len = bm->totvertsel; } - tob = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Mesh EditMode)"); + tob = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Mesh EditMode)"); if (ELEM(t->mode, TFM_SKIN_RESIZE, TFM_SHRINKFATTEN)) { /* warning, this is overkill, we only need 2 extra floats, * but this stores loads of extra stuff, for TFM_SHRINKFATTEN its even more overkill * since we may not use the 'alt' transform mode to maintain shell thickness, * but with generic transform code its hard to lazy init vars */ - tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), - "TransObData ext"); + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransObData ext"); } - copy_m3_m4(mtx, t->obedit->obmat); + copy_m3_m4(mtx, tc->obedit->obmat); /* we use a pseudoinverse so that when one of the axes is scaled to 0, * matrix inversion still works and we can still moving along the other */ pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON); @@ -2557,12 +2632,12 @@ static void createTransEditVerts(TransInfo *t) } /* detect CrazySpace [tm] */ - if (modifiers_getCageIndex(t->scene, t->obedit, NULL, 1) != -1) { + if (modifiers_getCageIndex(t->scene, tc->obedit, NULL, 1) != -1) { int totleft = -1; - if (modifiers_isCorrectableDeformed(t->scene, t->obedit)) { + if (modifiers_isCorrectableDeformed(t->scene, tc->obedit)) { /* check if we can use deform matrices for modifier from the * start up to stack, they are more accurate than quats */ - totleft = BKE_crazyspace_get_first_deform_matrices_editbmesh(&t->eval_ctx, t->scene, t->obedit, em, &defmats, &defcos); + totleft = BKE_crazyspace_get_first_deform_matrices_editbmesh(&t->eval_ctx, t->scene, tc->obedit, em, &defmats, &defcos); } /* if we still have more modifiers, also do crazyspace @@ -2575,7 +2650,7 @@ static void createTransEditVerts(TransInfo *t) if (totleft > 0) #endif { - mappedcos = BKE_crazyspace_get_mapped_editverts(&t->eval_ctx, t->scene, t->obedit); + mappedcos = BKE_crazyspace_get_mapped_editverts(&t->eval_ctx, t->scene, tc->obedit); quats = MEM_mallocN(em->bm->totvert * sizeof(*quats), "crazy quats"); BKE_crazyspace_set_quats_editmesh(em, defcos, mappedcos, quats, !prop_mode); if (mappedcos) @@ -2675,8 +2750,8 @@ static void createTransEditVerts(TransInfo *t) } if (mirror != 0) { - tob = t->data; - for (a = 0; a < t->total; a++, tob++) { + tob = tc->data; + for (a = 0; a < tc->data_len; a++, tob++) { if (ABS(tob->loc[0]) <= 0.00001f) { tob->flag |= TD_MIRROR_EDGE; } @@ -2697,12 +2772,15 @@ cleanup: if (t->flag & T_MIRROR) { EDBM_verts_mirror_cache_end(em); } + } } /* *** NODE EDITOR *** */ void flushTransNodes(TransInfo *t) { const float dpi_fac = UI_DPI_FAC; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { int a; TransData *td; TransData2D *td2d; @@ -2710,7 +2788,7 @@ void flushTransNodes(TransInfo *t) applyGridAbsolute(t); /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d; a < t->total; a++, td++, td2d++) { + for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { bNode *node = td->extra; float locx, locy; @@ -2732,11 +2810,12 @@ void flushTransNodes(TransInfo *t) node->locy = locy; } } - + /* handle intersection with noodles */ - if (t->total == 1) { + if (tc->data_len == 1) { ED_node_link_intersect_test(t->sa, 1); } + } } /* *** SEQUENCE EDITOR *** */ @@ -2765,13 +2844,14 @@ BLI_INLINE void trans_update_seq(Scene *sce, Sequence *seq, int old_start, int s void flushTransSeq(TransInfo *t) { ListBase *seqbasep = BKE_sequencer_editing_get(t->scene, false)->seqbasep; /* Editing null check already done */ + int a, new_frame; TransData *td = NULL; TransData2D *td2d = NULL; TransDataSeq *tdsq = NULL; Sequence *seq; - + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); /* prevent updating the same seq twice * if the transdata order is changed this will mess up @@ -2780,7 +2860,7 @@ void flushTransSeq(TransInfo *t) int old_start_prev = 0, sel_flag_prev = 0; /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d; a < t->total; a++, td++, td2d++) { + for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { int old_start; tdsq = (TransDataSeq *)td->extra; seq = tdsq->seq; @@ -2853,8 +2933,8 @@ void flushTransSeq(TransInfo *t) } /* update effects inside meta's */ - for (a = 0, seq_prev = NULL, td = t->data, td2d = t->data2d; - a < t->total; + for (a = 0, seq_prev = NULL, td = tc->data, td2d = tc->data_2d; + a < tc->data_len; a++, td++, td2d++, seq_prev = seq) { tdsq = (TransDataSeq *)td->extra; @@ -2870,7 +2950,7 @@ void flushTransSeq(TransInfo *t) /* need to do the overlap check in a new loop otherwise adjacent strips * will not be updated and we'll get false positives */ seq_prev = NULL; - for (a = 0, td = t->data, td2d = t->data2d; a < t->total; a++, td++, td2d++) { + for (a = 0, td = tc->data, td2d = tc->data_2d; a < tc->data_len; a++, td++, td2d++) { tdsq = (TransDataSeq *)td->extra; seq = tdsq->seq; @@ -2931,22 +3011,27 @@ static void createTransUVs(bContext *C, TransInfo *t) Image *ima = CTX_data_edit_image(C); Scene *scene = t->scene; ToolSettings *ts = CTX_data_tool_settings(C); + + const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; + const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; + const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS); + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = NULL; TransData2D *td2d = NULL; - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMFace *efa; BMIter iter, liter; UvElementMap *elementmap = NULL; BLI_bitmap *island_enabled = NULL; struct { float co[2]; int co_num; } *island_center = NULL; int count = 0, countsel = 0, count_rejected = 0; - const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; - const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0; - const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - if (!ED_space_image_show_uvedit(sima, t->obedit)) - return; + if (!ED_space_image_show_uvedit(sima, tc->obedit)) { + continue; + } /* count */ if (is_prop_connected || is_island_center) { @@ -2969,7 +3054,7 @@ static void createTransUVs(bContext *C, TransInfo *t) BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BMLoop *l; - if (!uvedit_face_visible_test(scene, t->obedit, ima, efa)) { + if (!uvedit_face_visible_test(scene, tc->obedit, ima, efa)) { BM_elem_flag_disable(efa, BM_ELEM_TAG); continue; } @@ -3017,17 +3102,17 @@ static void createTransUVs(bContext *C, TransInfo *t) } } - t->total = (is_prop_edit) ? count : countsel; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(UV Editing)"); + tc->data_len = (is_prop_edit) ? count : countsel; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(UV Editing)"); /* for each 2d uv coord a 3d vector is allocated, so that they can be * treated just as if they were 3d verts */ - t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransObData2D(UV Editing)"); + tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransObData2D(UV Editing)"); if (sima->flag & SI_CLIP_UV) t->flag |= T_CLIP_UV; - td = t->data; - td2d = t->data2d; + td = tc->data; + td2d = tc->data_2d; BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { BMLoop *l; @@ -3065,12 +3150,16 @@ static void createTransUVs(bContext *C, TransInfo *t) } if (is_prop_connected) { - t->total -= count_rejected; + tc->data_len -= count_rejected; } - if (sima->flag & SI_LIVE_UNWRAP) - ED_uvedit_live_unwrap_begin(t->scene, t->obedit); - + if (sima->flag & SI_LIVE_UNWRAP) { + /* TODO(campbell): xform: Only active object currently! + * it uses a static variable. */ + if (tc->is_active) { + ED_uvedit_live_unwrap_begin(t->scene, tc->obedit); + } + } finally: if (is_prop_connected || is_island_center) { @@ -3084,15 +3173,18 @@ finally: MEM_freeN(island_center); } } + } } void flushTransUVs(TransInfo *t) { SpaceImage *sima = t->sa->spacedata.first; + const bool use_pixel_snap = ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)); + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { TransData2D *td; int a; float aspect_inv[2], size[2]; - const bool use_pixel_snap = ((sima->flag & SI_PIXELSNAP) && (t->state != TRANS_CANCEL)); aspect_inv[0] = 1.0f / t->aspect[0]; aspect_inv[1] = 1.0f / t->aspect[1]; @@ -3105,7 +3197,7 @@ void flushTransUVs(TransInfo *t) } /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data2d; a < t->total; a++, td++) { + for (a = 0, td = tc->data_2d; a < tc->data_len; a++, td++) { td->loc2d[0] = td->loc[0] * aspect_inv[0]; td->loc2d[1] = td->loc[1] * aspect_inv[1]; @@ -3114,12 +3206,11 @@ void flushTransUVs(TransInfo *t) td->loc2d[1] = roundf(td->loc2d[1] * size[1]) / size[1]; } } + } } bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) { - TransData *td; - int a; bool clipx = true, clipy = true; float min[2], max[2]; @@ -3127,22 +3218,27 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) max[0] = t->aspect[0]; max[1] = t->aspect[1]; - for (a = 0, td = t->data; a < t->total; a++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + TransData *td; + int a; + + for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { minmax_v2v2_v2(min, max, td->loc); } if (resize) { - if (min[0] < 0.0f && t->center[0] > 0.0f && t->center[0] < t->aspect[0] * 0.5f) - vec[0] *= t->center[0] / (t->center[0] - min[0]); - else if (max[0] > t->aspect[0] && t->center[0] < t->aspect[0]) - vec[0] *= (t->center[0] - t->aspect[0]) / (t->center[0] - max[0]); + if (min[0] < 0.0f && t->center_global[0] > 0.0f && t->center_global[0] < t->aspect[0] * 0.5f) + vec[0] *= t->center_global[0] / (t->center_global[0] - min[0]); + else if (max[0] > t->aspect[0] && t->center_global[0] < t->aspect[0]) + vec[0] *= (t->center_global[0] - t->aspect[0]) / (t->center_global[0] - max[0]); else clipx = 0; - if (min[1] < 0.0f && t->center[1] > 0.0f && t->center[1] < t->aspect[1] * 0.5f) - vec[1] *= t->center[1] / (t->center[1] - min[1]); - else if (max[1] > t->aspect[1] && t->center[1] < t->aspect[1]) - vec[1] *= (t->center[1] - t->aspect[1]) / (t->center[1] - max[1]); + if (min[1] < 0.0f && t->center_global[1] > 0.0f && t->center_global[1] < t->aspect[1] * 0.5f) + vec[1] *= t->center_global[1] / (t->center_global[1] - min[1]); + else if (max[1] > t->aspect[1] && t->center_global[1] < t->aspect[1]) + vec[1] *= (t->center_global[1] - t->aspect[1]) / (t->center_global[1] - max[1]); else clipy = 0; } @@ -3161,16 +3257,16 @@ bool clipUVTransform(TransInfo *t, float vec[2], const bool resize) else clipy = 0; } + } return (clipx || clipy); } void clipUVData(TransInfo *t) { - TransData *td = NULL; - int a; - - for (a = 0, td = t->data; a < t->total; a++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (int a = 0; a < tc->data_len; a++, td++) { if (td->flag & TD_NOACTION) break; @@ -3180,6 +3276,7 @@ void clipUVData(TransInfo *t) td->loc[0] = min_ff(max_ff(0.0f, td->loc[0]), t->aspect[0]); td->loc[1] = min_ff(max_ff(0.0f, td->loc[1]), t->aspect[1]); } + } } /* ********************* ANIMATION EDITORS (GENERAL) ************************* */ @@ -3212,7 +3309,9 @@ static void createTransNlaData(bContext *C, TransInfo *t) int filter; int count = 0; - + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* determine what type of data we are operating on */ if (ANIM_animdata_get_context(C, &ac) == 0) return; @@ -3272,12 +3371,12 @@ static void createTransNlaData(bContext *C, TransInfo *t) } /* allocate memory for data */ - t->total = count; + tc->data_len = count; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(NLA Editor)"); - td = t->data; - t->custom.type.data = tdn = MEM_callocN(t->total * sizeof(TransDataNla), "TransDataNla (NLA Editor)"); - t->custom.type.use_free = true; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(NLA Editor)"); + td = tc->data; + tc->custom.type.data = tdn = MEM_callocN(tc->data_len * sizeof(TransDataNla), "TransDataNla (NLA Editor)"); + tc->custom.type.use_free = true; /* loop 2: build transdata array */ for (ale = anim_data.first; ale; ale = ale->next) { @@ -3807,11 +3906,11 @@ typedef struct tGPFtransdata { /* This function helps flush transdata written to tempdata into the gp-frames */ void flushTransIntFrameActionData(TransInfo *t) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); tGPFtransdata *tfd = t->custom.type.data; - int i; /* flush data! */ - for (i = 0; i < t->total; i++, tfd++) { + for (int i = 0; i < tc->data_len; i++, tfd++) { *(tfd->sdata) = round_fl_to_int(tfd->val); } } @@ -3967,18 +4066,20 @@ static void createTransActionData(bContext *C, TransInfo *t) ANIM_animdata_freelist(&anim_data); return; } - + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* allocate memory for data */ - t->total = count; + tc->data_len = count; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(Action Editor)"); - t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "transdata2d"); - td = t->data; - td2d = t->data2d; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(Action Editor)"); + tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "transdata2d"); + td = tc->data; + td2d = tc->data_2d; if (ELEM(ac.datatype, ANIMCONT_GPENCIL, ANIMCONT_MASK)) { - t->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata"); - t->custom.type.use_free = true; + tc->custom.type.data = tfd = MEM_callocN(sizeof(tGPFtransdata) * count, "tGPFtransdata"); + tc->custom.type.use_free = true; } /* loop 2: build transdata array */ @@ -4023,7 +4124,7 @@ static void createTransActionData(bContext *C, TransInfo *t) /* calculate distances for proportional editing */ if (is_prop_edit) { - td = t->data; + td = tc->data; for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt; @@ -4365,19 +4466,21 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) ANIM_animdata_freelist(&anim_data); return; } - + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* allocate memory for data */ - t->total = count; + tc->data_len = count; - t->data = MEM_callocN(t->total * sizeof(TransData), "TransData (Graph Editor)"); + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData (Graph Editor)"); /* for each 2d vert a 3d vector is allocated, so that they can be treated just as if they were 3d verts */ - t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D (Graph Editor)"); - t->custom.type.data = MEM_callocN(t->total * sizeof(TransDataGraph), "TransDataGraph"); - t->custom.type.use_free = true; + tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D (Graph Editor)"); + tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataGraph), "TransDataGraph"); + tc->custom.type.use_free = true; - td = t->data; - td2d = t->data2d; - tdg = t->custom.type.data; + td = tc->data; + td2d = tc->data_2d; + tdg = tc->custom.type.data; /* precompute space-conversion matrices for dealing with non-uniform scaling of Graph Editor */ unit_m3(mtx); @@ -4504,7 +4607,7 @@ static void createTransGraphEditData(bContext *C, TransInfo *t) if (is_prop_edit) { /* loop 2: build transdata arrays */ - td = t->data; + td = tc->data; for (ale = anim_data.first; ale; ale = ale->next) { AnimData *adt = ANIM_nla_mapping_get(&ac, ale); @@ -4660,12 +4763,14 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totve TransData *td; int i, j; char *adjusted; - + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* dynamically allocate an array of chars to mark whether an TransData's * pointers have been fixed already, so that we don't override ones that are * already done */ - adjusted = MEM_callocN(t->total, "beztmap_adjusted_map"); + adjusted = MEM_callocN(tc->data_len, "beztmap_adjusted_map"); /* for each beztmap item, find if it is used anywhere */ bezm = bezms; @@ -4673,9 +4778,9 @@ static void beztmap_to_data(TransInfo *t, FCurve *fcu, BeztMap *bezms, int totve /* loop through transdata, testing if we have a hit * for the handles (vec[0]/vec[2]), we must also check if they need to be swapped... */ - td2d = t->data2d; - td = t->data; - for (j = 0; j < t->total; j++, td2d++, td++) { + td2d = tc->data_2d; + td = tc->data; + for (j = 0; j < tc->data_len; j++, td2d++, td++) { /* skip item if already marked */ if (adjusted[j] != 0) continue; @@ -4778,9 +4883,11 @@ void flushTransGraphData(TransInfo *t) double secf = FPS; int a; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d, tdg = t->custom.type.data; - a < t->total; + for (a = 0, td = tc->data, td2d = tc->data_2d, tdg = tc->custom.type.data; + a < tc->data_len; a++, td++, td2d++, tdg++) { AnimData *adt = (AnimData *)td->extra; /* pointers to relevant AnimData blocks are stored in the td->extra pointers */ @@ -5142,13 +5249,15 @@ static void SeqTransDataBounds(TransInfo *t, ListBase *seqbase, TransSeq *ts) } -static void freeSeqData(TransInfo *t, TransCustomData *custom_data) +static void freeSeqData(TransInfo *t, TransDataContainer *tc, TransCustomData *custom_data) { Editing *ed = BKE_sequencer_editing_get(t->scene, false); if (ed != NULL) { + + ListBase *seqbasep = ed->seqbasep; - TransData *td = t->data; + TransData *td = tc->data; int a; /* prevent updating the same seq twice @@ -5176,7 +5285,7 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) { int overlap = 0; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev) && (seq->depth == 0) && (seq->flag & SEQ_OVERLAP)) { overlap = 1; @@ -5189,8 +5298,8 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) for (seq = seqbasep->first; seq; seq = seq->next) seq->tmp = NULL; - td = t->data; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + td = tc->data; + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev)) { /* check effects strips, we cant change their time */ @@ -5213,8 +5322,8 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) if (t->flag & T_ALT_TRANSFORM) { int minframe = MAXFRAME; - td = t->data; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + td = tc->data; + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev) && (seq->depth == 0)) { minframe = min_ii(minframe, seq->startdisp); @@ -5250,8 +5359,8 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) if (has_effect_any) { /* update effects strips based on strips just moved in time */ - td = t->data; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + td = tc->data; + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev)) { if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) { @@ -5263,8 +5372,8 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) if (has_effect_root) { /* now if any effects _still_ overlap, we need to move them up */ - td = t->data; - for (a = 0, seq_prev = NULL; a < t->total; a++, td++, seq_prev = seq) { + td = tc->data; + for (a = 0, seq_prev = NULL; a < tc->data_len; a++, td++, seq_prev = seq) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev) && (seq->depth == 0)) { if ((seq->type & SEQ_TYPE_EFFECT) && seq->seq1) { @@ -5293,7 +5402,7 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) } else { /* Canceled, need to update the strips display */ - for (a = 0; a < t->total; a++, td++) { + for (a = 0; a < tc->data_len; a++, td++) { seq = ((TransDataSeq *)td->extra)->seq; if ((seq != seq_prev) && (seq->depth == 0)) { if (seq->flag & SEQ_OVERLAP) @@ -5312,9 +5421,9 @@ static void freeSeqData(TransInfo *t, TransCustomData *custom_data) MEM_freeN(custom_data->data); custom_data->data = NULL; } - if (t->data) { - MEM_freeN(t->data); // XXX postTrans usually does this - t->data = NULL; + if (tc->data) { + MEM_freeN(tc->data); // XXX postTrans usually does this + tc->data = NULL; } } @@ -5333,12 +5442,14 @@ static void createTransSeqData(bContext *C, TransInfo *t) int count = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + if (ed == NULL) { - t->total = 0; + tc->data_len = 0; return; } - t->custom.type.free_cb = freeSeqData; + tc->custom.type.free_cb = freeSeqData; xmouse = (int)UI_view2d_region_to_view_x(v2d, t->mouse.imval[0]); @@ -5377,18 +5488,18 @@ static void createTransSeqData(bContext *C, TransInfo *t) count = SeqTransCount(t, NULL, ed->seqbasep, 0); /* allocate memory for data */ - t->total = count; + tc->data_len = count; /* stop if trying to build list if nothing selected */ if (count == 0) { return; } - t->custom.type.data = ts = MEM_callocN(sizeof(TransSeq), "transseq"); - t->custom.type.use_free = true; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransSeq TransData"); - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransSeq TransData2D"); - ts->tdseq = tdsq = MEM_callocN(t->total * sizeof(TransDataSeq), "TransSeq TransDataSeq"); + tc->custom.type.data = ts = MEM_callocN(sizeof(TransSeq), "transseq"); + tc->custom.type.use_free = true; + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransSeq TransData"); + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransSeq TransData2D"); + ts->tdseq = tdsq = MEM_callocN(tc->data_len * sizeof(TransDataSeq), "TransSeq TransDataSeq"); /* loop 2: build transdata array */ SeqToTransData_Recursive(t, ed->seqbasep, td, td2d, tdsq); @@ -6118,9 +6229,11 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) { /* so automerge supports mirror */ if ((t->scene->toolsettings->automerge) && - (t->obedit && t->obedit->type == OB_MESH)) + ((t->flag & T_EDIT) && t->obedit_type == OB_MESH)) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); BMesh *bm = em->bm; char hflag; bool has_face_sel = (bm->totfacesel != 0); @@ -6133,7 +6246,7 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) * tag all mirrored verts, then automerge those */ BM_mesh_elem_hflag_disable_all(bm, BM_VERT, BM_ELEM_TAG, false); - for (i = 0, td = t->data; i < t->total; i++, td++) { + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { if (td->extra) { BM_elem_flag_enable((BMVert *)td->extra, BM_ELEM_TAG); } @@ -6145,7 +6258,7 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) hflag = BM_ELEM_SELECT; } - EDBM_automerge(t->scene, t->obedit, true, hflag); + EDBM_automerge(t->scene, tc->obedit, true, hflag); /* Special case, this is needed or faces won't re-select. * Flush selected edges to faces. */ @@ -6153,6 +6266,7 @@ static void special_aftertrans_update__mesh(bContext *UNUSED(C), TransInfo *t) EDBM_selectmode_flush_ex(em, SCE_SELECT_EDGE); } } + } } /* inserting keys, pointcache, redraw events... */ @@ -6168,50 +6282,53 @@ void special_aftertrans_update(bContext *C, TransInfo *t) const bool duplicate = (t->mode == TFM_TIME_DUPLICATE); /* early out when nothing happened */ - if (t->total == 0 || t->mode == TFM_DUMMY) + if (t->data_len_all == 0 || t->mode == TFM_DUMMY) { return; - + } + if (t->spacetype == SPACE_VIEW3D) { - if (t->obedit) { + if (t->flag & T_EDIT) { /* Special Exception: * We don't normally access 't->custom.mode' here, but its needed in this case. */ if (canceled == 0) { /* we need to delete the temporary faces before automerging */ if (t->mode == TFM_EDGE_SLIDE) { - EdgeSlideData *sld = t->custom.mode.data; - /* handle multires re-projection, done * on transform completion since it's * really slow -joeedh */ projectEdgeSlideData(t, true); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + EdgeSlideData *sld = tc->custom.mode.data; + /* free temporary faces to avoid automerging and deleting * during cleanup - psy-fi */ freeEdgeSlideTempFaces(sld); + } } else if (t->mode == TFM_VERT_SLIDE) { /* as above */ - VertSlideData *sld = t->custom.mode.data; projectVertSlideData(t, true); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + VertSlideData *sld = tc->custom.mode.data; freeVertSlideTempFaces(sld); + } } - if (t->obedit->type == OB_MESH) { + if (t->obedit_type == OB_MESH) { special_aftertrans_update__mesh(C, t); } } else { if (t->mode == TFM_EDGE_SLIDE) { - EdgeSlideData *sld = t->custom.mode.data; - - sld->perc = 0.0; + EdgeSlideParams *slp = t->custom.mode.data; + slp->perc = 0.0; projectEdgeSlideData(t, false); } else if (t->mode == TFM_VERT_SLIDE) { - VertSlideData *sld = t->custom.mode.data; - - sld->perc = 0.0; + EdgeSlideParams *slp = t->custom.mode.data; + slp->perc = 0.0; projectVertSlideData(t, false); } } @@ -6485,26 +6602,33 @@ void special_aftertrans_update(bContext *C, TransInfo *t) ED_nla_postop_refresh(&ac); } } - else if (t->obedit) { - if (t->obedit->type == OB_MESH) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); - /* table needs to be created for each edit command, since vertices can move etc */ - ED_mesh_mirror_spatial_table(t->obedit, em, NULL, NULL, 'e'); + else if (t->flag & T_EDIT) { + if (t->obedit_type == OB_MESH) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); + /* table needs to be created for each edit command, since vertices can move etc */ + ED_mesh_mirror_spatial_table(tc->obedit, em, NULL, NULL, 'e'); + /* TODO(campbell): xform: We need support for many mirror objects at once! */ + break; + } } } - else if ((t->flag & T_POSE) && (t->poseobj)) { + else if (t->flag & T_POSE) { + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + bArmature *arm; bPoseChannel *pchan; short targetless_ik = 0; - ob = t->poseobj; + ob = tc->poseobj; arm = ob->data; if ((t->flag & T_AUTOIK) && (t->options & CTX_AUTOCONFIRM)) { /* when running transform non-interactively (operator exec), * we need to update the pose otherwise no updates get called during * transform and the auto-ik is not applied. see [#26164] */ - struct Object *pose_ob = t->poseobj; + struct Object *pose_ob = tc->poseobj; BKE_pose_where_is(&t->eval_ctx, t->scene, pose_ob); } @@ -6542,7 +6666,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t) else { DEG_id_tag_update(&ob->id, OB_RECALC_DATA); } - + } } else if (t->options & CTX_PAINT_CURVE) { /* pass */ @@ -6559,8 +6683,10 @@ void special_aftertrans_update(bContext *C, TransInfo *t) BLI_assert(t->flag & (T_OBJECT | T_TEXTURE)); - for (i = 0; i < t->total; i++) { - TransData *td = t->data + i; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + for (i = 0; i < tc->data_len; i++) { + TransData *td = tc->data + i; ListBase pidlist; PTCacheID *pid; ob = td->ob; @@ -6614,7 +6740,7 @@ int special_transform_moving(TransInfo *t) else if (t->spacetype == SPACE_IPO) { return G_TRANSFORM_FCURVES; } - else if (t->obedit || ((t->flag & T_POSE) && (t->poseobj))) { + else if ((t->flag & T_EDIT) || (t->flag & T_POSE)) { return G_TRANSFORM_EDIT; } else if (t->flag & (T_OBJECT | T_TEXTURE)) { @@ -6632,21 +6758,23 @@ static void createTransObject(bContext *C, TransInfo *t) set_trans_object_base_flags(t); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* count */ - t->total = CTX_DATA_COUNT(C, selected_objects); + tc->data_len = CTX_DATA_COUNT(C, selected_objects); - if (!t->total) { + if (!tc->data_len) { /* clear here, main transform function escapes too */ clear_trans_object_base_flags(t); return; } if (is_prop_edit) { - t->total += count_proportional_objects(t); + tc->data_len += count_proportional_objects(t); } - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransOb"); - tx = t->ext = MEM_callocN(t->total * sizeof(TransDataExtension), "TransObExtension"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransOb"); + tx = tc->data_ext = MEM_callocN(tc->data_len * sizeof(TransDataExtension), "TransObExtension"); CTX_DATA_BEGIN(C, Base *, base, selected_bases) { @@ -6766,7 +6894,9 @@ static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) SpaceNode *snode = t->sa->spacedata.first; bNode *node; - t->total = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + tc->data_len = 0; if (!snode->edittree) { return; @@ -6779,15 +6909,15 @@ static void createTransNodeData(bContext *UNUSED(C), TransInfo *t) for (node = snode->edittree->nodes.first; node; node = node->next) { if (node->flag & NODE_SELECT && is_node_parent_select(node) == false) { node->flag |= NODE_TRANSFORM; - t->total++; + tc->data_len++; } else { node->flag &= ~NODE_TRANSFORM; } } - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransNode TransData"); - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransNode TransData2D"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransNode TransData"); + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransNode TransData2D"); for (node = snode->edittree->nodes.first; node; node = node->next) { if (node->flag & NODE_TRANSFORM) { @@ -6972,7 +7102,7 @@ static void planeTrackToTransData(const int framenr, TransData *td, TransData2D } } -static void transDataTrackingFree(TransInfo *UNUSED(t), TransCustomData *custom_data) +static void transDataTrackingFree(TransInfo *UNUSED(t), TransDataContainer *UNUSED(tc), TransCustomData *custom_data) { if (custom_data->data) { TransDataTracking *tdt = custom_data->data; @@ -6997,22 +7127,24 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) TransDataTracking *tdt; int framenr = ED_space_clip_get_clip_frame_number(sc); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* count */ - t->total = 0; + tc->data_len = 0; track = tracksbase->first; while (track) { if (TRACK_VIEW_SELECTED(sc, track) && (track->flag & TRACK_LOCKED) == 0) { - t->total++; /* offset */ + tc->data_len++; /* offset */ if (track->flag & SELECT) - t->total++; + tc->data_len++; if (track->pat_flag & SELECT) - t->total += 4; + tc->data_len += 4; if (track->search_flag & SELECT) - t->total += 2; + tc->data_len += 2; } track = track->next; @@ -7023,18 +7155,18 @@ static void createTransTrackingTracksData(bContext *C, TransInfo *t) plane_track = plane_track->next) { if (PLANE_TRACK_VIEW_SELECTED(plane_track)) { - t->total += 4; + tc->data_len += 4; } } - if (t->total == 0) + if (tc->data_len == 0) return; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransTracking TransData"); - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransTracking TransData2D"); - tdt = t->custom.type.data = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData"); + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransTracking TransData2D"); + tdt = tc->custom.type.data = MEM_callocN(tc->data_len * sizeof(TransDataTracking), "TransTracking TransDataTracking"); - t->custom.type.free_cb = transDataTrackingFree; + tc->custom.type.free_cb = transDataTrackingFree; /* create actual data */ track = tracksbase->first; @@ -7136,8 +7268,10 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t) BKE_movieclip_get_size(clip, &sc->user, &width, &height); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* count */ - t->total = 0; + tc->data_len = 0; if ((sc->flag & SC_SHOW_GRAPH_TRACKS_MOTION) == 0) { return; @@ -7154,23 +7288,23 @@ static void createTransTrackingCurvesData(bContext *C, TransInfo *t) continue; if (marker->flag & MARKER_GRAPH_SEL_X) - t->total += 1; + tc->data_len += 1; if (marker->flag & MARKER_GRAPH_SEL_Y) - t->total += 1; + tc->data_len += 1; } } track = track->next; } - if (t->total == 0) + if (tc->data_len == 0) return; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransTracking TransData"); - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransTracking TransData2D"); - t->custom.type.data = tdt = MEM_callocN(t->total * sizeof(TransDataTracking), "TransTracking TransDataTracking"); - t->custom.type.free_cb = transDataTrackingFree; + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransTracking TransData"); + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransTracking TransData2D"); + tc->custom.type.data = tdt = MEM_callocN(tc->data_len * sizeof(TransDataTracking), "TransTracking TransDataTracking"); + tc->custom.type.free_cb = transDataTrackingFree; /* create actual data */ track = tracksbase->first; @@ -7211,7 +7345,9 @@ static void createTransTrackingData(bContext *C, TransInfo *t) MovieClip *clip = ED_space_clip_get_clip(sc); int width, height; - t->total = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + tc->data_len = 0; if (!clip) return; @@ -7232,12 +7368,14 @@ static void createTransTrackingData(bContext *C, TransInfo *t) static void cancelTransTracking(TransInfo *t) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); SpaceClip *sc = t->sa->spacedata.first; int i, framenr = ED_space_clip_get_clip_frame_number(sc); - TransDataTracking *tdt_array = t->custom.type.data; + TransDataTracking *tdt_array = tc->custom.type.data; + i = 0; - while (i < t->total) { + while (i < tc->data_len) { TransDataTracking *tdt = &tdt_array[i]; if (tdt->mode == transDataTracking_ModeTracks) { @@ -7294,8 +7432,10 @@ void flushTransTracking(TransInfo *t) if (t->state == TRANS_CANCEL) cancelTransTracking(t); + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data, td2d = t->data2d, tdt = t->custom.type.data; a < t->total; a++, td2d++, td++, tdt++) { + for (a = 0, td = tc->data, td2d = tc->data_2d, tdt = tc->custom.type.data; a < tc->data_len; a++, td2d++, td++, tdt++) { if (tdt->mode == transDataTracking_ModeTracks) { float loc2d[2]; @@ -7561,7 +7701,9 @@ static void createTransMaskingData(bContext *C, TransInfo *t) const bool is_prop_edit = (t->flag & T_PROP_EDIT); float asp[2]; - t->total = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + tc->data_len = 0; if (!mask) return; @@ -7621,13 +7763,13 @@ static void createTransMaskingData(bContext *C, TransInfo *t) ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]); - t->total = (is_prop_edit) ? count : countsel; - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransObData(Mask Editing)"); + tc->data_len = (is_prop_edit) ? count : countsel; + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(Mask Editing)"); /* for each 2d uv coord a 3d vector is allocated, so that they can be * treated just as if they were 3d verts */ - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransObData2D(Mask Editing)"); - t->custom.type.data = tdm = MEM_callocN(t->total * sizeof(TransDataMasking), "TransDataMasking(Mask Editing)"); - t->custom.type.use_free = true; + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransObData2D(Mask Editing)"); + tc->custom.type.data = tdm = MEM_callocN(tc->data_len * sizeof(TransDataMasking), "TransDataMasking(Mask Editing)"); + tc->custom.type.use_free = true; /* create data */ for (masklay = mask->masklayers.first; masklay; masklay = masklay->next) { @@ -7684,12 +7826,14 @@ void flushTransMasking(TransInfo *t) int a; float asp[2], inv[2]; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + ED_mask_get_aspect(t->sa, t->ar, &asp[0], &asp[1]); inv[0] = 1.0f / asp[0]; inv[1] = 1.0f / asp[1]; /* flush to 2d vector from internally used 3d vector */ - for (a = 0, td = t->data2d, tdm = t->custom.type.data; a < t->total; a++, td++, tdm++) { + for (a = 0, td = tc->data_2d, tdm = tc->custom.type.data; a < tc->data_len; a++, td++, tdm++) { td->loc2d[0] = td->loc[0] * inv[0]; td->loc2d[1] = td->loc[1] * inv[1]; mul_m3_v2(tdm->parent_inverse_matrix, td->loc2d); @@ -7808,7 +7952,9 @@ static void createTransPaintCurveVerts(bContext *C, TransInfo *t) int i; int total = 0; - t->total = 0; + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + tc->data_len = 0; if (!paint || !paint->brush || !paint->brush->paint_curve) return; @@ -7834,11 +7980,11 @@ static void createTransPaintCurveVerts(bContext *C, TransInfo *t) if (!total) return; - t->total = total; - td2d = t->data2d = MEM_callocN(t->total * sizeof(TransData2D), "TransData2D"); - td = t->data = MEM_callocN(t->total * sizeof(TransData), "TransData"); - t->custom.type.data = tdpc = MEM_callocN(t->total * sizeof(TransDataPaintCurve), "TransDataPaintCurve"); - t->custom.type.use_free = true; + tc->data_len = total; + td2d = tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransData2D"); + td = tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData"); + tc->custom.type.data = tdpc = MEM_callocN(tc->data_len * sizeof(TransDataPaintCurve), "TransDataPaintCurve"); + tc->custom.type.use_free = true; for (pcp = pc->points, i = 0; i < pc->tot_points; i++, pcp++) { if (PC_IS_ANY_SEL(pcp)) { @@ -7869,10 +8015,13 @@ static void createTransPaintCurveVerts(bContext *C, TransInfo *t) void flushTransPaintCurve(TransInfo *t) { int i; - TransData2D *td2d = t->data2d; - TransDataPaintCurve *tdpc = t->custom.type.data; - for (i = 0; i < t->total; i++, tdpc++, td2d++) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + TransData2D *td2d = tc->data_2d; + TransDataPaintCurve *tdpc = tc->custom.type.data; + + for (i = 0; i < tc->data_len; i++, tdpc++, td2d++) { PaintCurvePoint *pcp = tdpc->pcp; copy_v2_v2(pcp->bez.vec[tdpc->id], td2d->loc); } @@ -7891,15 +8040,16 @@ static void createTransGPencil(bContext *C, TransInfo *t) const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0; const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0; - - + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* == Grease Pencil Strokes to Transform Data == * Grease Pencil stroke points can be a mixture of 2D (screen-space), * or 3D coordinates. However, they're always saved as 3D points. * For now, we just do these without creating TransData2D for the 2D * strokes. This may cause issues in future though. */ - t->total = 0; + tc->data_len = 0; if (gpd == NULL) return; @@ -7928,11 +8078,11 @@ static void createTransGPencil(bContext *C, TransInfo *t) if (is_prop_edit_connected) { /* connected only - so only if selected */ if (gps->flag & GP_STROKE_SELECT) - t->total += gps->totpoints; + tc->data_len += gps->totpoints; } else { /* everything goes - connection status doesn't matter */ - t->total += gps->totpoints; + tc->data_len += gps->totpoints; } } else { @@ -7944,7 +8094,7 @@ static void createTransGPencil(bContext *C, TransInfo *t) // TODO: 2D vs 3D? for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) { if (pt->flag & GP_SPOINT_SELECT) - t->total++; + tc->data_len++; } } } @@ -7953,13 +8103,13 @@ static void createTransGPencil(bContext *C, TransInfo *t) } /* Stop trying if nothing selected */ - if (t->total == 0) { + if (tc->data_len == 0) { return; } /* Allocate memory for data */ - t->data = MEM_callocN(t->total * sizeof(TransData), "TransData(GPencil)"); - td = t->data; + tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(GPencil)"); + td = tc->data; unit_m3(smtx); unit_m3(mtx); @@ -8127,6 +8277,29 @@ static void createTransGPencil(bContext *C, TransInfo *t) } } +static int countAndCleanTransDataContainer(TransInfo *t) +{ + BLI_assert(ELEM(t->data_len_all, 0, -1)); + t->data_len_all = 0; + uint data_container_len_orig = t->data_container_len; + for (TransDataContainer *th_end = t->data_container - 1, *tc = t->data_container + (t->data_container_len - 1); tc != th_end; tc--) { + if (tc->data_len == 0) { + uint index = tc - t->data_container; + if (index + 1 != t->data_container_len) { + SWAP(TransDataContainer, t->data_container[index], t->data_container[t->data_container_len - 1]); + } + t->data_container_len -= 1; + } + else { + t->data_len_all += tc->data_len; + } + } + if (data_container_len_orig != t->data_container_len) { + t->data_container = MEM_reallocN(t->data_container, sizeof(*t->data_container) * t->data_container_len); + } + return t->data_len_all; +} + void createTransData(bContext *C, TransInfo *t) { @@ -8134,16 +8307,27 @@ void createTransData(bContext *C, TransInfo *t) ViewLayer *view_layer = t->view_layer; Object *ob = OBACT(view_layer); + t->data_len_all = -1; + /* if tests must match recalcData for correct updates */ if (t->options & CTX_TEXTURE) { t->flag |= T_TEXTURE; + createTransTexspace(t); + countAndCleanTransDataContainer(t); } else if (t->options & CTX_EDGE) { - t->ext = NULL; + /* Multi object editing. */ + initTransDataContainers_FromObjectData(t); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc->data_ext = NULL; + } t->flag |= T_EDIT; + createTransEdge(t); - if (t->data && t->flag & T_PROP_EDIT) { + countAndCleanTransDataContainer(t); + + if (t->data_len_all && t->flag & T_PROP_EDIT) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8152,9 +8336,11 @@ void createTransData(bContext *C, TransInfo *t) else if (t->options & CTX_GPENCIL_STROKES) { t->options |= CTX_GPENCIL_STROKES; t->flag |= T_POINTS; + createTransGPencil(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8163,22 +8349,32 @@ void createTransData(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_IMAGE) { t->flag |= T_POINTS | T_2D_EDIT; if (t->options & CTX_MASK) { + /* copied from below */ createTransMaskingData(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, true); sort_trans_data_dist(t); } } else if (t->options & CTX_PAINT_CURVE) { - if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) + if (!ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { createTransPaintCurveVerts(C, t); + countAndCleanTransDataContainer(t); + } } - else if (t->obedit) { + else if (t->obedit_type == OB_MESH) { + + initTransDataContainers_FromObjectData(t); createTransUVs(C, t); - if (t->data && (t->flag & T_PROP_EDIT)) { + countAndCleanTransDataContainer(t); + + t->flag |= T_EDIT; + + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8187,9 +8383,11 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_ACTION) { t->flag |= T_POINTS | T_2D_EDIT; + createTransActionData(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array //set_prop_dist(t, false); /* don't do that, distance has been set in createTransActionData already */ sort_trans_data_dist(t); @@ -8198,17 +8396,20 @@ void createTransData(bContext *C, TransInfo *t) else if (t->spacetype == SPACE_NLA) { t->flag |= T_POINTS | T_2D_EDIT; createTransNlaData(C, t); + countAndCleanTransDataContainer(t); } else if (t->spacetype == SPACE_SEQ) { t->flag |= T_POINTS | T_2D_EDIT; t->num.flag |= NUM_NO_FRACTION; /* sequencer has no use for floating point transformations */ createTransSeqData(C, t); + countAndCleanTransDataContainer(t); } else if (t->spacetype == SPACE_IPO) { t->flag |= T_POINTS | T_2D_EDIT; createTransGraphEditData(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, false); /* don't do that, distance has been set in createTransGraphEditData already */ sort_trans_data_dist(t); @@ -8216,8 +8417,11 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_NODE) { t->flag |= T_POINTS | T_2D_EDIT; + createTransNodeData(C, t); - if (t->data && (t->flag & T_PROP_EDIT)) { + countAndCleanTransDataContainer(t); + + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8225,34 +8429,42 @@ void createTransData(bContext *C, TransInfo *t) } else if (t->spacetype == SPACE_CLIP) { t->flag |= T_POINTS | T_2D_EDIT; - if (t->options & CTX_MOVIECLIP) + if (t->options & CTX_MOVIECLIP) { createTransTrackingData(C, t); + countAndCleanTransDataContainer(t); + } else if (t->options & CTX_MASK) { /* copied from above */ createTransMaskingData(C, t); + countAndCleanTransDataContainer(t); - if (t->data && (t->flag & T_PROP_EDIT)) { + if (t->data_len_all && (t->flag & T_PROP_EDIT)) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, true); sort_trans_data_dist(t); } } } - else if (t->obedit) { - t->ext = NULL; - if (t->obedit->type == OB_MESH) { + else if (t->obedit_type != -1) { + /* Multi object editing. */ + initTransDataContainers_FromObjectData(t); + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc->data_ext = NULL; + } + if (t->obedit_type == OB_MESH) { createTransEditVerts(t); } - else if (ELEM(t->obedit->type, OB_CURVE, OB_SURF)) { + else if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) { createTransCurveVerts(t); } - else if (t->obedit->type == OB_LATTICE) { + else if (t->obedit_type == OB_LATTICE) { createTransLatticeVerts(t); } - else if (t->obedit->type == OB_MBALL) { + else if (t->obedit_type == OB_MBALL) { createTransMBallVerts(t); } - else if (t->obedit->type == OB_ARMATURE) { + else if (t->obedit_type == OB_ARMATURE) { t->flag &= ~T_PROP_EDIT; createTransArmatureVerts(t); } @@ -8260,12 +8472,14 @@ void createTransData(bContext *C, TransInfo *t) printf("edit type not implemented!\n"); } + countAndCleanTransDataContainer(t); + t->flag |= T_EDIT | T_POINTS; - if (t->data && t->flag & T_PROP_EDIT) { - if (ELEM(t->obedit->type, OB_CURVE, OB_MESH)) { + if (t->data_len_all && t->flag & T_PROP_EDIT) { + if (ELEM(t->obedit_type, OB_CURVE, OB_MESH)) { sort_trans_data(t); // makes selected become first in array - if ((t->obedit->type == OB_MESH) && (t->flag & T_PROP_CONNECTED)) { + if ((t->obedit_type == OB_MESH) && (t->flag & T_PROP_CONNECTED)) { /* already calculated by editmesh_set_connectivity_distance */ } else { @@ -8284,13 +8498,21 @@ void createTransData(bContext *C, TransInfo *t) if (t->mode == TFM_BONESIZE) { t->flag &= ~(T_EDIT | T_POINTS); t->flag |= T_POSE; - t->poseobj = ob; /* <- tsk tsk, this is going to give issues one day */ + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + tc->poseobj = tc->obedit; + tc->obedit = NULL; + } } } else if (ob && (ob->mode & OB_MODE_POSE)) { // XXX this is currently limited to active armature only... // XXX active-layer checking isn't done as that should probably be checked through context instead - createTransPose(t, ob); + + /* Multi object editing. */ + initTransDataContainers_FromObjectData(t); + createTransPose(t, NULL, 0); + countAndCleanTransDataContainer(t); } else if (ob && (ob->mode & OB_MODE_WEIGHT_PAINT) && !(t->options & CTX_PAINT_CURVE)) { /* important that ob_armature can be set even when its not selected [#23412] @@ -8300,7 +8522,11 @@ void createTransData(bContext *C, TransInfo *t) Base *base_arm = BKE_view_layer_base_find(t->view_layer, ob_armature); if (base_arm) { if (BASE_VISIBLE(base_arm)) { - createTransPose(t, ob_armature); + Object *objects[1]; + objects[0] = ob_armature; + uint objects_len = 1; + createTransPose(t, objects, objects_len); + countAndCleanTransDataContainer(t); } } @@ -8308,9 +8534,10 @@ void createTransData(bContext *C, TransInfo *t) } else if (ob && (ob->mode & OB_MODE_PARTICLE_EDIT) && PE_start_edit(PE_get_current(scene, ob))) { createTransParticleVerts(C, t); + countAndCleanTransDataContainer(t); t->flag |= T_POINTS; - if (t->data && t->flag & T_PROP_EDIT) { + if (t->data_len_all && t->flag & T_PROP_EDIT) { sort_trans_data(t); // makes selected become first in array set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8320,13 +8547,15 @@ void createTransData(bContext *C, TransInfo *t) if ((t->options & CTX_PAINT_CURVE) && !ELEM(t->mode, TFM_SHEAR, TFM_SHRINKFATTEN)) { t->flag |= T_POINTS | T_2D_EDIT; createTransPaintCurveVerts(C, t); + countAndCleanTransDataContainer(t); } } else { createTransObject(C, t); + countAndCleanTransDataContainer(t); t->flag |= T_OBJECT; - if (t->data && t->flag & T_PROP_EDIT) { + if (t->data_len_all && t->flag & T_PROP_EDIT) { // selected objects are already first, no need to presort set_prop_dist(t, 1); sort_trans_data_dist(t); @@ -8344,4 +8573,7 @@ void createTransData(bContext *C, TransInfo *t) } } } + + /* Check that 'countAndCleanTransDataContainer' ran. */ + BLI_assert(t->data_len_all != -1); } diff --git a/source/blender/editors/transform/transform_generics.c b/source/blender/editors/transform/transform_generics.c index 08e8e8fe175..2ab6d0d0900 100644 --- a/source/blender/editors/transform/transform_generics.c +++ b/source/blender/editors/transform/transform_generics.c @@ -82,6 +82,7 @@ #include "BKE_tracking.h" #include "BKE_mask.h" #include "BKE_workspace.h" +#include "BKE_layer.h" #include "DEG_depsgraph.h" @@ -127,8 +128,10 @@ void getViewVector(TransInfo *t, float coord[3], float vec[3]) /* ************************** GENERICS **************************** */ -static void clipMirrorModifier(TransInfo *t, Object *ob) +static void clipMirrorModifier(TransInfo *t) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob = tc->obedit; ModifierData *md = ob->modifiers.first; float tolerance[3] = {0.0f, 0.0f, 0.0f}; int axis = 0; @@ -154,7 +157,6 @@ static void clipMirrorModifier(TransInfo *t, Object *ob) if (axis) { float mtx[4][4], imtx[4][4]; int i; - TransData *td = t->data; if (mmd->mirror_ob) { float obinv[4][4]; @@ -164,7 +166,8 @@ static void clipMirrorModifier(TransInfo *t, Object *ob) invert_m4_m4(imtx, mtx); } - for (i = 0; i < t->total; i++, td++) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { int clip; float loc[3], iloc[3]; @@ -222,16 +225,18 @@ static void clipMirrorModifier(TransInfo *t, Object *ob) } } } + } } /* assumes obedit set to mesh object */ static void editbmesh_apply_to_mirror(TransInfo *t) { - TransData *td = t->data; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; BMVert *eve; int i; - - for (i = 0; i < t->total; i++, td++) { + + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_NOACTION) break; if (td->loc == NULL) @@ -250,6 +255,7 @@ static void editbmesh_apply_to_mirror(TransInfo *t) td->loc[0] = 0; } } + } } /* for the realtime animation recording feature, handle overlapping data */ @@ -432,12 +438,14 @@ static void recalcData_nla(TransInfo *t) Scene *scene = t->scene; double secf = FPS; int i; - + + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + /* for each strip we've got, perform some additional validation of the values that got set before * using RNA to set the value (which does some special operations when setting these values to make * sure that everything works ok) */ - for (i = 0; i < t->total; i++, tdn++) { + for (i = 0; i < tc->data_len; i++, tdn++) { NlaStrip *strip = tdn->strip; PointerRNA strip_ptr; short pExceeded, nExceeded, iter; @@ -654,14 +662,18 @@ static void recalcData_image(TransInfo *t) else if (t->options & CTX_PAINT_CURVE) { flushTransPaintCurve(t); } - else if (t->obedit && t->obedit->type == OB_MESH) { + else if ((t->flag & T_EDIT) && t->obedit_type == OB_MESH) { SpaceImage *sima = t->sa->spacedata.first; flushTransUVs(t); if (sima->flag & SI_LIVE_UNWRAP) ED_uvedit_live_unwrap_re_solve(); - - DEG_id_tag_update(t->obedit->data, 0); + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->data_len) { + DEG_id_tag_update(tc->obedit->data, 0); + } + } } } @@ -716,19 +728,21 @@ static void recalcData_objects(TransInfo *t) { Base *base = t->view_layer->basact; - if (t->obedit) { - if (ELEM(t->obedit->type, OB_CURVE, OB_SURF)) { - Curve *cu = t->obedit->data; - ListBase *nurbs = BKE_curve_editNurbs_get(cu); - Nurb *nu = nurbs->first; - + if (t->obedit_type != -1) { + if (ELEM(t->obedit_type, OB_CURVE, OB_SURF)) { + if (t->state != TRANS_CANCEL) { - clipMirrorModifier(t, t->obedit); + clipMirrorModifier(t); applyProject(t); } - - DEG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ - + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Curve *cu = tc->obedit->data; + ListBase *nurbs = BKE_curve_editNurbs_get(cu); + Nurb *nu = nurbs->first; + + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + if (t->state == TRANS_CANCEL) { while (nu) { BKE_nurb_handles_calc(nu); /* Cant do testhandlesNurb here, it messes up the h1 and h2 flags */ @@ -743,25 +757,28 @@ static void recalcData_objects(TransInfo *t) nu = nu->next; } } + } } - else if (t->obedit->type == OB_LATTICE) { - Lattice *la = t->obedit->data; - + else if (t->obedit_type == OB_LATTICE) { + if (t->state != TRANS_CANCEL) { applyProject(t); } - - DEG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ - - if (la->editlatt->latt->flag & LT_OUTSIDE) outside_lattice(la->editlatt->latt); + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Lattice *la = tc->obedit->data; + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + if (la->editlatt->latt->flag & LT_OUTSIDE) { + outside_lattice(la->editlatt->latt); + } + } } - else if (t->obedit->type == OB_MESH) { - BMEditMesh *em = BKE_editmesh_from_object(t->obedit); + else if (t->obedit_type == OB_MESH) { /* mirror modifier clipping? */ if (t->state != TRANS_CANCEL) { /* apply clipping after so we never project past the clip plane [#25423] */ applyProject(t); - clipMirrorModifier(t, t->obedit); + clipMirrorModifier(t); } if ((t->options & CTX_NO_MIRROR) == 0 && (t->flag & T_MIRROR)) editbmesh_apply_to_mirror(t); @@ -773,26 +790,30 @@ static void recalcData_objects(TransInfo *t) projectVertSlideData(t, false); } - DEG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ - + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + BMEditMesh *em = BKE_editmesh_from_object(tc->obedit); EDBM_mesh_normals_update(em); BKE_editmesh_tessface_calc(em); + } } - else if (t->obedit->type == OB_ARMATURE) { /* no recalc flag, does pose */ - bArmature *arm = t->obedit->data; - ListBase *edbo = arm->edbo; - EditBone *ebo, *ebo_parent; - TransData *td = t->data; - int i; - + else if (t->obedit_type == OB_ARMATURE) { /* no recalc flag, does pose */ + if (t->state != TRANS_CANCEL) { applyProject(t); } - + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + bArmature *arm = tc->obedit->data; + ListBase *edbo = arm->edbo; + EditBone *ebo, *ebo_parent; + TransData *td = tc->data; + int i; + /* Ensure all bones are correctly adjusted */ for (ebo = edbo->first; ebo; ebo = ebo->next) { ebo_parent = (ebo->flag & BONE_CONNECTED) ? ebo->parent : NULL; - + if (ebo_parent) { /* If this bone has a parent tip that has been moved */ if (ebo_parent->flag & BONE_TIPSEL) { @@ -832,7 +853,7 @@ static void recalcData_objects(TransInfo *t) if (!ELEM(t->mode, TFM_BONE_ROLL, TFM_BONE_ENVELOPE, TFM_BONE_ENVELOPE_DIST, TFM_BONESIZE)) { /* fix roll */ - for (i = 0; i < t->total; i++, td++) { + for (i = 0; i < tc->data_len; i++, td++) { if (td->extra) { float vec[3], up_axis[3]; float qrot[4]; @@ -859,23 +880,32 @@ static void recalcData_objects(TransInfo *t) } } } - + if (arm->flag & ARM_MIRROR_EDIT) { - if (t->state != TRANS_CANCEL) - ED_armature_edit_transform_mirror_update(t->obedit); - else - restoreBones(t); + if (t->state != TRANS_CANCEL) { + ED_armature_edit_transform_mirror_update(tc->obedit); + } + else { + restoreBones(tc); + } + } } } else { if (t->state != TRANS_CANCEL) { applyProject(t); } - DEG_id_tag_update(t->obedit->data, 0); /* sets recalc flags */ + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + if (tc->data_len) { + DEG_id_tag_update(tc->obedit->data, 0); /* sets recalc flags */ + } + } } + } - else if ((t->flag & T_POSE) && t->poseobj) { - Object *ob = t->poseobj; + else if (t->flag & T_POSE) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob = tc->poseobj; bArmature *arm = ob->data; /* if animtimer is running, and the object already has animation data, @@ -900,6 +930,7 @@ static void recalcData_objects(TransInfo *t) } else BKE_pose_where_is(&t->eval_ctx, t->scene, ob); + } } else if (base && (base->object->mode & OB_MODE_PARTICLE_EDIT) && PE_get_current(t->scene, base->object)) @@ -915,9 +946,11 @@ static void recalcData_objects(TransInfo *t) if (t->state != TRANS_CANCEL) { applyProject(t); } - - for (i = 0; i < t->total; i++) { - TransData *td = t->data + i; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + + for (i = 0; i < tc->data_len; i++, td++) { Object *ob = td->ob; if (td->flag & TD_NOACTION) @@ -944,6 +977,7 @@ static void recalcData_objects(TransInfo *t) if (t->flag & T_TEXTURE) DEG_id_tag_update(&ob->id, OB_RECALC_DATA); } + } } } @@ -954,7 +988,9 @@ static void recalcData_sequencer(TransInfo *t) int a; Sequence *seq_prev = NULL; - for (a = 0, td = t->data; a < t->total; a++, td++) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + for (a = 0, td = tc->data; a < tc->data_len; a++, td++) { TransDataSeq *tdsq = (TransDataSeq *) td->extra; Sequence *seq = tdsq->seq; @@ -979,8 +1015,10 @@ static void recalcData_sequencer(TransInfo *t) /* force recalculation of triangles during transformation */ static void recalcData_gpencil_strokes(TransInfo *t) { - TransData *td = t->data; - for (int i = 0; i < t->total; i++, td++) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + + TransData *td = tc->data; + for (int i = 0; i < tc->data_len; i++, td++) { bGPDstroke *gps = td->extra; if (gps != NULL) { gps->flag |= GP_STROKE_RECALC_CACHES; @@ -1078,11 +1116,17 @@ void drawLine(TransInfo *t, const float center[3], const float dir[3], char axis */ void resetTransModal(TransInfo *t) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { if (t->mode == TFM_EDGE_SLIDE) { - freeEdgeSlideVerts(t, &t->custom.mode); + freeEdgeSlideVerts(t, tc, &tc->custom.mode); } else if (t->mode == TFM_VERT_SLIDE) { - freeVertSlideVerts(t, &t->custom.mode); + freeVertSlideVerts(t, tc, &tc->custom.mode); + } + else { + /* no need to keep looping... */ + break; + } } } @@ -1105,6 +1149,40 @@ static int initTransInfo_edit_pet_to_flag(const int proportional) } } +void initTransDataContainers_FromObjectData(TransInfo *t) +{ + const eObjectMode object_mode = OBACT(t->view_layer) ? OBACT(t->view_layer)->mode : OB_MODE_OBJECT; + const short object_type = OBACT(t->view_layer) ? OBACT(t->view_layer)->type : -1; + + if ((object_mode & OB_MODE_EDIT) || + ((object_mode & OB_MODE_POSE) && (object_type == OB_ARMATURE))) + { + if (t->data_container) { + MEM_freeN(t->data_container); + } + uint objects_len; + Object **objects = BKE_view_layer_array_from_objects_in_mode( + t->view_layer, &objects_len, { + .object_mode = object_mode, + .no_dup_data = true}); + t->data_container = MEM_callocN(sizeof(*t->data_container) * objects_len, __func__); + t->data_container_len = objects_len; + + for (int i = 0; i < objects_len; i++) { + TransDataContainer *tc = &t->data_container[i]; + if (object_mode & OB_MODE_EDIT) { + tc->obedit = objects[i]; + copy_m3_m4(tc->obedit_mat, tc->obedit->obmat); + normalize_m3(tc->obedit_mat); + } + else if (object_mode & OB_MODE_POSE) { + tc->poseobj = objects[i]; + } + } + MEM_freeN(objects); + } +} + /** * Setup internal data, mouse, vectors * @@ -1118,11 +1196,12 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve Depsgraph *depsgraph = CTX_data_depsgraph(C); Scene *sce = CTX_data_scene(C); ViewLayer *view_layer = CTX_data_view_layer(C); + const eObjectMode object_mode = OBACT(view_layer) ? OBACT(view_layer)->mode : OB_MODE_OBJECT; + const short object_type = OBACT(view_layer) ? OBACT(view_layer)->type : -1; ToolSettings *ts = CTX_data_tool_settings(C); ARegion *ar = CTX_wm_region(C); ScrArea *sa = CTX_wm_area(C); - Object *obedit = CTX_data_edit_object(C); - Object *ob = CTX_data_active_object(C); + bGPdata *gpd = CTX_data_gpencil_data(C); RenderEngineType *engine_type = CTX_data_engine_type(C); PropertyRNA *prop; @@ -1133,22 +1212,21 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->engine_type = engine_type; t->sa = sa; t->ar = ar; - t->obedit = obedit; t->settings = ts; t->reports = op ? op->reports : NULL; - if (obedit) { - copy_m3_m4(t->obedit_mat, obedit->obmat); - normalize_m3(t->obedit_mat); - } - - t->data = NULL; - t->ext = NULL; - t->helpline = HLP_NONE; t->flag = 0; - + + t->obedit_type = (object_mode == OB_MODE_EDIT) ? object_type : -1; + + /* Many kinds of transform only use a single handle. */ + if (t->data_container == NULL) { + t->data_container = MEM_callocN(sizeof(*t->data_container), __func__); + t->data_container_len = 1; + } + t->redraw = TREDRAW_HARD; /* redraw first time */ if (event) { @@ -1169,12 +1247,11 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->transform = NULL; t->handleEvent = NULL; - t->total = 0; + t->data_len_all = 0; t->val = 0.0f; zero_v3(t->vec); - zero_v3(t->center); zero_v3(t->center_global); unit_m3(t->mat); @@ -1260,13 +1337,13 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve if (ELEM(t->mode, TFM_ROTATION, TFM_RESIZE, TFM_TRACKBALL)) { const bool use_island = transdata_check_local_islands(t, t->around); - if (obedit && !use_island) { + if ((t->obedit_type != -1) && !use_island) { t->options |= CTX_NO_PET; } } } - if (ob && ob->mode & OB_MODE_ALL_PAINT) { + if (object_mode & OB_MODE_ALL_PAINT) { Paint *p = BKE_paint_get_active_from_context(C); if (p && p->brush && (p->brush->flag & BRUSH_CURVE)) { t->options |= CTX_PAINT_CURVE; @@ -1295,7 +1372,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve t->view = &ar->v2d; t->around = sima->around; - if (ED_space_image_show_uvedit(sima, t->obedit)) { + if (ED_space_image_show_uvedit(sima, OBACT(t->view_layer))) { /* UV transform */ } else if (sima->mode == SI_MODE_MASK) { @@ -1385,7 +1462,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } // Need stuff to take it from edit mesh or whatnot here else if (t->spacetype == SPACE_VIEW3D) { - if (t->obedit && t->obedit->type == OB_MESH && (((Mesh *)t->obedit->data)->editflag & ME_EDIT_MIRROR_X)) { + /* TODO(campbell): xform, get mirror from each object. */ + if (t->obedit_type == OB_MESH && (((Mesh *)OBACT(t->view_layer)->data)->editflag & ME_EDIT_MIRROR_X)) { t->flag |= T_MIRROR; t->mirror = 1; } @@ -1406,7 +1484,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve else if (t->spacetype == SPACE_ACTION) { t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional_action); } - else if (t->obedit) { + else if (t->obedit_type != -1) { t->flag |= initTransInfo_edit_pet_to_flag(ts->proportional); } else if (t->options & CTX_GPENCIL_STROKES) { @@ -1421,7 +1499,7 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve } } } - else if (t->obedit == NULL && ts->proportional_objects) { + else if ((t->obedit_type == -1) && ts->proportional_objects) { t->flag |= T_PROP_EDIT; } } @@ -1467,8 +1545,8 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve setTransformViewAspect(t, t->aspect); if (op && (prop = RNA_struct_find_property(op->ptr, "center_override")) && RNA_property_is_set(op->ptr, prop)) { - RNA_property_float_get_array(op->ptr, prop, t->center); - mul_v3_v3(t->center, t->aspect); + RNA_property_float_get_array(op->ptr, prop, t->center_global); + mul_v3_v3(t->center_global, t->aspect); t->flag |= T_OVERRIDE_CENTER; } @@ -1476,11 +1554,25 @@ void initTransInfo(bContext *C, TransInfo *t, wmOperator *op, const wmEvent *eve initNumInput(&t->num); } +static void freeTransCustomDataContainer(TransInfo *t, TransDataContainer *tc, TransCustomDataContainer *tcdc) +{ + TransCustomData *custom_data = &tcdc->first_elem; + for (int i = 0; i < TRANS_CUSTOM_DATA_ELEM_MAX; i++, custom_data++) { + if (custom_data->free_cb) { + /* Can take over freeing t->data and data_2d etc... */ + custom_data->free_cb(t, tc, custom_data); + BLI_assert(custom_data->data == NULL); + } + else if ((custom_data->data != NULL) && custom_data->use_free) { + MEM_freeN(custom_data->data); + custom_data->data = NULL; + } + } +} + /* Here I would suggest only TransInfo related issues, like free data & reset vars. Not redraws */ void postTrans(bContext *C, TransInfo *t) { - TransData *td; - if (t->draw_handle_view) ED_region_draw_cb_exit(t->ar->type, t->draw_handle_view); if (t->draw_handle_apply) @@ -1491,46 +1583,37 @@ void postTrans(bContext *C, TransInfo *t) WM_paint_cursor_end(CTX_wm_manager(C), t->draw_handle_cursor); /* Free all custom-data */ - { - TransCustomData *custom_data = &t->custom.first_elem; - for (int i = 0; i < TRANS_CUSTOM_DATA_ELEM_MAX; i++, custom_data++) { - if (custom_data->free_cb) { - /* Can take over freeing t->data and data2d etc... */ - custom_data->free_cb(t, custom_data); - BLI_assert(custom_data->data == NULL); - } - else if ((custom_data->data != NULL) && custom_data->use_free) { - MEM_freeN(custom_data->data); - custom_data->data = NULL; - } - } + freeTransCustomDataContainer(t, NULL, &t->custom); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + freeTransCustomDataContainer(t, tc, &tc->custom); } /* postTrans can be called when nothing is selected, so data is NULL already */ - if (t->data) { - - /* free data malloced per trans-data */ - if ((t->obedit && ELEM(t->obedit->type, OB_CURVE, OB_SURF)) || - (t->spacetype == SPACE_IPO)) - { - int a; - for (a = 0, td = t->data; a < t->total; a++, td++) { - if (td->flag & TD_BEZTRIPLE) { - MEM_freeN(td->hdata); + if (t->data_len_all != 0) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + /* free data malloced per trans-data */ + if (ELEM(t->obedit_type, OB_CURVE, OB_SURF) || + (t->spacetype == SPACE_IPO)) + { + TransData *td = tc->data; + for (int a = 0; a < tc->data_len; a++, td++) { + if (td->flag & TD_BEZTRIPLE) { + MEM_freeN(td->hdata); + } } } + MEM_freeN(tc->data); + + MEM_SAFE_FREE(tc->data_ext); + MEM_SAFE_FREE(tc->data_2d); } - MEM_freeN(t->data); } + MEM_SAFE_FREE(t->data_container); + t->data_container = NULL; + BLI_freelistN(&t->tsnap.points); - if (t->ext) MEM_freeN(t->ext); - if (t->data2d) { - MEM_freeN(t->data2d); - t->data2d = NULL; - } - if (t->spacetype == SPACE_IMAGE) { if (t->options & (CTX_MASK | CTX_PAINT_CURVE)) { /* pass */ @@ -1558,9 +1641,11 @@ void postTrans(bContext *C, TransInfo *t) void applyTransObjects(TransInfo *t) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t); + TransData *td; - - for (td = t->data; td < t->data + t->total; td++) { + + for (td = tc->data; td < tc->data + tc->data_len; td++) { copy_v3_v3(td->iloc, td->loc); if (td->ext->rot) { copy_v3_v3(td->ext->irot, td->ext->rot); @@ -1609,14 +1694,16 @@ static void restoreElement(TransData *td) void restoreTransObjects(TransInfo *t) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td; TransData2D *td2d; - for (td = t->data; td < t->data + t->total; td++) { + for (td = tc->data; td < tc->data + tc->data_len; td++) { restoreElement(td); } - for (td2d = t->data2d; t->data2d && td2d < t->data2d + t->total; td2d++) { + for (td2d = tc->data_2d; tc->data_2d && td2d < tc->data_2d + tc->data_len; td2d++) { if (td2d->h1) { td2d->h1[0] = td2d->ih1[0]; td2d->h1[1] = td2d->ih1[1]; @@ -1628,6 +1715,8 @@ void restoreTransObjects(TransInfo *t) } unit_m3(t->mat); + + } recalcData(t); } @@ -1635,32 +1724,26 @@ void restoreTransObjects(TransInfo *t) void calculateCenter2D(TransInfo *t) { BLI_assert(!is_zero_v3(t->aspect)); - - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - float vec[3]; - - copy_v3_v3(vec, t->center); - mul_m4_v3(ob->obmat, vec); - projectFloatView(t, vec, t->center2d); - } - else { - projectFloatView(t, t->center, t->center2d); - } + projectFloatView(t, t->center_global, t->center2d); } -void calculateCenterGlobal( - TransInfo *t, const float center_local[3], - float r_center_global[3]) +void calculateCenterLocal( + TransInfo *t, const float center_global[3]) { /* setting constraint center */ /* note, init functions may over-ride t->center */ if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_v3_m4v3(r_center_global, ob->obmat, center_local); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + float obinv[4][4]; + Object *ob = tc->obedit ? tc->obedit : tc->poseobj; + invert_m4_m4(obinv, ob->obmat); + mul_v3_m4v3(tc->center_local, obinv, center_global); + } } else { - copy_v3_v3(r_center_global, center_local); + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + copy_v3_v3(tc->center_local, center_global); + } } } @@ -1672,16 +1755,7 @@ void calculateCenterCursor(TransInfo *t, float r_center[3]) copy_v3_v3(r_center, cursor); /* If edit or pose mode, move cursor in local space */ - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - float mat[3][3], imat[3][3]; - - sub_v3_v3v3(r_center, r_center, ob->obmat[3]); - copy_m3_m4(mat, ob->obmat); - invert_m3_m3(imat, mat); - mul_m3_v3(imat, r_center); - } - else if (t->options & CTX_PAINT_CURVE) { + if (t->options & CTX_PAINT_CURVE) { if (ED_view3d_project_float_global(t->ar, cursor, r_center, V3D_PROJ_TEST_NOP) != V3D_PROJ_RET_OK) { r_center[0] = t->ar->winx / 2.0f; r_center[1] = t->ar->winy / 2.0f; @@ -1755,16 +1829,26 @@ void calculateCenterMedian(TransInfo *t, float r_center[3]) { float partial[3] = {0.0f, 0.0f, 0.0f}; int total = 0; + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob_xform = tc->obedit ? tc->obedit : tc->poseobj; int i; - - for (i = 0; i < t->total; i++) { - if (t->data[i].flag & TD_SELECTED) { - if (!(t->data[i].flag & TD_NOCENTER)) { - add_v3_v3(partial, t->data[i].center); + for (i = 0; i < tc->data_len; i++) { + if (tc->data[i].flag & TD_SELECTED) { + if (!(tc->data[i].flag & TD_NOCENTER)) { + if (ob_xform) { + float v[3]; + mul_v3_m4v3(v, ob_xform->obmat, tc->data[i].center); + add_v3_v3(partial, v); + } + else { + add_v3_v3(partial, tc->data[i].center); + } total++; } } } + } if (total) { mul_v3_fl(partial, 1.0f / (float)total); } @@ -1776,18 +1860,31 @@ void calculateCenterBound(TransInfo *t, float r_center[3]) float max[3]; float min[3]; int i; - for (i = 0; i < t->total; i++) { - if (i) { - if (t->data[i].flag & TD_SELECTED) { - if (!(t->data[i].flag & TD_NOCENTER)) - minmax_v3v3_v3(min, max, t->data[i].center); + bool is_first = true; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob_xform = tc->obedit ? tc->obedit : tc->poseobj; + for (i = 0; i < tc->data_len; i++) { + if (is_first == false) { + if (tc->data[i].flag & TD_SELECTED) { + if (!(tc->data[i].flag & TD_NOCENTER)) { + if (ob_xform) { + float v[3]; + mul_v3_m4v3(v, ob_xform->obmat, tc->data[i].center); + minmax_v3v3_v3(min, max, v); + } + else { + minmax_v3v3_v3(min, max, tc->data[i].center); + } + } } + is_first = false; } else { - copy_v3_v3(max, t->data[i].center); - copy_v3_v3(min, t->data[i].center); + copy_v3_v3(max, tc->data[i].center); + copy_v3_v3(min, tc->data[i].center); } } + } mid_v3_v3v3(r_center, min, max); } @@ -1796,10 +1893,13 @@ void calculateCenterBound(TransInfo *t, float r_center[3]) */ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3]) { + TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_OK(t); + bool ok = false; - if (t->obedit) { - if (ED_object_editmode_calc_active_center(t->obedit, select_only, r_center)) { + if (tc->obedit) { + if (ED_object_editmode_calc_active_center(tc->obedit, select_only, r_center)) { + mul_m4_v3(tc->obedit->obmat, r_center); ok = true; } } @@ -1810,6 +1910,7 @@ bool calculateCenterActive(TransInfo *t, bool select_only, float r_center[3]) bPoseChannel *pchan = BKE_pose_channel_active(ob); if (pchan && (!select_only || (pchan->bone->flag & BONE_SELECTED))) { copy_v3_v3(r_center, pchan->pose_head); + mul_m4_v3(tc->obedit->obmat, r_center); ok = true; } } @@ -1874,14 +1975,13 @@ static void calculateCenter_FromAround(TransInfo *t, int around, float r_center[ void calculateCenter(TransInfo *t) { if ((t->flag & T_OVERRIDE_CENTER) == 0) { - calculateCenter_FromAround(t, t->around, t->center); + calculateCenter_FromAround(t, t->around, t->center_global); } - calculateCenterGlobal(t, t->center, t->center_global); + calculateCenterLocal(t, t->center_global); /* avoid calculating again */ { TransCenterData *cd = &t->center_cache[t->around]; - copy_v3_v3(cd->local, t->center); copy_v3_v3(cd->global, t->center_global); cd->is_set = true; } @@ -1899,16 +1999,15 @@ void calculateCenter(TransInfo *t) normalize_v3(axis); /* 6.0 = 6 grid units */ - axis[0] = t->center[0] - 6.0f * axis[0]; - axis[1] = t->center[1] - 6.0f * axis[1]; - axis[2] = t->center[2] - 6.0f * axis[2]; + axis[0] = t->center_global[0] - 6.0f * axis[0]; + axis[1] = t->center_global[1] - 6.0f * axis[1]; + axis[2] = t->center_global[2] - 6.0f * axis[2]; projectFloatView(t, axis, t->center2d); /* rotate only needs correct 2d center, grab needs ED_view3d_calc_zfac() value */ if (t->mode == TFM_TRANSLATION) { - copy_v3_v3(t->center, axis); - copy_v3_v3(t->center_global, t->center); + copy_v3_v3(t->center_global, axis); } } } @@ -1942,8 +2041,7 @@ const TransCenterData *transformCenter_from_type(TransInfo *t, int around) BLI_assert(around <= V3D_AROUND_ACTIVE); TransCenterData *cd = &t->center_cache[around]; if (cd->is_set == false) { - calculateCenter_FromAround(t, around, cd->local); - calculateCenterGlobal(t, cd->local, cd->global); + calculateCenter_FromAround(t, around, cd->global); cd->is_set = true; } return cd; @@ -1951,7 +2049,6 @@ const TransCenterData *transformCenter_from_type(TransInfo *t, int around) void calculatePropRatio(TransInfo *t) { - TransData *td = t->data; int i; float dist; const bool connected = (t->flag & T_PROP_CONNECTED) != 0; @@ -1960,7 +2057,9 @@ void calculatePropRatio(TransInfo *t) if (t->flag & T_PROP_EDIT) { const char *pet_id = NULL; - for (i = 0; i < t->total; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { if (td->flag & TD_SELECTED) { td->factor = 1.0f; } @@ -2029,6 +2128,8 @@ void calculatePropRatio(TransInfo *t) } } } + } + switch (t->prop_mode) { case PROP_SHARP: pet_id = N_("(Sharp)"); @@ -2063,8 +2164,11 @@ void calculatePropRatio(TransInfo *t) } } else { - for (i = 0; i < t->total; i++, td++) { - td->factor = 1.0; + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + TransData *td = tc->data; + for (i = 0; i < tc->data_len; i++, td++) { + td->factor = 1.0; + } } } } diff --git a/source/blender/editors/transform/transform_snap.c b/source/blender/editors/transform/transform_snap.c index 5cb3f262ced..f128b903183 100644 --- a/source/blender/editors/transform/transform_snap.c +++ b/source/blender/editors/transform/transform_snap.c @@ -272,17 +272,19 @@ void applyProject(TransInfo *t) { /* XXX FLICKER IN OBJECT MODE */ if ((t->tsnap.project) && activeSnap(t) && (t->flag & T_NO_PROJECT) == 0) { - TransData *td = t->data; float tvec[3]; - float imat[4][4]; int i; - + + FOREACH_TRANS_DATA_CONTAINER(t, tc) { + TransData *td = tc->data; + + float imat[4][4]; if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; + Object *ob = tc->obedit ? tc->obedit : tc->poseobj; invert_m4_m4(imat, ob->obmat); } - for (i = 0; i < t->total; i++, td++) { + for (i = 0; i < tc->data_len; i++, td++) { float iloc[3], loc[3], no[3]; float mval_fl[2]; float dist_px = TRANSFORM_DIST_MAX_PX; @@ -298,7 +300,7 @@ void applyProject(TransInfo *t) copy_v3_v3(iloc, td->loc); if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; + Object *ob = tc->obedit ? tc->obedit : tc->poseobj; mul_m4_v3(ob->obmat, iloc); } else if (t->flag & T_OBJECT) { @@ -340,6 +342,7 @@ void applyProject(TransInfo *t) //XXX constraintTransLim(t, td); } + } } } @@ -347,7 +350,6 @@ void applyGridAbsolute(TransInfo *t) { float grid_size = 0.0f; GearsType grid_action; - TransData *td; float (*obmat)[4] = NULL; bool use_obmat = false; int i; @@ -368,13 +370,16 @@ void applyGridAbsolute(TransInfo *t) if (grid_size == 0.0f) return; + FOREACH_TRANS_DATA_CONTAINER(t, tc) { + TransData *td; + if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; + Object *ob = tc->obedit ? tc->obedit : tc->poseobj; obmat = ob->obmat; use_obmat = true; } - for (i = 0, td = t->data; i < t->total; i++, td++) { + for (i = 0, td = tc->data; i < tc->data_len; i++, td++) { float iloc[3], loc[3], tvec[3]; if (td->flag & TD_NOACTION) @@ -405,6 +410,7 @@ void applyGridAbsolute(TransInfo *t) mul_m3_v3(td->smtx, tvec); add_v3_v3(td->loc, tvec); } + } } void applySnapping(TransInfo *t, float *vec) @@ -501,7 +507,8 @@ static bool bm_face_is_snap_target(BMFace *f, void *UNUSED(user_data)) static void initSnappingMode(TransInfo *t) { ToolSettings *ts = t->settings; - Object *obedit = t->obedit; + /* All obedit types will match. */ + const int obedit_type = t->data_container->obedit ? t->data_container->obedit->type : -1; ViewLayer *view_layer = t->view_layer; Base *base_act = view_layer->basact; @@ -532,10 +539,10 @@ static void initSnappingMode(TransInfo *t) /* Edit mode */ if (t->tsnap.applySnap != NULL && // A snapping function actually exist - (obedit != NULL && ELEM(obedit->type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs + ((obedit_type != -1) && ELEM(obedit_type, OB_MESH, OB_ARMATURE, OB_CURVE, OB_LATTICE, OB_MBALL)) ) // Temporary limited to edit mode meshes, armature, curves, mballs { /* Exclude editmesh if using proportional edit */ - if ((obedit->type == OB_MESH) && (t->flag & T_PROP_EDIT)) { + if ((obedit_type == OB_MESH) && (t->flag & T_PROP_EDIT)) { t->tsnap.modeSelect = SNAP_NOT_ACTIVE; } else { @@ -544,13 +551,13 @@ static void initSnappingMode(TransInfo *t) } /* Particles edit mode*/ else if (t->tsnap.applySnap != NULL && // A snapping function actually exist - (obedit == NULL && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT)) + ((obedit_type == -1) && base_act && base_act->object && base_act->object->mode & OB_MODE_PARTICLE_EDIT)) { t->tsnap.modeSelect = SNAP_ALL; } /* Object mode */ else if (t->tsnap.applySnap != NULL && // A snapping function actually exist - (obedit == NULL) ) // Object Mode + (obedit_type == -1) ) // Object Mode { /* In "Edit Strokes" mode, Snap tool can perform snap to selected or active objects (see T49632) * TODO: perform self snap in gpencil_strokes */ @@ -863,7 +870,8 @@ static float TranslationBetween(TransInfo *UNUSED(t), const float p1[3], const f return len_squared_v3v3(p1, p2); } -static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]) +static float RotationBetween( + TransInfo *t, const float p1[3], const float p2[3]) { float angle, start[3], end[3]; @@ -874,7 +882,7 @@ static float RotationBetween(TransInfo *t, const float p1[3], const float p2[3]) if (t->con.applyRot != NULL && (t->con.mode & CON_APPLY)) { float axis[3], tmp[3]; - t->con.applyRot(t, NULL, axis, NULL); + t->con.applyRot(t, NULL, NULL, axis, NULL); project_v3_v3v3(tmp, end, axis); sub_v3_v3v3(end, end, tmp); @@ -977,14 +985,14 @@ static void CalcSnapGeometry(TransInfo *t, float *UNUSED(vec)) t->tsnap.status &= ~POINT_INIT; } } - else if (t->spacetype == SPACE_IMAGE && t->obedit != NULL && t->obedit->type == OB_MESH) { + else if (t->spacetype == SPACE_IMAGE && t->obedit_type == OB_MESH) { /* same as above but for UV's */ Image *ima = ED_space_image(t->sa->spacedata.first); float co[2]; UI_view2d_region_to_view(&t->ar->v2d, t->mval[0], t->mval[1], &co[0], &co[1]); - if (ED_uvedit_nearest_uv(t->scene, t->obedit, ima, co, t->tsnap.snapPoint)) { + if (ED_uvedit_nearest_uv(t->scene, TRANS_DATA_CONTAINER_FIRST_EVIL(t)->obedit, ima, co, t->tsnap.snapPoint)) { t->tsnap.snapPoint[0] *= t->aspect[0]; t->tsnap.snapPoint[1] *= t->aspect[1]; @@ -1059,11 +1067,6 @@ static void TargetSnapActive(TransInfo *t) /* Only need to calculate once */ if ((t->tsnap.status & TARGET_INIT) == 0) { if (calculateCenterActive(t, true, t->tsnap.snapTarget)) { - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, t->tsnap.snapTarget); - } - TargetSnapOffset(t, NULL); t->tsnap.status |= TARGET_INIT; @@ -1081,23 +1084,34 @@ static void TargetSnapMedian(TransInfo *t) { // Only need to calculate once if ((t->tsnap.status & TARGET_INIT) == 0) { - TransData *td = NULL; - int i; + int i_accum = 0; t->tsnap.snapTarget[0] = 0; t->tsnap.snapTarget[1] = 0; t->tsnap.snapTarget[2] = 0; - - for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) { - add_v3_v3(t->tsnap.snapTarget, td->center); - } - - mul_v3_fl(t->tsnap.snapTarget, 1.0 / i); - - if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; - mul_m4_v3(ob->obmat, t->tsnap.snapTarget); + + FOREACH_TRANS_DATA_CONTAINER (t, tc) { + Object *ob_xform = NULL; + if (t->flag & (T_EDIT | T_POSE)) { + ob_xform = tc->obedit ? tc->obedit : tc->poseobj; + } + TransData *td = tc->data; + int i; + for (i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) { + /* TODO(campbell): perform the global transformation once per TransDataContainer */ + if (ob_xform) { + float v[3]; + mul_v3_m4v3(v, ob_xform->obmat, td->center); + add_v3_v3(t->tsnap.snapTarget, v); + } + else { + add_v3_v3(t->tsnap.snapTarget, td->center); + } + } + i_accum += i; } + + mul_v3_fl(t->tsnap.snapTarget, 1.0 / i_accum); TargetSnapOffset(t, NULL); @@ -1110,12 +1124,14 @@ static void TargetSnapClosest(TransInfo *t) // Only valid if a snap point has been selected if (t->tsnap.status & POINT_INIT) { float dist_closest = 0.0f; - TransData *closest = NULL, *td = NULL; + TransData *closest = NULL; /* Object mode */ if (t->flag & T_OBJECT) { int i; - for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) { + FOREACH_TRANS_DATA_CONTAINER(t, tc) { + TransData *td = tc->data; + for (td = tc->data, i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) { struct BoundBox *bb = BKE_object_boundbox_get(td->ob); /* use boundbox if possible */ @@ -1157,17 +1173,20 @@ static void TargetSnapClosest(TransInfo *t) } } } + } } else { + FOREACH_TRANS_DATA_CONTAINER(t, tc) { + TransData *td = tc->data; int i; - for (td = t->data, i = 0; i < t->total && td->flag & TD_SELECTED; i++, td++) { + for (i = 0; i < tc->data_len && td->flag & TD_SELECTED; i++, td++) { float loc[3]; float dist; copy_v3_v3(loc, td->center); if (t->flag & (T_EDIT | T_POSE)) { - Object *ob = t->obedit ? t->obedit : t->poseobj; + Object *ob = tc->obedit ? tc->obedit : tc->poseobj; mul_m4_v3(ob->obmat, loc); } @@ -1181,6 +1200,7 @@ static void TargetSnapClosest(TransInfo *t) dist_closest = dist; } } + } } TargetSnapOffset(t, closest); diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index d8b194e3336..0b77006afb7 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -36,6 +36,7 @@ #include "CLG_log.h" #include "DNA_scene_types.h" +#include "DNA_object_types.h" #include "BLI_utildefines.h" @@ -46,6 +47,7 @@ #include "BKE_global.h" #include "BKE_main.h" #include "BKE_screen.h" +#include "BKE_layer.h" #include "BKE_undo_system.h" #include "ED_gpencil.h" @@ -495,3 +497,24 @@ void ED_OT_undo_history(wmOperatorType *ot) } /** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Undo Helper Functions + * \{ */ + +void ED_undo_object_set_active_or_warn(ViewLayer *view_layer, Object *ob, const char *info, CLG_LogRef *log) +{ + Object *ob_prev = OBACT(view_layer); + if (ob_prev != ob) { + Base *base = BKE_view_layer_base_find(view_layer, ob); + if (base != NULL) { + view_layer->basact = base; + } + else { + /* Should never fail, may not crash but can give odd behavior. */ + CLOG_WARN(log, "'%s' failed to restore active object: '%s'", info, ob->id.name + 2); + } + } +} + +/** \} */ diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index 3fcc89d0973..328ab3f1a8d 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -51,6 +51,7 @@ #include "BKE_DerivedMesh.h" #include "BKE_editmesh.h" #include "BKE_material.h" +#include "BKE_layer.h" #include "BKE_scene.h" @@ -1100,12 +1101,21 @@ void ED_uvedit_draw_main( draw_uv_shadows_get(sima, obact, obedit, &show_uvshadow, &show_texpaint_uvshadow); if (show_uvedit || show_uvshadow || show_texpaint_uvshadow) { - if (show_uvshadow) + if (show_uvshadow) { draw_uvs_shadow(obedit); - else if (show_uvedit) - draw_uvs(sima, scene, view_layer, obedit, depsgraph); - else + } + else if (show_uvedit) { + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *ob_iter = objects[ob_index]; + draw_uvs(sima, scene, view_layer, ob_iter, depsgraph); + } + MEM_SAFE_FREE(objects); + } + else { draw_uvs_texpaint(sima, scene, view_layer, obact); + } if (show_uvedit && !(toolsettings->use_uv_sculpt)) ED_image_draw_cursor(ar, sima->cursor); diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index c5f16d6fb14..e3db0162f10 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -51,6 +51,8 @@ void uv_poly_center(struct BMFace *f, float r_cent[2], const int cd_loop_uv_off /* find nearest */ typedef struct UvNearestHit { + /** Only for `*_multi(..)` versions of functions. */ + struct Object *ob; /** Always set if we have a hit. */ struct BMFace *efa; struct BMLoop *l; @@ -66,14 +68,23 @@ typedef struct UvNearestHit { bool uv_find_nearest_vert( struct Scene *scene, struct Image *ima, struct Object *obedit, const float co[2], const float penalty_dist, struct UvNearestHit *hit_final); +bool uv_find_nearest_vert_multi( + struct Scene *scene, struct Image *ima, struct Object **objects, const uint objects_len, + const float co[2], const float penalty_dist, struct UvNearestHit *hit_final); bool uv_find_nearest_edge( struct Scene *scene, struct Image *ima, struct Object *obedit, const float co[2], struct UvNearestHit *hit_final); +bool uv_find_nearest_edge_multi( + struct Scene *scene, struct Image *ima, struct Object **objects, const uint objects_len, + const float co[2], struct UvNearestHit *hit_final); bool uv_find_nearest_face( struct Scene *scene, struct Image *ima, struct Object *obedit, const float co[2], struct UvNearestHit *hit_final); +bool uv_find_nearest_face_multi( + struct Scene *scene, struct Image *ima, struct Object **objects, const uint objects_len, + const float co[2], struct UvNearestHit *hit_final); /* utility tool functions */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 9df0c7c89ed..c2e8c2b5786 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -66,6 +66,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_editmesh.h" +#include "BKE_layer.h" #include "DEG_depsgraph.h" @@ -89,7 +90,10 @@ #include "uvedit_intern.h" -static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, BMEditMesh *em, int action); +static bool uv_select_is_any_selected(Scene *scene, Image *ima, Object *obedit); +static bool uv_select_is_any_selected_multi(Scene *scene, Image *ima, Object **objects, const uint objects_len); +static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, int action); +static void uv_select_all_perform_multi(Scene *scene, Image *ima, Object **objects, const uint objects_len, int action); static void uv_select_flush_from_tag_face(SpaceImage *sima, Scene *scene, Object *obedit, const bool select); static void uv_select_flush_from_tag_loop(SpaceImage *sima, Scene *scene, Object *obedit, const bool select); @@ -794,6 +798,21 @@ bool uv_find_nearest_edge( return found; } +bool uv_find_nearest_edge_multi( + Scene *scene, Image *ima, Object **objects, const uint objects_len, + const float co[2], UvNearestHit *hit_final) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (uv_find_nearest_edge(scene, ima, obedit, co, hit_final)) { + hit_final->ob = obedit; + found = true; + } + } + return found; +} + bool uv_find_nearest_face( Scene *scene, Image *ima, Object *obedit, const float co[2], UvNearestHit *hit_final) @@ -837,6 +856,21 @@ bool uv_find_nearest_face( return found; } +bool uv_find_nearest_face_multi( + Scene *scene, Image *ima, Object **objects, const uint objects_len, + const float co[2], UvNearestHit *hit_final) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (uv_find_nearest_face(scene, ima, obedit, co, hit_final)) { + hit_final->ob = obedit; + found = true; + } + } + return found; +} + static bool uv_nearest_between( const BMLoop *l, const float co[2], const int cd_loop_uv_offset) @@ -918,6 +952,21 @@ bool uv_find_nearest_vert( return found; } +bool uv_find_nearest_vert_multi( + Scene *scene, Image *ima, Object **objects, const uint objects_len, + float const co[2], const float penalty_dist, UvNearestHit *hit_final) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (uv_find_nearest_vert(scene, ima, obedit, co, penalty_dist, hit_final)) { + hit_final->ob = obedit; + found = true; + } + } + return found; +} + bool ED_uvedit_nearest_uv(Scene *scene, Object *obedit, Image *ima, const float co[2], float r_uv[2]) { BMEditMesh *em = BKE_editmesh_from_object(obedit); @@ -1043,9 +1092,10 @@ static bool uv_select_edgeloop_edge_tag_faces(BMEditMesh *em, UvMapVert *first1, } static int uv_select_edgeloop( - Scene *scene, Image *ima, Object *obedit, BMEditMesh *em, UvNearestHit *hit, + Scene *scene, Image *ima, Object *obedit, UvNearestHit *hit, const float limit[2], const bool extend) { + BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMIter iter, liter; BMLoop *l; @@ -1064,7 +1114,7 @@ static int uv_select_edgeloop( BM_mesh_elem_index_ensure(em->bm, BM_VERT | BM_FACE); if (!extend) { - uv_select_all_perform(scene, ima, obedit, em, SEL_DESELECT); + uv_select_all_perform(scene, ima, obedit, SEL_DESELECT); } BM_mesh_elem_hflag_disable_all(em->bm, BM_FACE, BM_ELEM_TAG, false); @@ -1147,10 +1197,17 @@ static int uv_select_edgeloop( /** \name Select Linked * \{ */ -static void uv_select_linked( - Scene *scene, Image *ima, Object *obedit, BMEditMesh *em, const float limit[2], +static void uv_select_linked_multi( + Scene *scene, Image *ima, Object **objects, const uint objects_len, const float limit[2], UvNearestHit *hit_final, bool extend, bool select_faces) { + /* loop over objects, or just use hit_final->ob */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + if (hit_final && ob_index != 0) { + break; + } + Object *obedit = hit_final ? hit_final->ob : objects[ob_index]; + BMFace *efa; BMLoop *l; BMIter iter, liter; @@ -1161,6 +1218,7 @@ static void uv_select_linked( unsigned int a; char *flag; + BMEditMesh *em = BKE_editmesh_from_object(obedit); const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); BM_mesh_elem_table_ensure(em->bm, BM_FACE); /* we can use this too */ @@ -1336,6 +1394,7 @@ static void uv_select_linked( MEM_freeN(stack); MEM_freeN(flag); BM_uv_vert_map_free(vmap); + } } /* WATCH IT: this returns first selected UV, @@ -1957,9 +2016,53 @@ static void UV_OT_weld(wmOperatorType *ot) /** \name (De)Select All Operator * \{ */ -static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, BMEditMesh *em, int action) + +static bool uv_select_is_any_selected(Scene *scene, Image *ima, Object *obedit) { ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + + if (ts->uv_flag & UV_SYNC_SELECTION) { + return (em->bm->totvertsel || em->bm->totedgesel || em->bm->totfacesel); + } + else { + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + if (!uvedit_face_visible_test(scene, obedit, ima, efa)) { + continue; + } + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (luv->flag & MLOOPUV_VERTSEL) { + return true; + } + } + } + } + return false; +} + +static bool uv_select_is_any_selected_multi(Scene *scene, Image *ima, Object **objects, const uint objects_len) +{ + bool found = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + if (uv_select_is_any_selected(scene, ima, obedit)) { + found = true; + break; + } + } + return found; +} + +static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, int action) +{ + ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; BMIter iter, liter; @@ -1967,8 +2070,11 @@ static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, BMEd const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - if (ts->uv_flag & UV_SYNC_SELECTION) { + if (action == SEL_TOGGLE) { + action = uv_select_is_any_selected(scene, ima, obedit) ? SEL_DESELECT : SEL_SELECT; + } + if (ts->uv_flag & UV_SYNC_SELECTION) { switch (action) { case SEL_TOGGLE: EDBM_select_toggle_all(em); @@ -1986,24 +2092,6 @@ static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, BMEd } } else { - if (action == SEL_TOGGLE) { - action = SEL_SELECT; - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { - if (!uvedit_face_visible_test(scene, obedit, ima, efa)) - continue; - - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { - luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - - if (luv->flag & MLOOPUV_VERTSEL) { - action = SEL_DESELECT; - break; - } - } - } - } - - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { if (!uvedit_face_visible_test(scene, obedit, ima, efa)) continue; @@ -2027,18 +2115,38 @@ static void uv_select_all_perform(Scene *scene, Image *ima, Object *obedit, BMEd } } +static void uv_select_all_perform_multi( + Scene *scene, Image *ima, Object **objects, const uint objects_len, int action) +{ + if (action == SEL_TOGGLE) { + action = uv_select_is_any_selected_multi(scene, ima, objects, objects_len) ? SEL_DESELECT : SEL_SELECT; + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + uv_select_all_perform(scene, ima, obedit, action); + } +} + static int uv_select_all_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); Image *ima = CTX_data_edit_image(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); int action = RNA_enum_get(op->ptr, "action"); - uv_select_all_perform(scene, ima, obedit, em, action); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + uv_select_all_perform_multi(scene, ima, objects, objects_len, action); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + } + + MEM_SAFE_FREE(objects); return OPERATOR_FINISHED; } @@ -2087,14 +2195,14 @@ static bool uv_sticky_select(float *limit, int hitv[], int v, float *hituv[], fl return false; } -static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loop) +static int uv_mouse_select_multi( + bContext *C, Object **objects, uint objects_len, + const float co[2], bool extend, bool loop) { SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; - Object *obedit = CTX_data_edit_object(C); Image *ima = CTX_data_edit_image(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; BMIter iter, liter; @@ -2105,8 +2213,6 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo int flush = 0, hitlen = 0; /* 0 == don't flush, 1 == sel, -1 == desel; only use when selection sync is enabled */ float limit[2], **hituv = NULL; - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* notice 'limit' is the same no matter the zoom level, since this is like * remove doubles and could annoying if it joined points when zoomed out. * 'penalty' is in screen pixel space otherwise zooming in on a uv-vert and @@ -2143,7 +2249,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo /* find nearest element */ if (loop) { /* find edge */ - if (!uv_find_nearest_edge(scene, ima, obedit, co, &hit)) { + if (!uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit)) { return OPERATOR_CANCELLED; } @@ -2151,7 +2257,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo } else if (selectmode == UV_SELECT_VERTEX) { /* find vertex */ - if (!uv_find_nearest_vert(scene, ima, obedit, co, penalty_dist, &hit)) { + if (!uv_find_nearest_vert_multi(scene, ima, objects, objects_len, co, penalty_dist, &hit)) { return OPERATOR_CANCELLED; } @@ -2167,7 +2273,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo } else if (selectmode == UV_SELECT_EDGE) { /* find edge */ - if (!uv_find_nearest_edge(scene, ima, obedit, co, &hit)) { + if (!uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit)) { return OPERATOR_CANCELLED; } @@ -2185,10 +2291,13 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo } else if (selectmode == UV_SELECT_FACE) { /* find face */ - if (!uv_find_nearest_face(scene, ima, obedit, co, &hit)) { + if (!uv_find_nearest_face_multi(scene, ima, objects, objects_len, co, &hit)) { return OPERATOR_CANCELLED; } + BMEditMesh *em = BKE_editmesh_from_object(hit.ob); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + /* make active */ BM_mesh_active_face_set(em->bm, hit.efa); @@ -2205,7 +2314,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo hitlen = hit.efa->len; } else if (selectmode == UV_SELECT_ISLAND) { - if (!uv_find_nearest_edge(scene, ima, obedit, co, &hit)) { + if (!uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit)) { return OPERATOR_CANCELLED; } @@ -2216,12 +2325,24 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo return OPERATOR_CANCELLED; } + Object *obedit = hit.ob; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + /* do selection */ if (loop) { - flush = uv_select_edgeloop(scene, ima, obedit, em, &hit, limit, extend); + if (!extend) { + /* TODO(MULTI_EDIT): We only need to de-select non-active */ + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } + flush = uv_select_edgeloop(scene, ima, obedit, &hit, limit, extend); } else if (selectmode == UV_SELECT_ISLAND) { - uv_select_linked(scene, ima, obedit, em, limit, &hit, extend, false); + if (!extend) { + /* TODO(MULTI_EDIT): We only need to de-select non-active */ + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } + uv_select_linked_multi(scene, ima, objects, objects_len, limit, &hit, extend, false); } else if (extend) { if (selectmode == UV_SELECT_VERTEX) { @@ -2271,7 +2392,7 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo } else { /* deselect all */ - uv_select_all_perform(scene, ima, obedit, em, SEL_DESELECT); + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); if (selectmode == UV_SELECT_VERTEX) { /* select vertex */ @@ -2339,6 +2460,15 @@ static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loo return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED; } +static int uv_mouse_select(bContext *C, const float co[2], bool extend, bool loop) +{ + ViewLayer *view_layer = CTX_data_view_layer(C); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len); + int ret = uv_mouse_select_multi(C, objects, objects_len, co, extend, loop); + MEM_freeN(objects); + return ret; +} static int uv_select_exec(bContext *C, wmOperator *op) { @@ -2443,9 +2573,8 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); Image *ima = CTX_data_edit_image(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); float limit[2]; int extend; bool select_faces = (ts->uv_flag & UV_SYNC_SELECTION) && (ts->selectmode & SCE_SELECT_FACE); @@ -2460,6 +2589,9 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent extend = RNA_boolean_get(op->ptr, "extend"); uvedit_pixel_to_float(sima, limit, 0.05f); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len); + if (pick) { float co[2]; @@ -2475,15 +2607,32 @@ static int uv_select_linked_internal(bContext *C, wmOperator *op, const wmEvent RNA_float_get_array(op->ptr, "location", co); } - if (!uv_find_nearest_edge(scene, ima, obedit, co, &hit)) { + if (!uv_find_nearest_edge_multi(scene, ima, objects, objects_len, co, &hit)) { + MEM_SAFE_FREE(objects); return OPERATOR_CANCELLED; } } - uv_select_linked(scene, ima, obedit, em, limit, pick ? &hit : NULL, extend, select_faces); + if (!extend) { + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); + } - DEG_id_tag_update(obedit->data, 0); - WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + uv_select_linked_multi(scene, ima, objects, objects_len, limit, pick ? &hit : NULL, extend, select_faces); + + /* weak!, but works */ + Object **objects_free = objects; + if (pick) { + objects = &hit.ob; + objects_len = 1; + } + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + DEG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + } + + MEM_SAFE_FREE(objects_free); return OPERATOR_FINISHED; } @@ -2879,23 +3028,20 @@ static int uv_border_select_exec(bContext *C, wmOperator *op) SpaceImage *sima = CTX_wm_space_image(C); Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; - Object *obedit = CTX_data_edit_object(C); + ViewLayer *view_layer = CTX_data_view_layer(C); Image *ima = CTX_data_edit_image(C); ARegion *ar = CTX_wm_region(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); BMFace *efa; BMLoop *l; BMIter iter, liter; MLoopUV *luv; rctf rectf; - bool changed, pinned, select, extend; + bool pinned, select, extend; const bool use_face_center = ( (ts->uv_flag & UV_SYNC_SELECTION) ? (ts->selectmode == SCE_SELECT_FACE) : (ts->uv_selectmode == UV_SELECT_FACE)); - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); - /* get rectangle from operator */ WM_operator_properties_border_to_rctf(op, &rectf); UI_view2d_region_to_view_rctf(&ar->v2d, &rectf, &rectf); @@ -2905,8 +3051,22 @@ static int uv_border_select_exec(bContext *C, wmOperator *op) extend = RNA_boolean_get(op->ptr, "extend"); pinned = RNA_boolean_get(op->ptr, "pinned"); + bool changed_multi = false; + + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len); + + /* don't indent to avoid diff noise! */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + bool changed = false; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + if (!extend) - uv_select_all_perform(scene, ima, obedit, em, SEL_DESELECT); + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); /* do actual selection */ if (use_face_center && !pinned) { @@ -2972,12 +3132,17 @@ static int uv_border_select_exec(bContext *C, wmOperator *op) if (ts->uv_flag & UV_SYNC_SELECTION) { WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); } - - return OPERATOR_FINISHED; + } + changed_multi |= changed; } + MEM_SAFE_FREE(objects); + + if (changed_multi) { + return OPERATOR_FINISHED; + } return OPERATOR_CANCELLED; -} +} static void UV_OT_select_border(wmOperatorType *ot) { @@ -3131,36 +3296,46 @@ static void UV_OT_circle_select(wmOperatorType *ot) /** \name Lasso Select Operator * \{ */ -static bool do_lasso_select_mesh_uv( - bContext *C, const int mcords[][2], short moves, - const bool select, const bool extend) +static bool do_lasso_select_mesh_uv(bContext *C, const int mcords[][2], short moves, + const bool select, const bool extend) { SpaceImage *sima = CTX_wm_space_image(C); Image *ima = CTX_data_edit_image(C); ARegion *ar = CTX_wm_region(C); - Object *obedit = CTX_data_edit_object(C); Scene *scene = CTX_data_scene(C); ToolSettings *ts = scene->toolsettings; - BMEditMesh *em = BKE_editmesh_from_object(obedit); + ViewLayer *view_layer = CTX_data_view_layer(C); const bool use_face_center = ( (ts->uv_flag & UV_SYNC_SELECTION) ? (ts->selectmode == SCE_SELECT_FACE) : (ts->uv_selectmode == UV_SELECT_FACE)); - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); BMIter iter, liter; BMFace *efa; BMLoop *l; int screen_uv[2]; - bool changed = false; + bool changed_multi = false; rcti rect; BLI_lasso_boundbox(&rect, mcords, moves); + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len); + + /* don't indent to avoid diff noise! */ + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + + bool changed = false; + + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + if (!extend && select) { - uv_select_all_perform(scene, ima, obedit, em, SEL_DESELECT); + uv_select_all_perform_multi(scene, ima, objects, objects_len, SEL_DESELECT); } if (use_face_center) { /* Face Center Sel */ @@ -3223,7 +3398,10 @@ static bool do_lasso_select_mesh_uv( } } - return changed; + changed_multi |= changed; + } + + return changed_multi; } static int uv_lasso_select_exec(bContext *C, wmOperator *op) @@ -4175,7 +4353,7 @@ static int uv_mark_seam_exec(bContext *C, wmOperator *op) me->drawflag |= ME_DRAWSEAMS; if (scene->toolsettings->edge_mode_live_unwrap) - ED_unwrap_lscm(scene, ob, false); + ED_unwrap_lscm(scene, ob, false, false); DEG_id_tag_update(&me->id, 0); WM_event_add_notifier(C, NC_GEOM | ND_DATA, me); diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index 4f28d1f9eea..0e5f4886f3e 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -61,6 +61,7 @@ #include "BKE_report.h" #include "BKE_scene.h" #include "BKE_editmesh.h" +#include "BKE_layer.h" #include "DEG_depsgraph.h" @@ -202,6 +203,21 @@ static bool uvedit_have_selection(Scene *scene, BMEditMesh *em, bool implicit) return false; } +static bool uvedit_have_selection_multi( + Scene *scene, Object **objects, const uint objects_len, bool implicit) +{ + bool have_select = false; + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + if (uvedit_have_selection(scene, em, implicit)) { + have_select = true; + break; + } + } + return have_select; +} + void ED_uvedit_get_aspect(Scene *scene, Object *ob, BMesh *bm, float *aspx, float *aspy) { bool sloppy = true; @@ -258,9 +274,11 @@ static void construct_param_handle_face_add(ParamHandle *handle, Scene *scene, param_face_add(handle, key, i, vkeys, co, uv, pin, select, efa->no); } -static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm, - const bool implicit, const bool fill, const bool sel, - const bool correct_aspect) +/* See: construct_param_handle_multi to handle multiple objects at once. */ +static ParamHandle *construct_param_handle( + Scene *scene, Object *ob, BMesh *bm, + const bool implicit, const bool fill, const bool sel, + const bool correct_aspect) { ParamHandle *handle; BMFace *efa; @@ -324,6 +342,89 @@ static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm, return handle; } +/** + * Version of #construct_param_handle_single that handles multiple objects. + */ +static ParamHandle *construct_param_handle_multi( + Scene *scene, Object **objects, const uint objects_len, + const bool implicit, const bool fill, const bool sel, + const bool correct_aspect) +{ + ParamHandle *handle; + BMFace *efa; + BMLoop *l; + BMEdge *eed; + BMIter iter, liter; + int i; + + + handle = param_construct_begin(); + + if (correct_aspect) { + Object *ob = objects[0]; + BMEditMesh *em = BKE_editmesh_from_object(ob); + BMesh *bm = em->bm; + float aspx, aspy; + + ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy); + if (aspx != aspy) { + param_aspect_ratio(handle, aspx, aspy); + } + } + + /* we need the vert indices */ + EDBM_mesh_elem_index_ensure_multi(objects, objects_len, BM_VERT); + + int offset = 0; + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); + + BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) { + + if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || (sel && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) { + continue; + } + + if (implicit) { + bool is_loopsel = false; + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) { + is_loopsel = true; + break; + } + } + if (is_loopsel == false) { + continue; + } + } + + construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset); + } + + if (!implicit) { + BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) { + if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) { + ParamKey vkeys[2]; + vkeys[0] = (ParamKey)BM_elem_index_get(eed->v1); + vkeys[1] = (ParamKey)BM_elem_index_get(eed->v2); + param_edge_set_seam(handle, vkeys); + } + } + } + offset += bm->totface; + } + + param_construct_end(handle, fill, implicit); + + return handle; +} + static void texface_from_original_index(BMFace *efa, int index, float **uv, ParamBool *pin, ParamBool *select, Scene *scene, const int cd_loop_uv_offset) @@ -715,6 +816,7 @@ void UV_OT_minimize_stretch(wmOperatorType *ot) /* ******************** Pack Islands operator **************** */ + void ED_uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm, bool selected, bool correct_aspect, bool do_rotate) { ParamHandle *handle; @@ -724,14 +826,29 @@ void ED_uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm, bool selected, param_delete(handle); } +void ED_uvedit_pack_islands_multi( + Scene *scene, Object **objects, const uint objects_len, + bool selected, bool correct_aspect, bool do_rotate) +{ + ParamHandle *handle; + handle = construct_param_handle_multi( + scene, objects, objects_len, true, false, selected, correct_aspect); + param_pack(handle, scene->toolsettings->uvcalc_margin, do_rotate); + param_flush(handle); + param_delete(handle); +} + static int pack_islands_exec(bContext *C, wmOperator *op) { + ViewLayer *view_layer = CTX_data_view_layer(C); Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); bool do_rotate = RNA_boolean_get(op->ptr, "rotate"); - if (!uvedit_have_selection(scene, em, true)) { + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len); + + if (!uvedit_have_selection_multi(scene, objects, objects_len, true)) { + MEM_SAFE_FREE(objects); return OPERATOR_CANCELLED; } @@ -740,10 +857,15 @@ static int pack_islands_exec(bContext *C, wmOperator *op) else RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin); - ED_uvedit_pack_islands(scene, obedit, em->bm, true, true, do_rotate); - - DEG_id_tag_update(obedit->data, 0); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + ED_uvedit_pack_islands_multi(scene, objects, objects_len, true, true, do_rotate); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + DEG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + } + + MEM_SAFE_FREE(objects); return OPERATOR_FINISHED; } @@ -856,7 +978,7 @@ void ED_uvedit_live_unwrap(Scene *scene, Object *obedit) if (scene->toolsettings->edge_mode_live_unwrap && CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV)) { - ED_unwrap_lscm(scene, obedit, false); /* unwrap all not just sel */ + ED_unwrap_lscm(scene, obedit, false, false); /* unwrap all not just sel */ } } @@ -1178,7 +1300,7 @@ static void uv_map_clip_correct(Scene *scene, Object *ob, BMEditMesh *em, wmOper /* ******************** Unwrap operator **************** */ /* assumes UV Map is checked, doesn't run update funcs */ -void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel) +void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel, const bool pack) { BMEditMesh *em = BKE_editmesh_from_object(obedit); ParamHandle *handle; @@ -1199,7 +1321,10 @@ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel) param_lscm_end(handle); param_average(handle); - param_pack(handle, scene->toolsettings->uvcalc_margin, false); + + if (pack) { + param_pack(handle, scene->toolsettings->uvcalc_margin, false); + } param_flush(handle); @@ -1208,33 +1333,48 @@ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel) static int unwrap_exec(bContext *C, wmOperator *op) { + ViewLayer *view_layer = CTX_data_view_layer(C); Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); int method = RNA_enum_get(op->ptr, "method"); const bool fill_holes = RNA_boolean_get(op->ptr, "fill_holes"); const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); const bool use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data"); - bool use_subsurf_final; float obsize[3]; bool implicit = false; - if (!uvedit_have_selection(scene, em, implicit)) { + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + if (!uvedit_have_selection_multi(scene, objects, objects_len, implicit)) { + MEM_SAFE_FREE(objects); return OPERATOR_CANCELLED; } - + /* add uvs if they don't exist yet */ - if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { - return OPERATOR_CANCELLED; - } + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + bool use_subsurf_final; + + if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { + continue; + } - mat4_to_size(obsize, obedit->obmat); - if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f)) - BKE_report(op->reports, RPT_INFO, - "Object has non-uniform scale, unwrap will operate on a non-scaled version of the mesh"); - else if (is_negative_m4(obedit->obmat)) - BKE_report(op->reports, RPT_INFO, - "Object has negative scale, unwrap will operate on a non-flipped version of the mesh"); + mat4_to_size(obsize, obedit->obmat); + if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f)) + BKE_report(op->reports, RPT_INFO, + "Object has non-uniform scale, unwrap will operate on a non-scaled version of the mesh"); + else if (is_negative_m4(obedit->obmat)) + BKE_report(op->reports, RPT_INFO, + "Object has negative scale, unwrap will operate on a non-flipped version of the mesh"); + + + /* double up the check here but better keep ED_unwrap_lscm interface simple and not + * pass operator for warning append */ + modifier_unwrap_state(obedit, scene, &use_subsurf_final); + if (use_subsurf != use_subsurf_final) { + BKE_report(op->reports, RPT_INFO, "Subdivision Surface modifier needs to be first to work with unwrap"); + } + } /* remember last method for live unwrap */ if (RNA_struct_property_is_set(op->ptr, "method")) @@ -1257,17 +1397,17 @@ static int unwrap_exec(bContext *C, wmOperator *op) if (use_subsurf) scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF; else scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF; - /* double up the check here but better keep ED_unwrap_lscm interface simple and not - * pass operator for warning append */ - modifier_unwrap_state(obedit, scene, &use_subsurf_final); - if (use_subsurf != use_subsurf_final) - BKE_report(op->reports, RPT_INFO, "Subdivision Surface modifier needs to be first to work with unwrap"); - /* execute unwrap */ - ED_unwrap_lscm(scene, obedit, true); + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + ED_unwrap_lscm(scene, obedit, true, false); + DEG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + } - DEG_id_tag_update(obedit->data, 0); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + ED_uvedit_pack_islands_multi(scene, objects, objects_len, true, true, true); + + MEM_SAFE_FREE(objects); return OPERATOR_FINISHED; } @@ -1322,9 +1462,8 @@ static int uv_from_view_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE static int uv_from_view_exec(bContext *C, wmOperator *op) { + ViewLayer *view_layer = CTX_data_view_layer(C); Scene *scene = CTX_data_scene(C); - Object *obedit = CTX_data_edit_object(C); - BMEditMesh *em = BKE_editmesh_from_object(obedit); ARegion *ar = CTX_wm_region(C); View3D *v3d = CTX_wm_view3d(C); RegionView3D *rv3d = CTX_wm_region_view3d(C); @@ -1334,15 +1473,22 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) BMIter iter, liter; MLoopUV *luv; float rotmat[4][4]; + bool changed_multi = false; - int cd_loop_uv_offset; + uint objects_len = 0; + Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len); + + for (uint ob_index = 0; ob_index < objects_len; ob_index++) { + Object *obedit = objects[ob_index]; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + bool changed = false; /* add uvs if they don't exist yet */ if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { - return OPERATOR_CANCELLED; + continue; } - cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); if (RNA_boolean_get(op->ptr, "orthographic")) { uv_map_rotation_matrix(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f); @@ -1355,6 +1501,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); BLI_uvproject_from_view_ortho(luv->uv, l->v->co, rotmat); } + changed = true; } } else if (camera) { @@ -1372,6 +1519,7 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); BLI_uvproject_from_camera(luv->uv, l->v->co, uci); } + changed = true; } MEM_freeN(uci); @@ -1388,15 +1536,26 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); BLI_uvproject_from_view(luv->uv, l->v->co, rv3d->persmat, rotmat, ar->winx, ar->winy); } + changed = true; } } - uv_map_clip_correct(scene, obedit, em, op); + if (changed) { + uv_map_clip_correct(scene, obedit, em, op); - DEG_id_tag_update(obedit->data, 0); - WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + DEG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + changed_multi = true; + } + } + MEM_SAFE_FREE(objects); - return OPERATOR_FINISHED; + if (changed_multi) { + return OPERATOR_FINISHED; + } + else { + return OPERATOR_CANCELLED; + } } static int uv_from_view_poll(bContext *C) -- cgit v1.2.3