7
$\begingroup$

I just made a scene with a city made of mostly identical skyscrapers. I did that by copying and pasting. Then I joined all of them with Ctrl+J, but now I can't undo the join and I have got around 300+ glass and wall materials in one obj. My question is how do I merge them into 1 glass and 1 wall material so I can change all windows and all walls at once? Selecting them all is not an option. I have 1000+ windows in my scene and now in one obj.

Short version: I mucked up and now I need to clean up 300+ materials. How can I do that? Maybe an add-on?

edit: I tried deleting all but the first glass and wall. i had one skyscraper left that had colors.

edit2: my materials are in alternating order. like glass, wall, glass, wall, glass, wall.... i think you get the idea.

$\endgroup$
6
  • $\begingroup$ Can you post the blend file (or just a small sample of it with the problem)? I'm not sure what your material slots look like and what order they are in. $\endgroup$
    – David
    Mar 16 '17 at 14:22
  • 2
    $\begingroup$ Script is needed to do this. It has to know some pattern to know how to merge. Do the materials have a naming convention? Let's say all glass materials have "glass" in them? Or are they in some specific order? Or do all glass materials have the same setting/nodes? $\endgroup$ Mar 16 '17 at 14:27
  • $\begingroup$ Close to your need, but not sure it can do it all (untested) wiki.blender.org/index.php/Extensions:2.6/Py/Scripts/… $\endgroup$
    – lemon
    Mar 16 '17 at 18:22
  • 3
    $\begingroup$ Possible duplicate of How to append many objects without appending materials $\endgroup$ Mar 17 '17 at 0:10
  • $\begingroup$ This is not as straightforward as it seems. When you copy - paste objects in Blender, unfortunately not only the object is duplicated, but also the materials on the object. So that's why you get materials called Glass.001, Glass.002, Glass.003 and so on. For Blender, those are now entirely different materials, even though they look identical. If you had used Shift D to duplicate the objects, the materials would have remained singular, even after joining the meshes. The only solution here is a brute-force addon which changes material assignments by name. It's possible but not safe $\endgroup$
    – aliasguru
    Mar 19 '17 at 14:42
4
$\begingroup$

The following script extends from the already linked answer in that it removes any double-materials and assigns the 'correct' materials to the faces respectively. However, you'll need to understand that a few assumptions are made here:

  • your original material is named something
  • the duplicates are named something.045, something.046 and so on
  • they are REALLY duplicates
  • you've saved your scene before you run this :)
  • the active object in your scene is the mesh object you want to clean

Copy the following Python code to a text editor within your file and run it:

import bpy

# only search on own object materials
mat_list = [x.material.name for x in bpy.context.object.material_slots]
remove_slots = []


# the following only works in object mode
bpy.ops.object.mode_set(mode='OBJECT')

for s in bpy.context.object.material_slots:
    if s.material.name[-3:].isnumeric():
        # the last 3 characters are numbers
        # that indicates it might be a duplicate of another material
        # but this is pure guesswork, so expect errors to happen!
        if s.material.name[:-4] in mat_list:

            # there is a material without the numeric extension so use it
            # this again is just guessing that we're having identical node trees here

            # get the material index of the 'clean' material
            index_clean = mat_list.index(s.material.name[:-4])
            index_wrong = mat_list.index(s.material.name)
            print(index_wrong, index_clean)

            # get the faces which are assigned to the 'wrong' material
            faces = [x for x in bpy.context.object.data.polygons if x.material_index == index_wrong]

            for f in faces:
                f.material_index = index_clean

            remove_slots.append(s.name)

# now remove all empty material slots:
for s in remove_slots:

    if s in [x.name for x in bpy.context.object.material_slots]:
        print('removing slot %s' % s)
        bpy.context.object.active_material_index = [x.material.name for x in bpy.context.object.material_slots].index(s)
        bpy.ops.object.material_slot_remove()

It should turn this:

start

into this:

finish

$\endgroup$
4
  • 1
    $\begingroup$ replying to a very old question, but its nice to be able to mark it solved $\endgroup$
    – laundmo
    Jun 19 '19 at 22:55
  • $\begingroup$ I'm using Blender 2.83 and this isn't working for me. I have many duplicate materials across different objects. $\endgroup$ Jun 9 '20 at 18:47
  • $\begingroup$ @AaronFranke I've just tested the script and confirmed it still works, even in 2.90 alpha. Can you elaborate on what you struggle with? Keep in mind that this script is designed to remove duplicate materials on one single object, not across many objects $\endgroup$
    – aliasguru
    Jun 9 '20 at 19:38
  • $\begingroup$ Yes, that was the problem, I had many objects. It's a shame the script can't work for multiple objects, but I ended up just fixing it manually, so my situation is resolved. $\endgroup$ Jun 9 '20 at 20:01
6
$\begingroup$

I have only tested this on Blender 2.82

This solution only works if you are trying to get rid of duplicates like mat.001, mat.002 … (etc.)

You should save your work before running the script in case it doesn't produce the results you want

Copy/Paste the code into the Text Editor and run the script

Once it has finished running, all of the duplicate materials (mat.001, mat.002 …) will be replaced with a single material which will be named mat.

import bpy

def replace_material(bad_mat, good_mat):
    bad_mat.user_remap(good_mat)
    bpy.data.materials.remove(bad_mat)
    
    
def get_duplicate_materials(og_material):
    
    common_name = og_material.name
    
    if common_name[-3:].isnumeric():
        common_name = common_name[:-4]
    
    duplicate_materials = []
    
    for material in bpy.data.materials:
        if material is not og_material:
            name = material.name
            if name[-3:].isnumeric() and name[-4] == ".":
                name = name[:-4]
            
            if name == common_name:
                duplicate_materials.append(material)
    
    text = "{} duplicate materials found"
    print(text.format(len(duplicate_materials)))
    
    return duplicate_materials


def remove_all_duplicate_materials():
    i = 0
    while i < len(bpy.data.materials):
        
        og_material = bpy.data.materials[i]
        
        print("og material: " + og_material.name)
        
        # get duplicate materials
        duplicate_materials = get_duplicate_materials(og_material)
        
        # replace all duplicates
        for duplicate_material in duplicate_materials:
            replace_material(duplicate_material, og_material)
        
        # adjust name to no trailing numbers
        if og_material.name[-3:].isnumeric() and og_material.name[-4] == ".":
            og_material.name = og_material.name[:-4]
            
        i = i+1
    

remove_all_duplicate_materials()

```
$\endgroup$
0
$\begingroup$

If you enter edit mode, you can select each glass material, and click select (Not sure if you can select all of them, and click select once or do them individually off the top of my head) then once everything that uses all of your glass materials is selected, delete all but one of them with the minus button near the top of the panel, then select that one material and click the "Assign" button underneath the panel. Rinse and repeat for the walls.

$\endgroup$
0
$\begingroup$

Short version (works with Blender 2.93.1):

import bpy

mats = bpy.data.materials

for mat in mats:
    (original, _, ext) = mat.name.rpartition(".")
    
    if ext.isnumeric() and mats.find(original) != -1:
        print("%s -> %s" %(mat.name, original))
        
        mat.user_remap(mats[original])
        mats.remove(mat)

Output example:

gold.003 -> gold
gold.004 -> gold
paper.001 -> paper
paper.005 -> paper
paper.006 -> paper
paper_white.001 -> paper_white
$\endgroup$
-1
$\begingroup$

You can try this add-on. It checks materials and removes duplications. https://b3d.interplanety.org/en/blender-add-on-m-cleaner/

The main difference of this add-on from the scripts above is that it can really check the materials and find the really equal material.

If you merge two materials only by name and prefix (.001, .002) they could differ by their nodes - so, the name shows that they are equal, but not, they aren't. The "M-Cleaner" add-on really checks materials node by node, compares them, and only if the materials are really equal - it merges them into one. If materials even have equal names but differ by nodes - they wouldn't be merged. This mode is non-destructive.

Also, it has destructive methods of work, like described in the scripts below. It can merge materials by names and only by nodes on the first level of materials (with no checking groups).

$\endgroup$
3
  • 1
    $\begingroup$ Hi! Please add more detail what the add-on actually does, otherwise this is a so called "low quality" post. At a first glance it does the same thing like both scripts listed here. Might be wrong, but I strongly suspect that it's just a copy of any script from this site to monetize python code. $\endgroup$
    – brockmann
    Mar 26 at 8:22
  • $\begingroup$ Thanks for the posts, but this is not a regular forum, according to site rules links to answers are not answers if the link goes missing your answer becomes an empty shell without content. Answers should be substantial and stand on their own without relying on external data. Either transcribe essential parts of the process here, or posts these in the comments section instead. $\endgroup$ Mar 26 at 10:47
  • $\begingroup$ I added some more info to my comment. $\endgroup$
    – Korchiy
    Mar 26 at 12:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.