Merge branch 'master' into blender2.8
[blender.git] / source / blender / editors / uvedit / uvedit_unwrap_ops.c
index dbe8796..78b4125 100644 (file)
 #include "BKE_subsurf.h"
 #include "BKE_context.h"
 #include "BKE_customdata.h"
-#include "BKE_depsgraph.h"
 #include "BKE_image.h"
 #include "BKE_main.h"
+#include "BKE_material.h"
 #include "BKE_report.h"
 #include "BKE_scene.h"
 #include "BKE_editmesh.h"
+#include "BKE_layer.h"
+
+#include "DEG_depsgraph.h"
 
 #include "PIL_time.h"
 
@@ -100,9 +103,8 @@ static void modifier_unwrap_state(Object *obedit, Scene *scene, bool *r_use_subs
        *r_use_subsurf = subsurf;
 }
 
-static bool ED_uvedit_ensure_uvs(bContext *C, Scene *scene, Object *obedit)
+static bool ED_uvedit_ensure_uvs(bContext *C, Scene *UNUSED(scene), Object *obedit)
 {
-       Main *bmain = CTX_data_main(C);
        BMEditMesh *em = BKE_editmesh_from_object(obedit);
        BMFace *efa;
        BMIter iter;
@@ -116,7 +118,7 @@ static bool ED_uvedit_ensure_uvs(bContext *C, Scene *scene, Object *obedit)
        if (ED_uvedit_test(obedit))
                return 1;
 
-       if (em && em->bm->totface && !CustomData_has_layer(&em->bm->pdata, CD_MTEXPOLY))
+       if (em && em->bm->totface && !CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV))
                ED_mesh_uv_texture_add(obedit->data, NULL, true);
 
        if (!ED_uvedit_test(obedit))
@@ -146,9 +148,6 @@ static bool ED_uvedit_ensure_uvs(bContext *C, Scene *scene, Object *obedit)
                }
        }
 
-       if (ima)
-               ED_uvedit_assign_image(bmain, scene, obedit, ima, NULL);
-
        /* select new UV's (ignore UV_SYNC_SELECTION in this case) */
        BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
                BMIter liter;
@@ -200,7 +199,22 @@ static bool uvedit_have_selection(Scene *scene, BMEditMesh *em, bool implicit)
        return false;
 }
 
-void ED_uvedit_get_aspect(Scene *scene, Object *ob, BMesh *bm, float *aspx, float *aspy)
+static bool uvedit_have_selection_multi(
+        Scene *scene, Object **objects, const uint objects_len, bool implicit)
+{
+       bool have_select = false;
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+               Object *obedit = objects[ob_index];
+               BMEditMesh *em = BKE_editmesh_from_object(obedit);
+               if (uvedit_have_selection(scene, em, implicit)) {
+                       have_select = true;
+                       break;
+               }
+       }
+       return have_select;
+}
+
+void ED_uvedit_get_aspect(Scene *UNUSED(scene), Object *ob, BMesh *bm, float *aspx, float *aspy)
 {
        bool sloppy = true;
        bool selected = false;
@@ -210,13 +224,7 @@ void ED_uvedit_get_aspect(Scene *scene, Object *ob, BMesh *bm, float *aspx, floa
        efa = BM_mesh_active_face_get(bm, sloppy, selected);
 
        if (efa) {
-               if (BKE_scene_use_new_shading_nodes(scene)) {
-                       ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
-               }
-               else {
-                       MTexPoly *tf = CustomData_bmesh_get(&bm->pdata, efa->head.data, CD_MTEXPOLY);
-                       ima = tf->tpage;
-               }
+               ED_object_get_active_image(ob, efa->mat_nr + 1, &ima, NULL, NULL, NULL);
 
                ED_image_get_uv_aspect(ima, NULL, aspx, aspy);
        }
@@ -257,9 +265,11 @@ static void construct_param_handle_face_add(ParamHandle *handle, Scene *scene,
        param_face_add(handle, key, i, vkeys, co, uv, pin, select, efa->no);
 }
 
-static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm,
-                                           const bool implicit, const bool fill, const bool sel,
-                                           const bool correct_aspect)
+/* See: construct_param_handle_multi to handle multiple objects at once. */
+static ParamHandle *construct_param_handle(
+        Scene *scene, Object *ob, BMesh *bm,
+        const bool implicit, const bool fill, const bool sel,
+        const bool correct_aspect)
 {
        ParamHandle *handle;
        BMFace *efa;
@@ -323,6 +333,89 @@ static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm,
        return handle;
 }
 
+/**
+ * Version of #construct_param_handle_single that handles multiple objects.
+ */
+static ParamHandle *construct_param_handle_multi(
+        Scene *scene, Object **objects, const uint objects_len,
+        const bool implicit, const bool fill, const bool sel,
+        const bool correct_aspect)
+{
+       ParamHandle *handle;
+       BMFace *efa;
+       BMLoop *l;
+       BMEdge *eed;
+       BMIter iter, liter;
+       int i;
+
+
+       handle = param_construct_begin();
+
+       if (correct_aspect) {
+               Object *ob = objects[0];
+               BMEditMesh *em = BKE_editmesh_from_object(ob);
+               BMesh *bm = em->bm;
+               float aspx, aspy;
+
+               ED_uvedit_get_aspect(scene, ob, bm, &aspx, &aspy);
+               if (aspx != aspy) {
+                       param_aspect_ratio(handle, aspx, aspy);
+               }
+       }
+
+       /* we need the vert indices */
+       EDBM_mesh_elem_index_ensure_multi(objects, objects_len, BM_VERT);
+
+       int offset = 0;
+
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+               Object *obedit = objects[ob_index];
+               BMEditMesh *em = BKE_editmesh_from_object(obedit);
+               BMesh *bm = em->bm;
+
+               const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
+
+               BM_ITER_MESH_INDEX (efa, &iter, bm, BM_FACES_OF_MESH, i) {
+
+                       if ((BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) || (sel && BM_elem_flag_test(efa, BM_ELEM_SELECT) == 0)) {
+                               continue;
+                       }
+
+                       if (implicit) {
+                               bool is_loopsel = false;
+
+                               BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+                                       if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
+                                               is_loopsel = true;
+                                               break;
+                                       }
+                               }
+                               if (is_loopsel == false) {
+                                       continue;
+                               }
+                       }
+
+                       construct_param_handle_face_add(handle, scene, efa, i + offset, cd_loop_uv_offset);
+               }
+
+               if (!implicit) {
+                       BM_ITER_MESH (eed, &iter, bm, BM_EDGES_OF_MESH) {
+                               if (BM_elem_flag_test(eed, BM_ELEM_SEAM)) {
+                                       ParamKey vkeys[2];
+                                       vkeys[0] = (ParamKey)BM_elem_index_get(eed->v1);
+                                       vkeys[1] = (ParamKey)BM_elem_index_get(eed->v2);
+                                       param_edge_set_seam(handle, vkeys);
+                               }
+                       }
+               }
+               offset += bm->totface;
+       }
+
+       param_construct_end(handle, fill, implicit);
+
+       return handle;
+}
+
 
 static void texface_from_original_index(BMFace *efa, int index, float **uv, ParamBool *pin, ParamBool *select,
                                         Scene *scene, const int cd_loop_uv_offset)
@@ -570,7 +663,7 @@ static void minimize_stretch_iteration(bContext *C, wmOperator *op, bool interac
 
                ms->lasttime = PIL_check_seconds_timer();
 
-               DAG_id_tag_update(ms->obedit->data, 0);
+               DEG_id_tag_update(ms->obedit->data, 0);
                WM_event_add_notifier(C, NC_GEOM | ND_DATA, ms->obedit->data);
        }
 }
@@ -593,7 +686,7 @@ static void minimize_stretch_exit(bContext *C, wmOperator *op, bool cancel)
        param_stretch_end(ms->handle);
        param_delete(ms->handle);
 
-       DAG_id_tag_update(ms->obedit->data, 0);
+       DEG_id_tag_update(ms->obedit->data, 0);
        WM_event_add_notifier(C, NC_GEOM | ND_DATA, ms->obedit->data);
 
        MEM_freeN(ms);
@@ -714,6 +807,7 @@ void UV_OT_minimize_stretch(wmOperatorType *ot)
 
 /* ******************** Pack Islands operator **************** */
 
+
 void ED_uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm, bool selected, bool correct_aspect, bool do_rotate)
 {
        ParamHandle *handle;
@@ -723,14 +817,29 @@ void ED_uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm, bool selected,
        param_delete(handle);
 }
 
+void ED_uvedit_pack_islands_multi(
+        Scene *scene, Object **objects, const uint objects_len,
+        bool selected, bool correct_aspect, bool do_rotate)
+{
+       ParamHandle *handle;
+       handle = construct_param_handle_multi(
+               scene, objects, objects_len, true, false, selected, correct_aspect);
+       param_pack(handle, scene->toolsettings->uvcalc_margin, do_rotate);
+       param_flush(handle);
+       param_delete(handle);
+}
+
 static int pack_islands_exec(bContext *C, wmOperator *op)
 {
+       ViewLayer *view_layer = CTX_data_view_layer(C);
        Scene *scene = CTX_data_scene(C);
-       Object *obedit = CTX_data_edit_object(C);
-       BMEditMesh *em = BKE_editmesh_from_object(obedit);
        bool do_rotate = RNA_boolean_get(op->ptr, "rotate");
 
-       if (!uvedit_have_selection(scene, em, true)) {
+       uint objects_len = 0;
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data_with_uvs(view_layer, &objects_len);
+
+       if (!uvedit_have_selection_multi(scene, objects, objects_len, true)) {
+               MEM_freeN(objects);
                return OPERATOR_CANCELLED;
        }
 
@@ -739,10 +848,15 @@ static int pack_islands_exec(bContext *C, wmOperator *op)
        else
                RNA_float_set(op->ptr, "margin", scene->toolsettings->uvcalc_margin);
 
-       ED_uvedit_pack_islands(scene, obedit, em->bm, true, true, do_rotate);
+       ED_uvedit_pack_islands_multi(scene, objects, objects_len, true, true, do_rotate);
 
-       DAG_id_tag_update(obedit->data, 0);
-       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+               Object *obedit = objects[ob_index];
+               DEG_id_tag_update(obedit->data, 0);
+               WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+       }
+
+       MEM_freeN(objects);
 
        return OPERATOR_FINISHED;
 }
@@ -784,7 +898,7 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
        param_flush(handle);
        param_delete(handle);
 
-       DAG_id_tag_update(obedit->data, 0);
+       DEG_id_tag_update(obedit->data, 0);
        WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
 
        return OPERATOR_FINISHED;
@@ -855,7 +969,7 @@ void ED_uvedit_live_unwrap(Scene *scene, Object *obedit)
        if (scene->toolsettings->edge_mode_live_unwrap &&
            CustomData_has_layer(&em->bm->ldata, CD_MLOOPUV))
        {
-               ED_unwrap_lscm(scene, obedit, false); /* unwrap all not just sel */
+               ED_unwrap_lscm(scene, obedit, false, false); /* unwrap all not just sel */
        }
 }
 
@@ -903,7 +1017,7 @@ static void uv_map_transform_center(
         float r_bounds[2][3])
 {
        /* only operates on the edit object - this is all that's needed now */
-       const int around = (v3d) ? v3d->around : V3D_AROUND_CENTER_BOUNDS;
+       const int around = (v3d) ? scene->toolsettings->transform_pivot_point : V3D_AROUND_CENTER_BOUNDS;
 
        float bounds[2][3];
        INIT_MINMAX(bounds[0], bounds[1]);
@@ -925,7 +1039,7 @@ static void uv_map_transform_center(
                case V3D_AROUND_CURSOR:  /* cursor center */
                {
                        invert_m4_m4(ob->imat, ob->obmat);
-                       mul_v3_m4v3(r_center, ob->imat, ED_view3d_cursor3d_get(scene, v3d));
+                       mul_v3_m4v3(r_center, ob->imat, ED_view3d_cursor3d_get(scene, v3d)->location);
                        break;
                }
                case V3D_AROUND_ACTIVE:
@@ -1108,7 +1222,7 @@ static void uv_map_clip_correct_properties(wmOperatorType *ot)
                        "Scale UV coordinates to bounds after unwrapping");
 }
 
-static void uv_map_clip_correct(Scene *scene, Object *ob, BMEditMesh *em, wmOperator *op)
+static void uv_map_clip_correct_multi(Scene *scene, Object **objects, uint objects_len, wmOperator *op)
 {
        BMFace *efa;
        BMLoop *l;
@@ -1119,25 +1233,46 @@ static void uv_map_clip_correct(Scene *scene, Object *ob, BMEditMesh *em, wmOper
        const bool clip_to_bounds = RNA_boolean_get(op->ptr, "clip_to_bounds");
        const bool scale_to_bounds = RNA_boolean_get(op->ptr, "scale_to_bounds");
 
-       const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+       INIT_MINMAX2(min, max);
 
-       /* correct for image aspect ratio */
-       if (correct_aspect)
-               correct_uv_aspect(scene, ob, em);
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+               Object *ob = objects[ob_index];
 
-       if (scale_to_bounds) {
-               INIT_MINMAX2(min, max);
+               BMEditMesh *em = BKE_editmesh_from_object(ob);
+               const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
 
-               BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
-                               continue;
+               /* correct for image aspect ratio */
+               if (correct_aspect)
+                       correct_uv_aspect(scene, ob, em);
 
-                       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-                               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-                               minmax_v2v2_v2(min, max, luv->uv);
+               if (scale_to_bounds) {
+                       /* find uv limits */
+                       BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                               if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
+                                       continue;
+
+                               BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) {
+                                       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+                                       minmax_v2v2_v2(min, max, luv->uv);
+                               }
                        }
                }
+               else if (clip_to_bounds) {
+                       /* clipping and wrapping */
+                       BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                               if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
+                                       continue;
 
+                               BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) {
+                                       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+                                       CLAMP(luv->uv[0], 0.0f, 1.0f);
+                                       CLAMP(luv->uv[1], 0.0f, 1.0f);
+                               }
+                       }
+               }
+       }
+
+       if (scale_to_bounds) {
                /* rescale UV to be in 1/1 */
                dx = (max[0] - min[0]);
                dy = (max[1] - min[1]);
@@ -1147,37 +1282,36 @@ static void uv_map_clip_correct(Scene *scene, Object *ob, BMEditMesh *em, wmOper
                if (dy > 0.0f)
                        dy = 1.0f / dy;
 
-               BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
-                               continue;
+               for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+                       Object *ob = objects[ob_index];
 
-                       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-                               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+                       BMEditMesh *em = BKE_editmesh_from_object(ob);
+                       const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
 
-                               luv->uv[0] = (luv->uv[0] - min[0]) * dx;
-                               luv->uv[1] = (luv->uv[1] - min[1]) * dy;
-                       }
-               }
-       }
-       else if (clip_to_bounds) {
-               /* clipping and wrapping */
-               BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
-                               continue;
+                       BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                               if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
+                                       continue;
 
-                       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-                               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-                               CLAMP(luv->uv[0], 0.0f, 1.0f);
-                               CLAMP(luv->uv[1], 0.0f, 1.0f);
+                               BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) {
+                                       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+
+                                       luv->uv[0] = (luv->uv[0] - min[0]) * dx;
+                                       luv->uv[1] = (luv->uv[1] - min[1]) * dy;
+                               }
                        }
                }
        }
 }
 
+static void uv_map_clip_correct(Scene *scene, Object *ob, wmOperator *op)
+{
+       uv_map_clip_correct_multi(scene, &ob, 1, op);
+}
+
 /* ******************** Unwrap operator **************** */
 
 /* assumes UV Map is checked, doesn't run update funcs */
-void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel)
+void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel, const bool pack)
 {
        BMEditMesh *em = BKE_editmesh_from_object(obedit);
        ParamHandle *handle;
@@ -1198,7 +1332,10 @@ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel)
        param_lscm_end(handle);
 
        param_average(handle);
-       param_pack(handle, scene->toolsettings->uvcalc_margin, false);
+
+       if (pack) {
+               param_pack(handle, scene->toolsettings->uvcalc_margin, false);
+       }
 
        param_flush(handle);
 
@@ -1207,33 +1344,48 @@ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel)
 
 static int unwrap_exec(bContext *C, wmOperator *op)
 {
+       ViewLayer *view_layer = CTX_data_view_layer(C);
        Scene *scene = CTX_data_scene(C);
-       Object *obedit = CTX_data_edit_object(C);
-       BMEditMesh *em = BKE_editmesh_from_object(obedit);
        int method = RNA_enum_get(op->ptr, "method");
        const bool fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
        const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect");
        const bool use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data");
-       bool use_subsurf_final;
        float obsize[3];
        bool implicit = false;
 
-       if (!uvedit_have_selection(scene, em, implicit)) {
+       uint objects_len = 0;
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
+
+       if (!uvedit_have_selection_multi(scene, objects, objects_len, implicit)) {
+               MEM_freeN(objects);
                return OPERATOR_CANCELLED;
        }
 
        /* add uvs if they don't exist yet */
-       if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
-               return OPERATOR_CANCELLED;
-       }
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+               Object *obedit = objects[ob_index];
+               bool use_subsurf_final;
+
+               if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
+                       continue;
+               }
 
-       mat4_to_size(obsize, obedit->obmat);
-       if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f))
-               BKE_report(op->reports, RPT_INFO,
-                          "Object has non-uniform scale, unwrap will operate on a non-scaled version of the mesh");
-       else if (is_negative_m4(obedit->obmat))
-               BKE_report(op->reports, RPT_INFO,
-                          "Object has negative scale, unwrap will operate on a non-flipped version of the mesh");
+               mat4_to_size(obsize, obedit->obmat);
+               if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f))
+                       BKE_report(op->reports, RPT_INFO,
+                                  "Object has non-uniform scale, unwrap will operate on a non-scaled version of the mesh");
+               else if (is_negative_m4(obedit->obmat))
+                       BKE_report(op->reports, RPT_INFO,
+                                  "Object has negative scale, unwrap will operate on a non-flipped version of the mesh");
+
+
+               /* double up the check here but better keep ED_unwrap_lscm interface simple and not
+                * pass operator for warning append */
+               modifier_unwrap_state(obedit, scene, &use_subsurf_final);
+               if (use_subsurf != use_subsurf_final) {
+                       BKE_report(op->reports, RPT_INFO, "Subdivision Surface modifier needs to be first to work with unwrap");
+               }
+       }
 
        /* remember last method for live unwrap */
        if (RNA_struct_property_is_set(op->ptr, "method"))
@@ -1256,17 +1408,17 @@ static int unwrap_exec(bContext *C, wmOperator *op)
        if (use_subsurf) scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF;
        else scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF;
 
-       /* double up the check here but better keep ED_unwrap_lscm interface simple and not
-        * pass operator for warning append */
-       modifier_unwrap_state(obedit, scene, &use_subsurf_final);
-       if (use_subsurf != use_subsurf_final)
-               BKE_report(op->reports, RPT_INFO, "Subdivision Surface modifier needs to be first to work with unwrap");
-
        /* execute unwrap */
-       ED_unwrap_lscm(scene, obedit, true);
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+               Object *obedit = objects[ob_index];
+               ED_unwrap_lscm(scene, obedit, true, false);
+               DEG_id_tag_update(obedit->data, 0);
+               WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+       }
 
-       DAG_id_tag_update(obedit->data, 0);
-       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+       ED_uvedit_pack_islands_multi(scene, objects, objects_len, true, true, true);
+
+       MEM_freeN(objects);
 
        return OPERATOR_FINISHED;
 }
@@ -1321,9 +1473,8 @@ static int uv_from_view_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSE
 
 static int uv_from_view_exec(bContext *C, wmOperator *op)
 {
+       ViewLayer *view_layer = CTX_data_view_layer(C);
        Scene *scene = CTX_data_scene(C);
-       Object *obedit = CTX_data_edit_object(C);
-       BMEditMesh *em = BKE_editmesh_from_object(obedit);
        ARegion *ar = CTX_wm_region(C);
        View3D *v3d = CTX_wm_view3d(C);
        RegionView3D *rv3d = CTX_wm_region_view3d(C);
@@ -1333,69 +1484,98 @@ static int uv_from_view_exec(bContext *C, wmOperator *op)
        BMIter iter, liter;
        MLoopUV *luv;
        float rotmat[4][4];
+       bool changed_multi = false;
+
+       /* Note: objects that aren't touched are set to NULL (to skip clipping). */
+       uint objects_len = 0;
+       Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, &objects_len);
+       for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
+               Object *obedit = objects[ob_index];
+               BMEditMesh *em = BKE_editmesh_from_object(obedit);
+               bool changed = false;
+
+               /* add uvs if they don't exist yet */
+               if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
+                       continue;
+               }
 
-       int cd_loop_uv_offset;
-
-       /* add uvs if they don't exist yet */
-       if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
-               return OPERATOR_CANCELLED;
-       }
+               const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
 
-       cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+               if (RNA_boolean_get(op->ptr, "orthographic")) {
+                       uv_map_rotation_matrix(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f);
 
-       if (RNA_boolean_get(op->ptr, "orthographic")) {
-               uv_map_rotation_matrix(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f);
+                       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                               if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
+                                       continue;
 
-               BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
-                               continue;
+                               BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+                                       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+                                       BLI_uvproject_from_view_ortho(luv->uv, l->v->co, rotmat);
+                               }
+                               changed = true;
+                       }
+               }
+               else if (camera) {
+                       const bool camera_bounds = RNA_boolean_get(op->ptr, "camera_bounds");
+                       struct ProjCameraInfo *uci = BLI_uvproject_camera_info(
+                               v3d->camera, obedit->obmat,
+                               camera_bounds ? (scene->r.xsch * scene->r.xasp) : 1.0f,
+                               camera_bounds ? (scene->r.ysch * scene->r.yasp) : 1.0f);
+
+                       if (uci) {
+                               BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
+                                               continue;
+
+                                       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+                                               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+                                               BLI_uvproject_from_camera(luv->uv, l->v->co, uci);
+                                       }
+                                       changed = true;
+                               }
 
-                       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-                               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-                               BLI_uvproject_from_view_ortho(luv->uv, l->v->co, rotmat);
+                               MEM_freeN(uci);
                        }
                }
-       }
-       else if (camera) {
-               const bool camera_bounds = RNA_boolean_get(op->ptr, "camera_bounds");
-               struct ProjCameraInfo *uci = BLI_uvproject_camera_info(v3d->camera, obedit->obmat,
-                                                                      camera_bounds ? (scene->r.xsch * scene->r.xasp) : 1.0f,
-                                                                      camera_bounds ? (scene->r.ysch * scene->r.yasp) : 1.0f);
+               else {
+                       copy_m4_m4(rotmat, obedit->obmat);
 
-               if (uci) {
                        BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
                                if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
                                        continue;
 
                                BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
                                        luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-                                       BLI_uvproject_from_camera(luv->uv, l->v->co, uci);
+                                       BLI_uvproject_from_view(luv->uv, l->v->co, rv3d->persmat, rotmat, ar->winx, ar->winy);
                                }
+                               changed = true;
                        }
+               }
 
-                       MEM_freeN(uci);
+               if (changed) {
+                       changed_multi = true;
+                       DEG_id_tag_update(obedit->data, 0);
+                       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+               }
+               else {
+                       ARRAY_DELETE_REORDER_LAST(objects, ob_index, 1, objects_len);
+                       objects_len -= 1;
+                       ob_index -= 1;
                }
        }
-       else {
-               copy_m4_m4(rotmat, obedit->obmat);
 
-               BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
-                               continue;
-
-                       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-                               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
-                               BLI_uvproject_from_view(luv->uv, l->v->co, rv3d->persmat, rotmat, ar->winx, ar->winy);
-                       }
-               }
+       if (changed_multi) {
+               uv_map_clip_correct_multi(scene, objects, objects_len, op);
        }
 
-       uv_map_clip_correct(scene, obedit, em, op);
+       MEM_freeN(objects);
 
-       DAG_id_tag_update(obedit->data, 0);
-       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
-
-       return OPERATOR_FINISHED;
+       if (changed_multi) {
+               return OPERATOR_FINISHED;
+       }
+       else {
+               return OPERATOR_CANCELLED;
+       }
 }
 
 static int uv_from_view_poll(bContext *C)
@@ -1435,18 +1615,30 @@ void UV_OT_project_from_view(wmOperatorType *ot)
 static int reset_exec(bContext *C, wmOperator *UNUSED(op))
 {
        Scene *scene = CTX_data_scene(C);
-       Object *obedit = CTX_data_edit_object(C);
-       Mesh *me = (Mesh *)obedit->data;
 
-       /* add uvs if they don't exist yet */
-       if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
-               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];
+               Mesh *me = (Mesh *)obedit->data;
+               BMEditMesh *em = BKE_editmesh_from_object(obedit);
+
+               if (em->bm->totfacesel == 0) {
+                       continue;
+               }
 
-       ED_mesh_uv_loop_reset(C, me);
+               /* add uvs if they don't exist yet */
+               if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
+                       continue;
+               }
 
-       DAG_id_tag_update(obedit->data, 0);
-       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+               ED_mesh_uv_loop_reset(C, me);
+
+               DEG_id_tag_update(obedit->data, 0);
+               WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+       }
+       MEM_freeN(objects);
 
        return OPERATOR_FINISHED;
 }
@@ -1481,7 +1673,7 @@ static void uv_sphere_project(float target[2], float source[3], float center[3],
                target[0] -= 1.0f;
 }
 
-static void uv_map_mirror(BMEditMesh *em, BMFace *efa, MTexPoly *UNUSED(tf))
+static void uv_map_mirror(BMEditMesh *em, BMFace *efa)
 {
        BMLoop *l;
        BMIter liter;
@@ -1513,46 +1705,53 @@ static void uv_map_mirror(BMEditMesh *em, BMFace *efa, MTexPoly *UNUSED(tf))
 static int sphere_project_exec(bContext *C, wmOperator *op)
 {
        Scene *scene = CTX_data_scene(C);
-       Object *obedit = CTX_data_edit_object(C);
        View3D *v3d = CTX_wm_view3d(C);
-       BMEditMesh *em = BKE_editmesh_from_object(obedit);
-       BMFace *efa;
-       BMLoop *l;
-       BMIter iter, liter;
-       MTexPoly *tf;
-       MLoopUV *luv;
-       float center[3], rotmat[4][4];
 
-       int cd_loop_uv_offset;
+       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);
+               BMFace *efa;
+               BMLoop *l;
+               BMIter iter, liter;
+               MLoopUV *luv;
 
-       /* add uvs if they don't exist yet */
-       if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
-               return OPERATOR_CANCELLED;
-       }
+               if (em->bm->totfacesel == 0) {
+                       continue;
+               }
 
-       cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+               /* add uvs if they don't exist yet */
+               if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
+                       continue;
+               }
 
-       uv_map_transform(C, op, rotmat);
-       uv_map_transform_center(scene, v3d, obedit, em, center, NULL);
+               const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+               float center[3], rotmat[4][4];
 
-       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-               if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
-                       continue;
+               uv_map_transform(C, op, rotmat);
+               uv_map_transform_center(scene, v3d, obedit, em, center, NULL);
 
-               BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-                       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+               BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
+                               continue;
 
-                       uv_sphere_project(luv->uv, l->v->co, center, rotmat);
-               }
+                       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+                               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
 
-               tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
-               uv_map_mirror(em, efa, tf);
-       }
+                               uv_sphere_project(luv->uv, l->v->co, center, rotmat);
+                       }
 
-       uv_map_clip_correct(scene, obedit, em, op);
+                       uv_map_mirror(em, efa);
+               }
 
-       DAG_id_tag_update(obedit->data, 0);
-       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+               uv_map_clip_correct(scene, obedit, op);
+
+               DEG_id_tag_update(obedit->data, 0);
+               WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+       }
+       MEM_freeN(objects);
 
        return OPERATOR_FINISHED;
 }
@@ -1594,46 +1793,53 @@ static void uv_cylinder_project(float target[2], float source[3], float center[3
 static int cylinder_project_exec(bContext *C, wmOperator *op)
 {
        Scene *scene = CTX_data_scene(C);
-       Object *obedit = CTX_data_edit_object(C);
        View3D *v3d = CTX_wm_view3d(C);
-       BMEditMesh *em = BKE_editmesh_from_object(obedit);
-       BMFace *efa;
-       BMLoop *l;
-       BMIter iter, liter;
-       MTexPoly *tf;
-       MLoopUV *luv;
-       float center[3], rotmat[4][4];
 
-       int cd_loop_uv_offset;
+       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);
+               BMFace *efa;
+               BMLoop *l;
+               BMIter iter, liter;
+               MLoopUV *luv;
 
-       /* add uvs if they don't exist yet */
-       if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
-               return OPERATOR_CANCELLED;
-       }
+               if (em->bm->totfacesel == 0) {
+                       continue;
+               }
 
-       cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+               /* add uvs if they don't exist yet */
+               if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
+                       continue;
+               }
 
-       uv_map_transform(C, op, rotmat);
-       uv_map_transform_center(scene, v3d, obedit, em, center, NULL);
+               const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
+               float center[3], rotmat[4][4];
 
-       BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
-               if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
-                       continue;
+               uv_map_transform(C, op, rotmat);
+               uv_map_transform_center(scene, v3d, obedit, em, center, NULL);
 
-               BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
-                       luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
+               BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
+                       if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
+                               continue;
 
-                       uv_cylinder_project(luv->uv, l->v->co, center, rotmat);
-               }
+                       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
+                               luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
 
-               tf = CustomData_bmesh_get(&em->bm->pdata, efa->head.data, CD_MTEXPOLY);
-               uv_map_mirror(em, efa, tf);
-       }
+                               uv_cylinder_project(luv->uv, l->v->co, center, rotmat);
+                       }
+
+                       uv_map_mirror(em, efa);
+               }
 
-       uv_map_clip_correct(scene, obedit, em, op);
+               uv_map_clip_correct(scene, obedit, op);
 
-       DAG_id_tag_update(obedit->data, 0);
-       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+               DEG_id_tag_update(obedit->data, 0);
+               WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+       }
+       MEM_freeN(objects);
 
        return OPERATOR_FINISHED;
 }
@@ -1663,7 +1869,6 @@ void ED_uvedit_unwrap_cube_project(BMesh *bm, float cube_size, bool use_select,
        BMFace *efa;
        BMLoop *l;
        BMIter iter, liter;
-       /* MTexPoly *tf; */ /* UNUSED */
        MLoopUV *luv;
        float loc[3];
        int cox, coy;
@@ -1701,40 +1906,57 @@ static int cube_project_exec(bContext *C, wmOperator *op)
 {
        Scene *scene = CTX_data_scene(C);
        View3D *v3d = CTX_wm_view3d(C);
-       Object *obedit = CTX_data_edit_object(C);
-       BMEditMesh *em = BKE_editmesh_from_object(obedit);
+
        PropertyRNA *prop_cube_size = RNA_struct_find_property(op->ptr, "cube_size");
-       float cube_size = RNA_property_float_get(op->ptr, prop_cube_size);
-       float center[3];
-       float bounds[2][3];
-       float (*bounds_buf)[3] = NULL;
+       const float cube_size_init = RNA_property_float_get(op->ptr, prop_cube_size);
 
-       /* add uvs if they don't exist yet */
-       if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
-               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];
+               BMEditMesh *em = BKE_editmesh_from_object(obedit);
 
-       if (!RNA_property_is_set(op->ptr, prop_cube_size)) {
-               bounds_buf = bounds;
-       }
+               if (em->bm->totfacesel == 0) {
+                       continue;
+               }
 
-       uv_map_transform_center(scene, v3d, obedit, em, center, bounds_buf);
+               /* add uvs if they don't exist yet */
+               if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
+                       continue;
+               }
 
-       /* calculate based on bounds */
-       if (bounds_buf) {
-               float dims[3];
-               sub_v3_v3v3(dims, bounds[1], bounds[0]);
-               cube_size = max_fff(UNPACK3(dims));
-               cube_size = cube_size ? 2.0f / cube_size : 1.0f;
-               RNA_property_float_set(op->ptr, prop_cube_size, cube_size);
-       }
+               float bounds[2][3];
+               float (*bounds_buf)[3] = NULL;
+
+               if (!RNA_property_is_set(op->ptr, prop_cube_size)) {
+                       bounds_buf = bounds;
+               }
+
+               float center[3];
+               uv_map_transform_center(scene, v3d, obedit, em, center, bounds_buf);
+
+               /* calculate based on bounds */
+               float cube_size = cube_size_init;
+               if (bounds_buf) {
+                       float dims[3];
+                       sub_v3_v3v3(dims, bounds[1], bounds[0]);
+                       cube_size = max_fff(UNPACK3(dims));
+                       cube_size = cube_size ? 2.0f / cube_size : 1.0f;
+                       if (ob_index == 0) {
+                               /* This doesn't fit well with, multiple objects. */
+                               RNA_property_float_set(op->ptr, prop_cube_size, cube_size);
+                       }
+               }
 
-       ED_uvedit_unwrap_cube_project(em->bm, cube_size, true, center);
+               ED_uvedit_unwrap_cube_project(em->bm, cube_size, true, center);
 
-       uv_map_clip_correct(scene, obedit, em, op);
+               uv_map_clip_correct(scene, obedit, op);
 
-       DAG_id_tag_update(obedit->data, 0);
-       WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+               DEG_id_tag_update(obedit->data, 0);
+               WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
+       }
+       MEM_freeN(objects);
 
        return OPERATOR_FINISHED;
 }