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😒olid.21, mb😒olid.21.001, mb😒olid.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!)
#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.
#ALWAYS BACKUP YOUR FILE BEFORE RUNNING THIS SCRIPT!!!
# 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":
continue
# 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"))):
continue
#Split name by the "." to get base name and number of duplicate
part = slot.name.rpartition('.')
if not (part[2].isnumeric()):
continue
#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])
else:
#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)
break
#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:
continue
#Ignore meshes with "uv" in name (decorated elements)
part = mesh.name.rpartition('.')
if not part[0].isnumeric():
continue
brickIDs[part[0]].append(mesh.name)
#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():
bpy.data.meshes[mesh].user_remap(bpy.data.meshes[testname].id_data)
break
========================================
Thanks for this! Definitely keeping it in mind shall I ever want to import several MB files after another. 👍
Sorry to bump such an old thread, but here is a revised version of the script that is designed to work with the current Mecabricks Blender addon, and it's updated naming scheme for meshes and materials.
Previous safety warnings still stand, this script could severely mess up your Blender file if you have made local modifications to mecabricks materials, or if you have custom materials that appear to be duplicates (e.g. if you had two different materials named sand.001 and sand.002, one of these will get erased)
Always make a backup before you run!
#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:o:64:191.001, mb:o:64:191.002, all become mb:o:64:191.
# Meshes "mb:o:1756:21.001", "mb:o:1756:21.001" both get changed to "mb:o:1756:21.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.
# ALWAYS BACKUP YOUR FILE BEFORE RUNNING THIS SCRIPT!!!
from collections import defaultdict
import bpy
# Loop over every object in the blender file, and remove any duplicated materials.
# The remaining materials are now unused and will auto-delete when the blender file is closed/reopend.
for obj in bpy.data.objects:
for slot in obj.material_slots:
#Not much safety, but should ensure that only mecabricks materials are remapped
if not (slot.name.startswith("mb:")):
continue
#Split name by the "." to get base name and number of duplicate
part = slot.name.rpartition('.')
if not (part[2].isnumeric()):
continue
#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])
else:
#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)
break
#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:
continue
#Ignore meshes with "uv" in name (decorated elements)
part = mesh.name.rpartition('.')
if not part[0].isnumeric():
continue
brickIDs[part[0]].append(mesh.name)
#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():
bpy.data.meshes[mesh].user_remap(bpy.data.meshes[testname].id_data)
break
LEGO, le logo LEGO, la minifigurine et les configurations des briques et tenons sont des marques déposées de LEGO Group of Companies. ©2024 The LEGO Group.
Mecabricks, le logo Mecabricks et tout le contenu non couvert par les droits d'auteur du groupe LEGO sont, sauf indication contraire, ©2011-2024 Mecabricks.