0
$\begingroup$

I have a bunch of Blender files with similar content. The files share a lot of common objects with identical names. I would like to propagate materials between the files. I plan to achieve this with a script that maintains a database of materials and associates them with objects based on their names.

All the materials will probably be simple - just diffuse and specular components. Some of them will perhaps also have a texture. However, it seems that rna2xml does not work on such a simple material:

for o in context.scene.objects:
    for m in o.material_slots:
        rna2xml(root_node="Material", root_rna=m.material)

Here's the output:

<Material>
  <texture_paint_images>
  </texture_paint_images>
  <texture_paint_slots>
  </texture_paint_slots>
  <texture_slots>
  </texture_slots>
</Material>

Looks a bit too empty (no diffuse, specular components).

Moreover, it seems that Material does not have _ _dict__ attribute nor an _asdict() method, and _ _slots__ appears to be empty. Well, theoretically could look through the attribute list provided by dir(), but I'm probably approaching this issue from the wrong side already.

Edit: For completeness, I also verified that pickle produces identical output for materials with different diffuse colors.

Except for hard-coding material parameters (which should be simple in my case - because I just need simple materials anyway), are there any ideas or suggestions?

I'm using Blender 2.76 on Windows 7 x64.

$\endgroup$
5
  • 1
    $\begingroup$ Would it be simpler (if far less satisfying) to put all your materials into a dedicated blender file and link or append to them from there for your future projects? $\endgroup$
    – d8sconz
    Commented Nov 17, 2017 at 10:20
  • $\begingroup$ @user3825715: Interesting thought. Do you know how to link materials from a different blender file? $\endgroup$
    – Sussch
    Commented Nov 17, 2017 at 21:59
  • 1
    $\begingroup$ File-->Append(or Link)-->navigate to file, click on 'Material' (or any other element of the file), click on 'Append from Library' $\endgroup$
    – d8sconz
    Commented Nov 18, 2017 at 0:37
  • $\begingroup$ root_rna=m.material.node_tree $\endgroup$ Commented Sep 20, 2023 at 6:37
  • $\begingroup$ I can serialize and deserialize any Blender material configuration in JSON. It is possible for me to save my materials in a FreeCAD file and restore all these materials in Blender from this FreeCAD file. for more information see this thread on the FreeCAD forum $\endgroup$
    – psilocybe
    Commented Dec 10, 2023 at 9:38

1 Answer 1

0
$\begingroup$

Decided to try the dir() approach until someone proposes a better solution. Seems to be working for simple materials without textures, as well as some modifiers, etc. To me this looks pretty dangerous, tho.

In order to later update Blender objects based on the generated dict, I had to skip read-only properties.

def bl_to_dict(bl_object, ignore_getters=True):
    """Generic Blender object to dict converter."""
    skip_attributes = ['__doc__', '__module__', '__slots__']
    supported_basetypes = [
        "<class 'bool'>", 
        "<class 'int'>", 
        "<class 'float'>", 
        "<class 'str'>", 
        "<class 'list'>",
        "<class 'set'>"
        ]
    # TODO:: Add support for more types for which casting helps.
    supported_casts = {
        "<class 'Color'>": 'list'
    }
    # TODO:: Figure out how to support bpy_prop_array, bpy_prop_collection, etc.
    # TODO:: Perhaps recurse on members of more complex classes.
    d = {}

    attributes = [x for x in dir(bl_object) if x not in skip_attributes]
    for name in attributes:
        # Get attribute value.
        val = getattr(bl_object, name)
        # Ignore getter properties.
        if ignore_getters:
            try:
                setattr(bl_object, name, val)
            except AttributeError as err:
                continue
        # Check against supported types.
        type_str = str(type(val))
        if type_str in supported_basetypes:
            d[name] = val
        elif type_str in supported_casts:
            d[name] = eval(supported_casts[type_str])(val)
        #else:
        #    print("Type {} of attribute {} not supported yet.".format(type(val), name))
    return d

def dict_to_bl(bl_obj, bl_dict):
    """Update a Blender object from a dict."""
    for key, val in bl_dict.iteritems():
        setattr(bl_obj, key, val)
    return bl_obj

# From here on it's easy to either convert the dict 
# to a string, to pickle, json, p-code or whatever it.

Disclaimer: The code is provided as-is. It's not guaranteed to work or anything. Getting and resetting random properties (basically what this code does) might seriously mess up stuff in Blender.

$\endgroup$

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .