SVG Import - Remove Redundant Materials?

I was hoping someone could help with a solution that I imagine a Python script would handle well. I’m very novice with Python, but have some familiarity through very basic scripts I’ve used in previous projects.

I’m working on a project that incorporates a ton of SVG assets from Illustrator. The fact that the color data is preserved upon import is part of the appeal, however the struggle I’m having is outlined below:

  • Import SVG. Looks good, colors are applied.
  • Convert to mesh, clean up mesh.
  • PROBLEM: Let’s say I import an SVG with only 3 colors throughout. Blender applies each set of connected vertices with an independent SVGMat.###. So, if i have the same color blue on 50 separate pieces of the mesh, Blender creates 50 different material slots though they all share the same color.

I’d like to be able to condense these material slots to a single material for each color, and ensure the material is applied to the other separate objects whose independent SVGMaterial shares the same color. Therefore, if I have an asset imported that has only 3 colors in its palette, there would only be three materials applied.

I’ve got about 100 assets I need to do this for, so I though scripting would be the most efficient approach. My material lists are getting out of hand :spin:

Much appreciated!

You have written an svg importer or are you using the one shipped with blender?

Everything I’ve brought in used the built in Blender SVG importer.

here’s a quick script, see if it helps: you need to run it after importing the svg, before converting to meshes and merging…
edit: just added a couple of lines to get better naming


import bpy
colores = {}
objects = bpy.context.selectable_objects
curves = [o for o in objects if o.type == 'CURVE']

# compare rgb values to get unique colors
for c in curves:
    rgb = c.material_slots[0].material.diffuse_color
    rgb.freeze()
    if rgb in colores:
        mat = bpy.data.materials[colores[rgb]]
        c.material_slots[0].material = mat
    else:
        colores[rgb] = c.material_slots[0].material.name

# try to clean up names a bit
for mat in bpy.data.materials:
    if mat.users and mat.name.startswith('SVGMat'):
        mat.name = 'COLOR.000'

Liero thank you! Boss mode. Any chance this can be modified for existing assets I’ve already converted to meshes and merged?

EDIT: Did get this running on Meshes by changing

“curves = [o for o in objects if o.type == ‘CURVE’]”

To

“curves = [o for o in objects if o.type == ‘MESH’]”

However, the comparison you setup doesn’t do much to reduce the number of mats. Is it possible to compare the colors in all the material_diffuse slots (SVG importer in trunk will apply the vector color there) and reduce based on some parameter, e.g. “if the color is within x percentage of the diffuse_color RGB value, selecting one and apply to both”?


The attached image above shows a single merged mesh and it’s material slots. As you can see there are really only about 5-6 color values there, but are all applied individually.

Thanks in advance, either way this is sitting in my scripting toolkit for future vector workflows.

Are the colors always grayscale?

Unfortunately not, but in this scenario they happened to be :confused:

so here’s a different script you can try and modify, was a bit tricky so it may fail for you
edit: added some rounding to rgb values in the naming function to get a color ‘tolerance’


## clean up slots and materials
import bpy
colores = {}
objects = [o for o in bpy.context.selected_objects if o.type=='MESH' and o.data.materials]

def nameFromRGB(c):
    n = 'R{0:.3f}'.format(round(c.r,3))+'_G{0:.3f}'.format(round(c.g,3))+'_B{0:.3f}'.format(round(c.b,3))
    return n

for obj in objects:
    # compare rgb values
    for slot in obj.material_slots:
        rgb = nameFromRGB(slot.material.diffuse_color)
        if rgb in colores: slot.material = bpy.data.materials[colores[rgb]]
        else: colores[rgb] = slot.name
    # get the material for each polygon
    names = [obj.material_slots[p.material_index].name for p in obj.data.polygons]
    # clear materials
    obj.data.materials.clear()
    # unique materials list
    setnames = list(set(names))
    for n in setnames:
        mat = bpy.data.materials[n]
        obj.data.materials.append(mat)
    # assign slot index to polygons
    for i, p in enumerate(obj.data.polygons):
        p.material_index = setnames.index(names[i])

You are a godsend. Wow, thank you so much. Workflow just got a million times faster! I will share the final product on the forums when it’s completed, much appreciated Liero.

ok glad it helps, maybe a better place to fix this would be the svg importer itself