Eliminate duplicate materials .001 .002 .003 after Append

(lobo_nz) #1

Hi Im looking for a script to eliminate multiple materials, when I Append models which use identical materials I end up with lots of duplicates, like glass, glass.001 glass.002, glass.003. … Then I have to “Databrose” which dosnt have the nice new material previews.
I really dont want to write my own as Im lazy and busy although I will probably end up doing so.
Im pretty sure there isnt any way to “use existing” or “replace” materials when importing but please let me know if there is as this would help also.

Any help appreciated.

(greg57) #2

I have the same problem when importing a wrl generated by Catia. Anything possible about this?

(Michael W) #3

Here’s a script I wrote for 2.49 that will “ease” this…

(It could be modified to automatically pattern match based on name, but I personally think that could be dangerous so made it a “wizard”


run the script… (I think I put it in the “objects” category rather than the materials one… but you could always run it form the text editor…

it’ll pop up a list of materials for you to choose a material to replace, then offer a similar popup for what to replace it with…

It will then find and replace all references to the first material with the second…

If you save the scene and reload the “replaced” material will be gone…

Here’s a link

inside the script is a function:

mw_replaceMat(inMat,outMat, allScenes =True,sel =False)

You could import my script, build a list of materials with “auto names” and then call that function to automate it more… but the trick is when you get close to the 21 character limit and thenames have been truncated…

(mandoragon) #4

How many materials have you got that are 21 characters long? I guess it could be an option to delete any of this length, 21 char (abcdefghijklmnopqrst) This is an extreme conditional but I cant really think of any other way of doing it…

(Michael W) #5

I’ve done some video game work where the naming conventions imposed by the client meant that 21 characters for a material name was woefully inadequate… I ended up having to use all sorts of hacks and workarounds in blender and maya to get stuff named right once transferred to Maya…

Which is why the “wizard” approach works for me… it only takes a few minutes for even very complex scenes… but truncated names aside:

a “slightly” dangerous way to do it (could potentially damage any material name with a “.” if what precedes the dot is the same name as another material in the scene.

importing my script and then doing something like the following should do what you want:

import bpy
import substitutemat
from substitutemat import*

mats = bpy.data.materials
for m in mats:
    #find the name without any auto numbered stuff:
    nm = m.name.split('.')[0]

    #check to see if the portion before the "." matches an existing material
    if nm in mats:
        mw_replaceMat(m.name, nm)

any material names that got truncated and didn’t match would have to be cleaned by just running the first script wizard I posted… which is safer I guess, and only necesary with long material names anyway!

(Michael W) #6

Oops, of course I’m checking a list of material objects against a string! that’s never match…

import bpy 
import substitutemat 
from substitutemat import* 
mats = bpy.data.materials 
#build a list of material names in the scene: 

matnames = [] 
for m in mats: 
for m in mats: 
    #find the name without any auto numbered stuff: 
    nm = m.name.split('.')[0] 
    #check to see if the portion before the "." matches an existing material 
    if nm in matnames: 
         mw_replaceMat(m.name, nm)

This is about the fourth edit of this!

(mandoragon) #7

do you think we could ask the devs to increase the character limit in the next svn release? any idea why 21?

(Michael W) #8

I asked Letterip ages ago… he said he’d pass it on…

this stuff, whilst probably easy for someone to do, really takes someone to “need” it themselves to do the code… I’d patch it myself if I could code!!!

(mandoragon) #9

your scripting so you can code :s

I was just assuming that the number 21 was just a variable set somewhere…

(Looch) #10

Any similar script to this one still in existence?

(liero) #11

here’s a very basic snippet that worked for me, at least for simple scenes

## 'desduplicar' materiales

import bpy
mats = bpy.data.materials

for obj in bpy.data.objects:
    for slt in obj.material_slots:
        part = slt.name.rpartition('.')
        if part[2].isnumeric() and part[0] in mats:
            slt.material = mats.get(part[0])

(RickyBlender) #12

don’t see it as an import script

it only checks for same name with number at the end

do you want to convert it as an import script too ?

but if there are like 1000 mat this might get very sluggish !

happy bl

(Lsscpp) #13

would this work also for nodegroups, changing the appropriate bpy.data.stuff?

(liero) #14

to deduplicate nodegroups in materials it could be something like this:

## deduplicate nodegroups in materials from name

import bpy
n_gs = bpy.data.node_groups

for mat in bpy.data.materials:
    for nt in mat.node_tree.nodes:
        if nt.type == 'GROUP':
            part = nt.node_tree.name.rpartition('.')
            if part[2].isnumeric() and part[0] in n_gs:
                nt.node_tree = n_gs.get(part[0])

but you need to be careful as it will go through all materials
there’s also the new id_remap thing meant for this situations I believe

(Paljas) #15

Like a charm, thank you! In addition, I used to replace upper/lowercase duplicates:

for obj in bpy.data.objects:    for slt in obj.material_slots:
        if slt.name != slt.name.lower() and slt.name.lower() in mats:
            slt.material = mats.get(slt.name.lower())