17 Posts
Hi, I noticed that if I import multiple zmbx files to the same Blender file, I often end up with multiple copies of the materials (e.g. mb:solid.21, mb:solid.21.001, mb:solid.21.002) and the meshes ("20482.001", "20482.003"). So I have hacked together a little python script that will remap the meshes and materials, leaving the duplicate materials as orphan data that will auto-delete when the Blender file is closed.

To use this, just copy the script at the end of this post into the "Scripting" tab of Blender. Make sure you keep the correct line indentation! Then click "Run Script".

The script carries a few dangers:
If you make local modifications to the Mecabricks materials for one (but not all) of the imported zmbx files, then these changes could be lost in the material reshuffle
If you create custom meshes or materials with names that are similar to the names of Mecabricks meshes or materials., these could get replaced with Mecabricks objects.
*** Always backup your blender file before you run this script!!! ***
*** You use this script entirely at your own risk ***

(P.S. This is my first python script in several years, and my first ever attempt at a script for Blender. Any suggestions for improving it would be appreciated!)

Python script:
#Mecabricks multi-import cleanup
#by NathanR2015

#If multiple Mecabricks zmbx files are imported into a single scene, there may be 
#several duplicated materials and meshes. This script cleans up the blender file
#by remapping the materials and meshes. 
#For example: 
#  Materials mb:solid.21.001, mb:solid.21.002, all become mb:solid.21.
#  Meshes "20482.001", "20482.003" both get changed to "20482.001" if they share the 
#  same material (the mesh+material combination must be unique)
#WARNING! If you make local modifications to Mecabricks materials or meshes, then 
#run this script, your changes may be lost.  Also, materials or meshes with names
#matching the Mecabricks naming scheme may be misidentified and remapped.

# Loop over every object in the blender file, and remove any duplicated materials.
# e.g. mb:solid.21.001, mb:solid.21.002, all get replaced with mb:solid.21.
# The remaining materials are now unused and will auto-delete when the blender file is closed/reopend.
# Search for duplicate meshes, if one is found (same mesh, same material, reassign it

from collections import defaultdict
import bpy

# NOTE: This will only check mb materials
# WARNING! If you have modified a mecabricks material, the changes will be lost!
for obj in bpy.data.objects:
    for slot in obj.material_slots:

        #Ignore mb decorations, they need to be unique due to possible custom textures
        if slot.name == "mb:decoration": 
        # Ignore anything that is not a mecabricks material
        if not (slot.name.startswith(("mb:chrome", "mb:glitter", "mb:metal", "mb:milky", "mb:pearlescent", "mb:rubber", "mb:solid", "mb:speckle", "mb:transparent"))):
        #Split name by the "." to get base name and number of duplicate
        part = slot.name.rpartition('.')
        if not (part[2].isnumeric()):
        #Try to remap numbered material to a non-numbered material
        if part[0] in bpy.data.materials:
            slot.material = bpy.data.materials.get(part[0])
            #Problem - the non-numbered material doesn't exist, try to find a numbered material instead
            for num in range(1, int(part[2])):
                name = part[0] + "." + str(num).rjust(3, '0')
                if name in bpy.data.materials:
                    slot.material = bpy.data.materials.get(name)

#Construct a dictionary of mesh names to make script faster
#Key = base name, List = All numbered names, e.g '20482': ['20482.001', '20482.003']
brickIDs = defaultdict(list)
for mesh in bpy.data.meshes:
    #Ignore orphan meshes
    if mesh.users == 0:  

    #Ignore meshes with "uv" in name (decorated elements)
    part = mesh.name.rpartition('.')
    if not part[0].isnumeric(): 


#Loop over each mesh, see if there is a lower number that it can be assigned to.
#Brick ID and assigned material must match.
#Note, this assumes that there is no mesh name without a number.
#This should always be the case as mesh "20482" is orphaned data.
for brickID in brickIDs:
    for mesh in brickIDs[brickID]:
        matname = bpy.data.meshes[mesh].materials.values()
        part = mesh.rpartition('.')
        #Loop over every mesh number up this point, if there is a match then reassign mesh
        for num in range(1, int(part[2])):
            testname = part[0] + "." + str(num).rjust(3, '0')
            if testname in bpy.data.meshes:
                if matname == bpy.data.meshes[testname].materials.values():

31 Aug 2019, 21:51
Edited 31 Aug 2019, 21:53
233 Posts
Thanks for this! Definitely keeping it in mind shall I ever want to import several MB files after another.
2 Sep 2019, 22:17
Page 1 of 1