Multi-Object Editing
authorCampbell Barton <ideasman42@gmail.com>
Mon, 16 Apr 2018 14:27:55 +0000 (16:27 +0200)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 16 Apr 2018 15:56:50 +0000 (17:56 +0200)
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

58 files changed:
source/blender/blenkernel/BKE_layer.h
source/blender/blenkernel/BKE_object.h
source/blender/blenkernel/CMakeLists.txt
source/blender/blenkernel/intern/editmesh.c
source/blender/blenkernel/intern/layer.c
source/blender/blenkernel/intern/layer_utils.c [new file with mode: 0644]
source/blender/blenkernel/intern/object.c
source/blender/blenkernel/intern/object_update.c
source/blender/bmesh/intern/bmesh_mesh.c
source/blender/bmesh/intern/bmesh_mesh.h
source/blender/draw/intern/draw_armature.c
source/blender/draw/intern/draw_manager.c
source/blender/draw/modes/edit_curve_mode.c
source/blender/draw/modes/edit_lattice_mode.c
source/blender/draw/modes/edit_mesh_mode.c
source/blender/draw/modes/edit_metaball_mode.c
source/blender/draw/modes/pose_mode.c
source/blender/editors/armature/CMakeLists.txt
source/blender/editors/armature/armature_add.c
source/blender/editors/armature/armature_edit.c
source/blender/editors/armature/armature_intern.h
source/blender/editors/armature/armature_select.c
source/blender/editors/armature/editarmature_undo.c
source/blender/editors/armature/pose_edit.c
source/blender/editors/armature/pose_select.c
source/blender/editors/curve/CMakeLists.txt
source/blender/editors/curve/editcurve_undo.c
source/blender/editors/include/ED_armature.h
source/blender/editors/include/ED_mesh.h
source/blender/editors/include/ED_object.h
source/blender/editors/include/ED_undo.h
source/blender/editors/include/ED_uvedit.h
source/blender/editors/include/ED_view3d.h
source/blender/editors/lattice/CMakeLists.txt
source/blender/editors/lattice/editlattice_undo.c
source/blender/editors/mesh/CMakeLists.txt
source/blender/editors/mesh/editmesh_select.c
source/blender/editors/mesh/editmesh_tools.c
source/blender/editors/mesh/editmesh_undo.c
source/blender/editors/mesh/meshtools.c
source/blender/editors/metaball/CMakeLists.txt
source/blender/editors/metaball/editmball_undo.c
source/blender/editors/object/object_edit.c
source/blender/editors/object/object_modes.c
source/blender/editors/screen/screen_context.c
source/blender/editors/space_view3d/space_view3d.c
source/blender/editors/space_view3d/view3d_select.c
source/blender/editors/transform/transform.c
source/blender/editors/transform/transform.h
source/blender/editors/transform/transform_constraints.c
source/blender/editors/transform/transform_conversions.c
source/blender/editors/transform/transform_generics.c
source/blender/editors/transform/transform_snap.c
source/blender/editors/undo/ed_undo.c
source/blender/editors/uvedit/uvedit_draw.c
source/blender/editors/uvedit/uvedit_intern.h
source/blender/editors/uvedit/uvedit_ops.c
source/blender/editors/uvedit/uvedit_unwrap_ops.c

index 9c06ae4f40d86aa755bf52d8306660d1895523e7..75fb4962bef3aec15bb80d9b9de4f83d3dd6aa26 100644 (file)
@@ -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
index c698db2e8f551105a9cafb75bf8a78a291a9a0f0..c75bbf849a8fed74d68913e26c1e5d03b6f67e90 100644 (file)
@@ -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,
index 6a1c3ea883c0ed20a6b9a39e508cbb8731eff4a8..e8fd71c2b2d7252110809c4d332f76def56460e9 100644 (file)
@@ -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
index c95da3b25695b3a7dac27bf97538caaba12d57e3..b63ab276b14124b07e2c325b9cc1adf1e5b3126d 100644 (file)
@@ -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;
 }
index 50c7dc0c02fedd4a460fac193a025fafeea88737..5f24dd481e24afd63148170694eba50cd4223f97 100644 (file)
@@ -26,6 +26,7 @@
 
 #include <string.h>
 
+#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 (file)
index 0000000..94bac8a
--- /dev/null
@@ -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 <string.h>
+
+#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;
+}
index 875d716305f697940ea0cbc5f1ea3b0d3fc82989..081f9f15508a4d66f81b39caedc603b42657d488 100644 (file)
@@ -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
  */
index c70e07e6c4cb838f6240f8ab8a1403716d82c6f5..35ab4024f621f42d135a031bdf869076ceb09bab 100644 (file)
@@ -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) */
index 9e03c28ba1b8f538098d45ad97a317c8c876a8ef..8071637d95ec5211e2d155095dabd39d4be7a421 100644 (file)
@@ -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);
 }
 
 
index d449aac04f5bef1fff969590e6238ad16c5d4ebe..3ebe6535a8b20e881f78672ce155817f44d576ad 100644 (file)
@@ -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,
index c14fe70e0c35233a23c3f8afd9c94a81915cb518..fe87e7f17fd88d4785e9375a8f35579a903eed19 100644 (file)
@@ -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;
index 6ba1225b6873083086e4e458ade0fb2182785f6e..420841e2efaf51bed3e06c2d8466ec15f557feb0 100644 (file)
@@ -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(
index 73a4fb1e9e6d14595f1e8eba6b60216ba23ca7cc..b33ebd8ba601b01275d5e122e30ad1ad8c63f8a8 100644 (file)
@@ -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;
index 0268f4eb45363480a03039111cce9f884adcfd21..e8628711ffd5dae176b4a6bb49115231b9024c4e 100644 (file)
@@ -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;
 
index 4bd69941809b774aa9883e05932c62ca3b6f58f3..c465fa38f04f376cbaf0377edd0ff90c84bc8670 100644 (file)
@@ -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");
index bcabeef5bc311741e7738f41d33e471ade52c440..f7b7113a4d63cfe20d0c68db42c6936c0e664969 100644 (file)
@@ -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();
index 749c3e71368bbf2f0bf74b87cb74bf4f865b5849..1d3d31ab54d14b3c37620020880aa2e58bc78245 100644 (file)
@@ -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;
        }
 
index 4301fe6582ffa9990df9ef9a3fb16c8f6dd049b5..8a40ea3b383e3701f9aaeda8219ba855fe87aa14 100644 (file)
@@ -28,6 +28,7 @@ set(INC
        ../../makesdna
        ../../makesrna
        ../../windowmanager
+       ../../../../intern/clog
        ../../../../intern/guardedalloc
        ../../../../intern/eigen
        ../../../../intern/glew-mx
index cb072bee345c5c6aa045b5ceb42c92a731516969..36dded5ed5e4845218035df4f9404279f49dae2b 100644 (file)
@@ -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 */
index 75b80627dff96b47b996e73d178bb221f941c7e4..2335e29aca8b9d591f04b14bf5607768ae69d560 100644 (file)
@@ -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;
 }
index 0ba720a17d002f27c65d7f4794ec4b13e044689d..575d1597cc43536f78917d529b5a5528baad17d4 100644 (file)
@@ -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 *));
index 587cafa6d486187a935d04bef35e327163b44e45..63864e75edcaabbf521aff25f755f7be1bc583d9 100644 (file)
@@ -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"
 
 
 /* **************** 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;
 
index 217de06d99b8fd9f7f704b458cebab61cbb23e0b..f6f97af32b981f347a4da77327c81aca4dd6c55d 100644 (file)
  *  \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. */
index 520ecc797aa7f079da89f80039d4afb1f5ed0bcf..ea93e024f8ef6e7194ad123fb5adba27751424a4 100644 (file)
@@ -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);
index 1c23f71233d3186a2676517e455ba58f00105a56..b6f1e101291ed888b5f8cdcf46bf78ddaecff2c6 100644 (file)
@@ -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;
 }
 
index 85daa7e44e51edb2ddcfcef181f375500b5f2144..301d333ebdbaa80265ef7e8784ef92561df93fb8 100644 (file)
@@ -28,6 +28,7 @@ set(INC
        ../../makesdna
        ../../makesrna
        ../../windowmanager
+       ../../../../intern/clog
        ../../../../intern/guardedalloc
        ../../../../intern/glew-mx
        ../../../../extern/curve_fit_nd
index 4eb2abaefad8e8639d1ae0056c60ea90b111fcb5..ad17331853ba726e4202483bf54a1affbd0491db 100644 (file)
  *  \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. */
index 186ca5313e58ef000e971272642036745f8ceb43..e6284cb16564e9f5145d9c6cd3dea1e574fc6d4c 100644 (file)
@@ -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);
index 3217433204ea46e7187bf28e1e56ae31cbf4e82a..d3f2e1fff8505192345a3a3053797a99241555cd 100644 (file)
@@ -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
 
index 831190622037bece44ae506a60b0c66d8c0ee4b2..bfc3325d7eba14f925a142c327c5777972a1d2fd 100644 (file)
@@ -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);
 
index b3814ab58998334b536c6ca305d8c6f2853e0f42..43ffb0916667f5b05d11c5ac791451bcb335da67 100644 (file)
@@ -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);
index 2a5ad4946438401caea18c915d3bfb652e7ad0dd..fd532e704781df49c054b2e28c82bb45b2ba2bad 100644 (file)
@@ -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,
index 7f18c10f970cfbf69bb5376f27be06c8d4cc51e7..c5b99013610b8db2732df7b8f6fd91946ad165ef 100644 (file)
@@ -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);
index eaf837cf97817f51546dcceeaa66420a8f74da0a..2207e0fa7363431c33e413a02288e1680e6a8962 100644 (file)
@@ -27,6 +27,7 @@ set(INC
        ../../makesrna
        ../../render/extern/include
        ../../windowmanager
+       ../../../../intern/clog
        ../../../../intern/guardedalloc
 )
 
index 58fa08e5aa9b4d180b931d734aa3db78b594d251..cbd89016b4425d91e0738760dee986a182770d22 100644 (file)
@@ -33,6 +33,8 @@
 
 #include "MEM_guardedalloc.h"
 
+#include "CLG_log.h"
+
 #include "BLI_utildefines.h"
 #include "BLI_array_utils.h"
 
 #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. */
index 3877838ec54cc5718f0973f82b89b9159d938252..eae6b7192d7de8832182b9888659e183a3f3aa81 100644 (file)
@@ -32,6 +32,7 @@ set(INC
        ../../makesrna
        ../../render/extern/include
        ../../windowmanager
+       ../../../../intern/clog
        ../../../../intern/guardedalloc
        ../../../../intern/glew-mx
 )
index 87937fd414682996239730af5be70522584b1042..20cebc9d4b9f3a52c256def65f34faa551cdb040 100644 (file)
@@ -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;
 }
index bf70cc3fa7ecd5012f02a5bf7a1a7e2876154f2a..78d563c64e960d3a49042c267fc7ab3277d8e113 100644 (file)
 
 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;
 }
index ab7e13117a04802a38456ccd9d93ba46bfc95548..4d4b7a098b08320da6e6947d18862107d02ea81d 100644 (file)
 
 #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);
        }
index 531a26a66a8a294487e8d5f3e68c5588259fdc7d..dec1327341790c9e97cc0a68935a3d154574c5dd 100644 (file)
@@ -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);
+       }
+}
index 73f80774716b2db4f520ae566b3017f9153dfaf5..b0ae31227274d7d189df63504ce9f8b137a9dd38 100644 (file)
@@ -27,6 +27,7 @@ set(INC
        ../../makesrna
        ../../render/extern/include
        ../../windowmanager
+       ../../../../intern/clog
        ../../../../intern/guardedalloc
 )
 
index cc461c0c365947b4f95fab9861f8e40e9915dcdd..7045025e227938e8ff12df902fa349487122af5e 100644 (file)
@@ -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"
 #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. */
index f4066360805e6fc77d0cc63c58f9ddb830b22bf3..a17b1c122adc97e06adf77f00054adcf8ae8bc5d 100644 (file)
@@ -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;
        }
index f074a56fb8687b1d8d5ed14b05581a330529d2c3..f61e597e69e57da4180d5ccfe0475b0dad2d9096 100644 (file)
@@ -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;
        }
index 343e615f76bc7a16dab94249ef8604b2ad7d66a1..c023c5d90bcc68b416b0ba2fab3fd6a44d45d477 100644 (file)
@@ -30,6 +30,8 @@
 #include <stdlib.h>
 #include <string.h>
 
+#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;
                }
index 9fb602c81d6797561a782fa40954bef0b6e93205..744553dedb734b5ec73b65323589b8696079340b 100644 (file)
@@ -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);
                                        }
index 6833dac558d7a72f3a77ef76bacb4494fb209aaa..cdbcc3664a75b9fa44dabe1e96f980f9ef99cfd8 100644 (file)
@@ -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);
                }
        }
        
index d4c5f6053b4cea8f8e471cedf228bbc3d7e6a378..8c4aeb1d13602ced1cb553ed152aa9cc400d6c8f 100644 (file)
@@ -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