Another method
- Gets all the face indices for each material in slots
How to get indices of the faces to which a material has been applied, using Python?
If not assigned a slot, or slot is empty assign to material "None". Make sure there is no material named "None" in file
If more than one slot has same material it merges them
Will end up with a dictionary of materials by name to corresponding list of face indices, and those assigned to None.
Note if a material index is found on a face that is outside the range of slots it is also assigned to "None".
Make sure the slots list is the same length.
Reassign slots and faces materials to dictionary values.
Test script. As pointed out by @Ratt always test on a copy, or don't save until happy with result. Simplifies material slot list of context object to those used, and an empty slot for outliers.
from collections import defaultdict
import bpy
context = bpy.context
ob = context.object
me = ob.data
slots = ob.material_slots
def getmname(idx):
if idx < len(slots):
name = slots[idx].name
name = name if name else "None"
else:
name = "None"
return name
matdic = defaultdict(list)
for f in me.polygons:
matdic[getmname(f.material_index)].append(f.index)
while len(slots) > len(matdic):
ob.active_material_index = 1
bpy.ops.object.material_slot_remove()
while len(slots) < len(matdic):
bpy.ops.object.material_slot_add()
for i, (name, faces) in enumerate(matdic.items()):
slots[i].material = bpy.data.materials.get(name)
for f in faces:
me.polygons[f].material_index = i