Merge remote-tracking branch 'origin/master' into blender2.8
authorRay Molenkamp <github@lazydodo.com>
Fri, 19 Oct 2018 15:16:26 +0000 (09:16 -0600)
committerRay Molenkamp <github@lazydodo.com>
Fri, 19 Oct 2018 15:16:26 +0000 (09:16 -0600)
1  2 
source/blender/editors/uvedit/uvedit_unwrap_ops.c

index 538ca9afafa2fa092cb751c3c788a5bea8646cf4,088ed42b0c032a89ffadb946712aef88035e66db..b344bb423186283ab05942fa1ad9749f4ab151ec
  #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"
  
@@@ -103,8 -100,9 +103,8 @@@ static void modifier_unwrap_state(Objec
        *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;
        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))
                }
        }
  
 -      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;
@@@ -199,22 -200,7 +199,22 @@@ static bool uvedit_have_selection(Scen
        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;
        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);
        }
@@@ -262,14 -254,12 +262,14 @@@ static void construct_param_handle_face
                select[i] = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
        }
  
-       param_face_add(handle, key, i, vkeys, co, uv, pin, select, efa->no);
+       param_face_add(handle, key, i, vkeys, co, uv, pin, select);
  }
  
 -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;
        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)
@@@ -496,7 -403,7 +496,7 @@@ static ParamHandle *construct_param_han
        smd.subdivType = smd_real->subdivType;
  
        initialDerived = CDDM_from_editbmesh(em, false, false);
 -      derivedMesh = subsurf_make_derived_from_derived(initialDerived, &smd,
 +      derivedMesh = subsurf_make_derived_from_derived(initialDerived, &smd, scene,
                                                        NULL, SUBSURF_IN_EDIT_MODE);
  
        initialDerived->release(initialDerived);
                texface_from_original_index(origFace, origVertIndices[mloop[2].v], &uv[2], &pin[2], &select[2], scene, cd_loop_uv_offset);
                texface_from_original_index(origFace, origVertIndices[mloop[3].v], &uv[3], &pin[3], &select[3], scene, cd_loop_uv_offset);
  
-               param_face_add(handle, key, 4, vkeys, co, uv, pin, select, NULL);
+               param_face_add(handle, key, 4, vkeys, co, uv, pin, select);
        }
  
        /* these are calculated from original mesh too */
  
  typedef struct MinStretch {
        Scene *scene;
 -      Object *obedit;
 -      BMEditMesh *em;
 +      Object **objects_edit;
 +      uint objects_len;
        ParamHandle *handle;
        float blend;
        double lasttime;
  static bool minimize_stretch_init(bContext *C, wmOperator *op)
  {
        Scene *scene = CTX_data_scene(C);
 -      Object *obedit = CTX_data_edit_object(C);
 -      BMEditMesh *em = BKE_editmesh_from_object(obedit);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +
        MinStretch *ms;
        const bool fill_holes = RNA_boolean_get(op->ptr, "fill_holes");
        bool implicit = true;
  
 -      if (!uvedit_have_selection(scene, em, implicit)) {
 +      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, implicit)) {
 +              MEM_freeN(objects);
                return false;
        }
  
        ms = MEM_callocN(sizeof(MinStretch), "MinStretch");
        ms->scene = scene;
 -      ms->obedit = obedit;
 -      ms->em = em;
 +      ms->objects_edit = objects;
 +      ms->objects_len = objects_len;
        ms->blend = RNA_float_get(op->ptr, "blend");
        ms->iterations = RNA_int_get(op->ptr, "iterations");
        ms->i = 0;
 -      ms->handle = construct_param_handle(scene, obedit, em->bm, implicit, fill_holes, 1, 1);
 +      ms->handle = construct_param_handle_multi(scene, objects, objects_len, implicit, fill_holes, true, true);
        ms->lasttime = PIL_check_seconds_timer();
  
        param_stretch_begin(ms->handle);
@@@ -647,9 -550,6 +647,9 @@@ static void minimize_stretch_iteration(
  {
        MinStretch *ms = op->customdata;
        ScrArea *sa = CTX_wm_area(C);
 +      Scene *scene = CTX_data_scene(C);
 +      ToolSettings *ts = scene->toolsettings;
 +      const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
  
        param_stretch_blend(ms->handle, ms->blend);
        param_stretch_iter(ms->handle);
                param_flush(ms->handle);
  
                if (sa) {
 -                      BLI_snprintf(str, sizeof(str),
 -                                   IFACE_("Minimize Stretch. Blend %.2f (Press + and -, or scroll wheel to set)"), ms->blend);
 -                      ED_area_headerprint(sa, str);
 +                      BLI_snprintf(str, sizeof(str), IFACE_("Minimize Stretch. Blend %.2f"), ms->blend);
 +                      ED_area_status_text(sa, str);
 +                      ED_workspace_status_text(C, IFACE_("Press + and -, or scroll wheel to set blending"));
                }
  
                ms->lasttime = PIL_check_seconds_timer();
  
 -              DAG_id_tag_update(ms->obedit->data, 0);
 -              WM_event_add_notifier(C, NC_GEOM | ND_DATA, ms->obedit->data);
 +              for (uint ob_index = 0; ob_index < ms->objects_len; ob_index++) {
 +                      Object *obedit = ms->objects_edit[ob_index];
 +                      BMEditMesh *em = BKE_editmesh_from_object(obedit);
 +
 +                      if (synced_selection && (em->bm->totfacesel == 0)) {
 +                              continue;
 +                      }
 +
 +                      DEG_id_tag_update(obedit->data, 0);
 +                      WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
 +              }
        }
  }
  
@@@ -688,13 -579,9 +688,13 @@@ static void minimize_stretch_exit(bCont
  {
        MinStretch *ms = op->customdata;
        ScrArea *sa = CTX_wm_area(C);
 +      Scene *scene = CTX_data_scene(C);
 +      ToolSettings *ts = scene->toolsettings;
 +      const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
 +
 +      ED_area_status_text(sa, NULL);
 +      ED_workspace_status_text(C, NULL);
  
 -      if (sa)
 -              ED_area_headerprint(sa, NULL);
        if (ms->timer)
                WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), ms->timer);
  
        param_stretch_end(ms->handle);
        param_delete(ms->handle);
  
 -      DAG_id_tag_update(ms->obedit->data, 0);
 -      WM_event_add_notifier(C, NC_GEOM | ND_DATA, ms->obedit->data);
 +      for (uint ob_index = 0; ob_index < ms->objects_len; ob_index++) {
 +              Object *obedit = ms->objects_edit[ob_index];
 +              BMEditMesh *em = BKE_editmesh_from_object(obedit);
 +
 +              if (synced_selection && (em->bm->totfacesel == 0)) {
 +                      continue;
 +              }
  
 +              DEG_id_tag_update(obedit->data, 0);
 +              WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
 +      }
 +
 +      MEM_freeN(ms->objects_edit);
        MEM_freeN(ms);
        op->customdata = NULL;
  }
@@@ -837,7 -714,6 +837,7 @@@ void UV_OT_minimize_stretch(wmOperatorT
  
  /* ******************** Pack Islands operator **************** */
  
 +
  void ED_uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm, bool selected, bool correct_aspect, bool do_rotate)
  {
        ParamHandle *handle;
        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;
        }
  
        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);
 +
 +      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);
 +      }
  
 -      DAG_id_tag_update(obedit->data, 0);
 -      WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
 +      MEM_freeN(objects);
  
        return OPERATOR_FINISHED;
  }
@@@ -914,37 -770,23 +914,37 @@@ void UV_OT_pack_islands(wmOperatorType 
  static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op))
  {
        Scene *scene = CTX_data_scene(C);
 -      Object *obedit = CTX_data_edit_object(C);
 -      BMEditMesh *em = BKE_editmesh_from_object(obedit);
 +      ViewLayer *view_layer = CTX_data_view_layer(C);
 +      ToolSettings *ts = scene->toolsettings;
 +      const bool synced_selection = (ts->uv_flag & UV_SYNC_SELECTION) != 0;
 +      const bool implicit = true;
        ParamHandle *handle;
 -      bool implicit = true;
  
 -      if (!uvedit_have_selection(scene, em, implicit)) {
 +      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, implicit)) {
 +              MEM_freeN(objects);
                return OPERATOR_CANCELLED;
        }
  
 -      handle = construct_param_handle(scene, obedit, em->bm, implicit, 0, 1, 1);
 +      handle = construct_param_handle_multi(scene, objects, objects_len, implicit, false, true, true);
        param_average(handle);
        param_flush(handle);
        param_delete(handle);
  
 -      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];
 +              BMEditMesh *em = BKE_editmesh_from_object(obedit);
  
 +              if (synced_selection && (em->bm->totvertsel == 0)) {
 +                      continue;
 +              }
 +
 +              DEG_id_tag_update(obedit->data, 0);
 +              WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
 +      }
 +      MEM_freeN(objects);
        return OPERATOR_FINISHED;
  }
  
@@@ -964,14 -806,10 +964,14 @@@ void UV_OT_average_islands_scale(wmOper
  
  /**************** Live Unwrap *****************/
  
 -static ParamHandle *liveHandle = NULL;
 +static struct {
 +      ParamHandle **handles;
 +      uint len, len_alloc;
 +} g_live_unwrap = {NULL};
  
  void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit)
  {
 +      ParamHandle *handle = NULL;
        BMEditMesh *em = BKE_editmesh_from_object(obedit);
        const bool abf = (scene->toolsettings->unwrapper == 0);
        const bool fillholes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0;
        }
  
        if (use_subsurf)
 -              liveHandle = construct_param_handle_subsurfed(scene, obedit, em, fillholes, false, true);
 +              handle = construct_param_handle_subsurfed(scene, obedit, em, fillholes, false, true);
        else
 -              liveHandle = construct_param_handle(scene, obedit, em->bm, false, fillholes, false, true);
 +              handle = construct_param_handle(scene, obedit, em->bm, false, fillholes, false, true);
 +
 +      param_lscm_begin(handle, PARAM_TRUE, abf);
  
 -      param_lscm_begin(liveHandle, PARAM_TRUE, abf);
 +      /* Create or increase size of g_live_unwrap.handles array */
 +      if (g_live_unwrap.handles == NULL) {
 +              g_live_unwrap.len_alloc = 32;
 +              g_live_unwrap.handles = MEM_mallocN(sizeof(ParamHandle *) * g_live_unwrap.len_alloc, "uvedit_live_unwrap_liveHandles");
 +              g_live_unwrap.len = 0;
 +      }
 +      if (g_live_unwrap.len >= g_live_unwrap.len_alloc) {
 +              g_live_unwrap.len_alloc *= 2;
 +              g_live_unwrap.handles = MEM_reallocN(g_live_unwrap.handles, sizeof(ParamHandle *) * g_live_unwrap.len_alloc);
 +      }
 +      g_live_unwrap.handles[g_live_unwrap.len] = handle;
 +      g_live_unwrap.len++;
  }
  
  void ED_uvedit_live_unwrap_re_solve(void)
  {
 -      if (liveHandle) {
 -              param_lscm_solve(liveHandle);
 -              param_flush(liveHandle);
 +      if (g_live_unwrap.handles) {
 +              for (int i = 0; i < g_live_unwrap.len; i++) {
 +                      param_lscm_solve(g_live_unwrap.handles[i]);
 +                      param_flush(g_live_unwrap.handles[i]);
 +              }
        }
  }
  
  void ED_uvedit_live_unwrap_end(short cancel)
  {
 -      if (liveHandle) {
 -              param_lscm_end(liveHandle);
 -              if (cancel)
 -                      param_flush_restore(liveHandle);
 -              param_delete(liveHandle);
 -              liveHandle = NULL;
 +      if (g_live_unwrap.handles) {
 +              for (int i = 0; i < g_live_unwrap.len; i++) {
 +                      param_lscm_end(g_live_unwrap.handles[i]);
 +                      if (cancel)
 +                              param_flush_restore(g_live_unwrap.handles[i]);
 +                      param_delete(g_live_unwrap.handles[i]);
 +              }
 +              MEM_freeN(g_live_unwrap.handles);
 +              g_live_unwrap.handles = NULL;
 +              g_live_unwrap.len = 0;
 +              g_live_unwrap.len_alloc = 0;
        }
  }
  
@@@ -1037,7 -855,7 +1037,7 @@@ void ED_uvedit_live_unwrap(Scene *scene
        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 */
        }
  }
  
@@@ -1085,7 -903,7 +1085,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]);
                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:
        }
  }
  
 -static void uv_map_rotation_matrix(float result[4][4], RegionView3D *rv3d, Object *ob,
 -                                   float upangledeg, float sideangledeg, float radius)
 +static void uv_map_rotation_matrix_ex(
 +        float result[4][4], RegionView3D *rv3d, Object *ob,
 +        float upangledeg, float sideangledeg, float radius, float offset[4])
  {
        float rotup[4][4], rotside[4][4], viewmatrix[4][4], rotobj[4][4];
        float sideangle = 0.0f, upangle = 0.0f;
 -      int k;
  
        /* get rotation of the current view matrix */
        if (rv3d)
                unit_m4(viewmatrix);
  
        /* but shifting */
 -      for (k = 0; k < 4; k++)
 -              viewmatrix[3][k] = 0.0f;
 +      copy_v4_fl(viewmatrix[3], 0.0f);
  
        /* get rotation of the current object matrix */
        copy_m4_m4(rotobj, ob->obmat);
  
        /* but shifting */
 -      for (k = 0; k < 4; k++)
 -              rotobj[3][k] = 0.0f;
 +      add_v4_v4(rotobj[3], offset);
 +      rotobj[3][3] = 0.0f;
  
        zero_m4(rotup);
        zero_m4(rotside);
        mul_m4_series(result, rotup, rotside, viewmatrix, rotobj);
  }
  
 +static void uv_map_rotation_matrix(
 +        float result[4][4], RegionView3D *rv3d, Object *ob,
 +        float upangledeg, float sideangledeg, float radius)
 +{
 +      float offset[4] = {0};
 +      uv_map_rotation_matrix_ex(result, rv3d, ob, upangledeg, sideangledeg, radius, offset);
 +}
 +
  static void uv_map_transform(bContext *C, wmOperator *op, float rotmat[4][4])
  {
        /* context checks are messy here, making it work in both 3d view and uv editor */
@@@ -1297,7 -1108,7 +1297,7 @@@ static void uv_map_clip_correct_propert
                        "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;
        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]);
                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;
        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);
  
        param_delete(handle);
  }
  
 +enum {
 +      UNWRAP_ERROR_NONUNIFORM = (1 << 0),
 +      UNWRAP_ERROR_NEGATIVE = (1 << 1),
 +};
 +
  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;
 +      int reported_errors = 0;
 +      /* We will report an error unless at least one object has the subsurf modifier in the right place. */
 +      bool subsurf_error = use_subsurf;
 +
 +      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(scene, em, implicit)) {
 +      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];
 +              float obsize[3];
 +              bool use_subsurf_final;
 +
 +              if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
 +                      continue;
 +              }
 +
 +              if (subsurf_error) {
 +                      /* 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_final) {
 +                              subsurf_error = false;
 +                      }
 +              }
 +
 +              if (reported_errors & (UNWRAP_ERROR_NONUNIFORM | UNWRAP_ERROR_NEGATIVE)) {
 +                      continue;
 +              }
 +
 +              mat4_to_size(obsize, obedit->obmat);
 +              if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f)) {
 +                      if ((reported_errors & UNWRAP_ERROR_NONUNIFORM) == 0) {
 +                              BKE_report(op->reports, RPT_INFO,
 +                                        "Object has non-uniform scale, unwrap will operate on a non-scaled version of the mesh");
 +                              reported_errors |= UNWRAP_ERROR_NONUNIFORM;
 +                      }
 +              }
 +              else if (is_negative_m4(obedit->obmat)) {
 +                      if ((reported_errors & UNWRAP_ERROR_NEGATIVE) == 0) {
 +                              BKE_report(op->reports, RPT_INFO,
 +                                         "Object has negative scale, unwrap will operate on a non-flipped version of the mesh");
 +                              reported_errors |= UNWRAP_ERROR_NEGATIVE;
 +                      }
 +              }
        }
  
 -      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");
 +      if (subsurf_error) {
 +              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"))
        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);
 +      }
 +
 +      ED_uvedit_pack_islands_multi(scene, objects, objects_len, true, true, true);
  
 -      DAG_id_tag_update(obedit->data, 0);
 -      WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
 +      MEM_freeN(objects);
  
        return OPERATOR_FINISHED;
  }
@@@ -1573,8 -1321,9 +1573,8 @@@ static int uv_from_view_invoke(bContex
  
  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);
        BMIter iter, liter;
        MLoopUV *luv;
        float rotmat[4][4];
 +      float objects_pos_offset[4];
 +      bool changed_multi = false;
  
 -      int cd_loop_uv_offset;
 +      const bool use_orthographic = RNA_boolean_get(op->ptr, "orthographic");
  
 -      /* add uvs if they don't exist yet */
 -      if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
 -              return OPERATOR_CANCELLED;
 -      }
 +      /* 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);
  
 -      cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
 +      if (use_orthographic) {
 +              /* Calculate average object position. */
 +              float objects_pos_avg[4] = {0};
  
 -      if (RNA_boolean_get(op->ptr, "orthographic")) {
 -              uv_map_rotation_matrix(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f);
 +              for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
 +                      add_v4_v4(objects_pos_avg, objects[ob_index]->obmat[3]);
 +              }
  
 -              BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
 -                      if (!BM_elem_flag_test(efa, BM_ELEM_SELECT))
 -                              continue;
 +              mul_v4_fl(objects_pos_avg, 1.0f / objects_len);
 +              negate_v4_v4(objects_pos_offset, objects_pos_avg);
 +      }
  
 -                      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);
 -                      }
 +      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;
                }
 -      }
 -      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) {
 +              const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
 +
 +              if (use_orthographic) {
 +                      uv_map_rotation_matrix_ex(rotmat, rv3d, obedit, 90.0f, 0.0f, 1.0f, objects_pos_offset);
 +
                        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_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;
 +                              }
  
 -                      MEM_freeN(uci);
 +                              MEM_freeN(uci);
 +                      }
                }
 -      }
 -      else {
 -              copy_m4_m4(rotmat, obedit->obmat);
 +              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_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);
 +                              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);
 +                              }
 +                              changed = true;
                        }
                }
 +
 +              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;
 +              }
        }
  
 -      uv_map_clip_correct(scene, obedit, em, op);
 +      if (changed_multi) {
 +              uv_map_clip_correct_multi(scene, objects, objects_len, op);
 +      }
  
 -      DAG_id_tag_update(obedit->data, 0);
 -      WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
 +      MEM_freeN(objects);
  
 -      return OPERATOR_FINISHED;
 +      if (changed_multi) {
 +              return OPERATOR_FINISHED;
 +      }
 +      else {
 +              return OPERATOR_CANCELLED;
 +      }
  }
  
  static bool uv_from_view_poll(bContext *C)
@@@ -1731,30 -1435,18 +1731,30 @@@ void UV_OT_project_from_view(wmOperator
  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);
  
 -      ED_mesh_uv_loop_reset(C, me);
 +              if (em->bm->totfacesel == 0) {
 +                      continue;
 +              }
  
 -      DAG_id_tag_update(obedit->data, 0);
 -      WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data);
 +              /* add uvs if they don't exist yet */
 +              if (!ED_uvedit_ensure_uvs(C, scene, obedit)) {
 +                      continue;
 +              }
 +
 +              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;
  }
@@@ -1789,7 -1481,7 +1789,7 @@@ static void uv_sphere_project(float tar
                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;
  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);
 +              }
 +
 +              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;
  }
@@@ -1909,53 -1594,46 +1909,53 @@@ static void uv_cylinder_project(float t
  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_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;
  }
@@@ -1985,6 -1663,7 +1985,6 @@@ void ED_uvedit_unwrap_cube_project(BMes
        BMFace *efa;
        BMLoop *l;
        BMIter iter, liter;
 -      /* MTexPoly *tf; */ /* UNUSED */
        MLoopUV *luv;
        float loc[3];
        int cox, coy;
@@@ -2022,57 -1701,40 +2022,57 @@@ static int cube_project_exec(bContext *
  {
        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;
  
 -      ED_uvedit_unwrap_cube_project(em->bm, cube_size, true, center);
 +              if (!RNA_property_is_set(op->ptr, prop_cube_size)) {
 +                      bounds_buf = bounds;
 +              }
  
 -      uv_map_clip_correct(scene, obedit, em, op);
 +              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);
  
 -      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;
  }