Add operator for removing unused material slots
authorLukas Stockner <lukas.stockner@freenet.de>
Wed, 31 Jul 2019 19:04:52 +0000 (12:04 -0700)
committerLukas Stockner <lukas.stockner@freenet.de>
Wed, 31 Jul 2019 19:08:18 +0000 (12:08 -0700)
Reviewers: campbellbarton, brecht

Reviewed By: brecht

Subscribers: brecht

Differential Revision: https://developer.blender.org/D4991

12 files changed:
release/scripts/startup/bl_ui/properties_material.py
source/blender/blenkernel/BKE_curve.h
source/blender/blenkernel/BKE_gpencil.h
source/blender/blenkernel/BKE_material.h
source/blender/blenkernel/BKE_mesh.h
source/blender/blenkernel/intern/curve.c
source/blender/blenkernel/intern/gpencil.c
source/blender/blenkernel/intern/material.c
source/blender/blenkernel/intern/mesh.c
source/blender/editors/render/render_intern.h
source/blender/editors/render/render_ops.c
source/blender/editors/render/render_shading.c

index d9d48debcd4840a756575575371bb90007d6d462..f3c34cf4660e3f61985d8c41ef6f9c363c43b06e 100644 (file)
@@ -32,6 +32,7 @@ class MATERIAL_MT_context_menu(Menu):
         layout.operator("material.copy", icon='COPYDOWN')
         layout.operator("object.material_slot_copy")
         layout.operator("material.paste", icon='PASTEDOWN')
+        layout.operator("object.material_slot_remove_unused")
 
 
 class MATERIAL_UL_matslots(UIList):
index df1e7a7baea278556912dc313cb2e29e6e275a6c..51ff4673aba69a402ca6166467efb3c2a060a64a 100644 (file)
@@ -109,6 +109,7 @@ void BKE_curve_transform(struct Curve *cu,
                          const bool do_props);
 void BKE_curve_translate(struct Curve *cu, float offset[3], const bool do_keys);
 void BKE_curve_material_index_remove(struct Curve *cu, int index);
+bool BKE_curve_material_index_used(struct Curve *cu, int index);
 void BKE_curve_material_index_clear(struct Curve *cu);
 bool BKE_curve_material_index_validate(struct Curve *cu);
 void BKE_curve_material_remap(struct Curve *cu, const unsigned int *remap, unsigned int remap_len);
index ae1000d1b995a1c9c84fe830e3935633ff8e0607..60e9e6b91d3c206752c5f3a94db665ac24c6bb35 100644 (file)
@@ -87,6 +87,7 @@ void BKE_gpencil_frame_delete_laststroke(struct bGPDlayer *gpl, struct bGPDframe
 
 /* materials */
 void BKE_gpencil_material_index_reassign(struct bGPdata *gpd, int totcol, int index);
+bool BKE_gpencil_material_index_used(struct bGPdata *gpd, int index);
 void BKE_gpencil_material_remap(struct bGPdata *gpd,
                                 const unsigned int *remap,
                                 unsigned int remap_len);
index 268cf831456f96d40f84ee8d585d1bf124f42276..b1200c7e608dc087aaa7c7cf2c68c0d8dee43668 100644 (file)
@@ -90,6 +90,7 @@ void assign_matarar(struct Main *bmain, struct Object *ob, struct Material ***ma
 short BKE_object_material_slot_find_index(struct Object *ob, struct Material *ma);
 bool BKE_object_material_slot_add(struct Main *bmain, struct Object *ob);
 bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob);
+bool BKE_object_material_slot_used(struct ID *id, short actcol);
 
 struct MaterialGPencilStyle *BKE_material_gpencil_settings_get(struct Object *ob, short act);
 
index 3b7fe208bb6dd7405dfa71e5022f91dfd3b76167..59812df246f917b5140afbe2a56e917e8fe31a66 100644 (file)
@@ -182,6 +182,7 @@ void BKE_mesh_to_curve(struct Main *bmain,
                        struct Scene *scene,
                        struct Object *ob);
 void BKE_mesh_material_index_remove(struct Mesh *me, short index);
+bool BKE_mesh_material_index_used(struct Mesh *me, short index);
 void BKE_mesh_material_index_clear(struct Mesh *me);
 void BKE_mesh_material_remap(struct Mesh *me, const unsigned int *remap, unsigned int remap_len);
 void BKE_mesh_smooth_flag_set(struct Object *meshOb, int enableSmooth);
index 931c0ed73d3118f2a3361fe5d3579ecb20378e4d..efdfa2c19bb135e4cd5c5720f40d9da7fff6d5c1 100644 (file)
@@ -5313,6 +5313,32 @@ void BKE_curve_material_index_remove(Curve *cu, int index)
   }
 }
 
+bool BKE_curve_material_index_used(Curve *cu, int index)
+{
+  const int curvetype = BKE_curve_type_get(cu);
+
+  if (curvetype == OB_FONT) {
+    struct CharInfo *info = cu->strinfo;
+    int i;
+    for (i = cu->len_wchar - 1; i >= 0; i--, info++) {
+      if (info->mat_nr == index) {
+        return true;
+      }
+    }
+  }
+  else {
+    Nurb *nu;
+
+    for (nu = cu->nurb.first; nu; nu = nu->next) {
+      if (nu->mat_nr == index) {
+        return true;
+      }
+    }
+  }
+
+  return false;
+}
+
 void BKE_curve_material_index_clear(Curve *cu)
 {
   const int curvetype = BKE_curve_type_get(cu);
index 058c0d10b8e22f1c4cac8e2771d8cad153a85544..7d732235f7e51e5de963d1e13a49d4dff13b7e9f 100644 (file)
@@ -1662,6 +1662,22 @@ void BKE_gpencil_material_index_reassign(bGPdata *gpd, int totcol, int index)
   }
 }
 
+/* remove strokes using a material */
+bool BKE_gpencil_material_index_used(bGPdata *gpd, int index)
+{
+  for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+    for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+      for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+        if (gps->mat_nr == index) {
+          return true;
+        }
+      }
+    }
+  }
+
+  return false;
+}
+
 void BKE_gpencil_material_remap(struct bGPdata *gpd,
                                 const unsigned int *remap,
                                 unsigned int remap_len)
index 391af8b96aba84a54d85e94519d5ea430def3a45..1382b8636377f77906945f315e694f89c00444a7 100644 (file)
@@ -366,6 +366,26 @@ static void material_data_index_remove_id(ID *id, short index)
   }
 }
 
+bool BKE_object_material_slot_used(ID *id, short actcol)
+{
+  /* ensure we don't try get materials from non-obdata */
+  BLI_assert(OB_DATA_SUPPORT_ID(GS(id->name)));
+
+  switch (GS(id->name)) {
+    case ID_ME:
+      return BKE_mesh_material_index_used((Mesh *)id, actcol - 1);
+    case ID_CU:
+      return BKE_curve_material_index_used((Curve *)id, actcol - 1);
+    case ID_MB:
+      /* meta-elems don't have materials atm */
+      return false;
+    case ID_GD:
+      return BKE_gpencil_material_index_used((bGPdata *)id, actcol - 1);
+    default:
+      return false;
+  }
+}
+
 static void material_data_index_clear_id(ID *id)
 {
   /* ensure we don't try get materials from non-obdata */
index f38161546e27f451a5f77949bb1f60ebbb27f082..7e755e54eaa7d8bf999a9029923bbe72252032d3 100644 (file)
@@ -1206,6 +1206,27 @@ void BKE_mesh_material_index_remove(Mesh *me, short index)
   }
 }
 
+bool BKE_mesh_material_index_used(Mesh *me, short index)
+{
+  MPoly *mp;
+  MFace *mf;
+  int i;
+
+  for (mp = me->mpoly, i = 0; i < me->totpoly; i++, mp++) {
+    if (mp->mat_nr == index) {
+      return true;
+    }
+  }
+
+  for (mf = me->mface, i = 0; i < me->totface; i++, mf++) {
+    if (mf->mat_nr == index) {
+      return true;
+    }
+  }
+
+  return false;
+}
+
 void BKE_mesh_material_index_clear(Mesh *me)
 {
   MPoly *mp;
index 1e03d986e3ecf9081dcf920522cbfd6d74f0a8b6..50f0b53c0375c87a7576d9c5368e4580f0539e39 100644 (file)
@@ -37,6 +37,7 @@ void OBJECT_OT_material_slot_select(struct wmOperatorType *ot);
 void OBJECT_OT_material_slot_deselect(struct wmOperatorType *ot);
 void OBJECT_OT_material_slot_copy(struct wmOperatorType *ot);
 void OBJECT_OT_material_slot_move(struct wmOperatorType *ot);
+void OBJECT_OT_material_slot_remove_unused(struct wmOperatorType *ot);
 
 void MATERIAL_OT_new(struct wmOperatorType *ot);
 void TEXTURE_OT_new(struct wmOperatorType *ot);
index b77f5c2bbad26c88b3c8bfd41c60092e868e9bf0..8156f48343f383a9080c77e462be7567bc57709d 100644 (file)
@@ -42,6 +42,7 @@ void ED_operatortypes_render(void)
   WM_operatortype_append(OBJECT_OT_material_slot_deselect);
   WM_operatortype_append(OBJECT_OT_material_slot_copy);
   WM_operatortype_append(OBJECT_OT_material_slot_move);
+  WM_operatortype_append(OBJECT_OT_material_slot_remove_unused);
 
   WM_operatortype_append(MATERIAL_OT_new);
   WM_operatortype_append(TEXTURE_OT_new);
index 5c305f45fb23db3571147c049bd682ae82e5b768..36531b2806c013f0a6c769549178b20562512ac6 100644 (file)
@@ -549,6 +549,73 @@ void OBJECT_OT_material_slot_move(wmOperatorType *ot)
                "Direction to move the active material towards");
 }
 
+static int material_slot_remove_unused_exec(bContext *C, wmOperator *op)
+{
+  Object *ob = CTX_data_active_object(C);
+
+  if (!ob) {
+    return OPERATOR_CANCELLED;
+  }
+
+  /* Removing material slots in edit mode screws things up, see bug #21822.*/
+  if (ob == CTX_data_edit_object(C)) {
+    BKE_report(op->reports, RPT_ERROR, "Unable to remove material slot in edit mode");
+    return OPERATOR_CANCELLED;
+  }
+
+  int actcol = ob->actcol;
+
+  int removed = 0;
+  for (int slot = 1; slot <= ob->totcol; slot++) {
+    while (slot <= ob->totcol && !BKE_object_material_slot_used(ob->data, slot)) {
+      ob->actcol = slot;
+      BKE_object_material_slot_remove(CTX_data_main(C), ob);
+
+      if (actcol >= slot) {
+        actcol--;
+      }
+
+      removed++;
+    }
+  }
+
+  ob->actcol = actcol;
+
+  if (!removed) {
+    return OPERATOR_CANCELLED;
+  }
+
+  BKE_reportf(op->reports, RPT_INFO, "Removed %d slots", removed);
+
+  if (ob->mode & OB_MODE_TEXTURE_PAINT) {
+    Scene *scene = CTX_data_scene(C);
+    BKE_paint_proj_mesh_data_check(scene, ob, NULL, NULL, NULL, NULL);
+    WM_event_add_notifier(C, NC_SCENE | ND_TOOLSETTINGS, NULL);
+  }
+
+  DEG_id_tag_update(&ob->id, ID_RECALC_GEOMETRY);
+  WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, ob);
+  WM_event_add_notifier(C, NC_OBJECT | ND_OB_SHADING, ob);
+  WM_event_add_notifier(C, NC_MATERIAL | ND_SHADING_PREVIEW, ob);
+
+  return OPERATOR_FINISHED;
+}
+
+void OBJECT_OT_material_slot_remove_unused(wmOperatorType *ot)
+{
+  /* identifiers */
+  ot->name = "Remove Unused Slots";
+  ot->idname = "OBJECT_OT_material_slot_remove_unused";
+  ot->description = "Remove unused material slots";
+
+  /* api callbacks */
+  ot->exec = material_slot_remove_unused_exec;
+  ot->poll = ED_operator_object_active_editable;
+
+  /* flags */
+  ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
 /********************** new material operator *********************/
 
 static int new_material_exec(bContext *C, wmOperator *UNUSED(op))