Flexbile Python use in Blender

This post is about using Python in Blender. It’s not a tutorial. It’s more of an inspiration to learn and experiment with Python.

Imagine you work on a piece. The first stage is very experimental. You look for what works and what doesn’t. At some point the piece and the process take shape. Some things become repetitive. It’s very useful to notice those patterns early and optimize them.

Lets say I’m working in ZBrush and I want to render in Blender. The geometry moves between those two pretty often. Exporting and importing takes quite a few clicks. All that clicking adds up and nibbles at my, that day’s, energy.

Here’s where a little bit of Python can help.

I want to quickly reimport meshes from ZBrush. I don’t want to manually reapply materials and I don’t want to manually delete previous versions. I don’t want to create a full plugin or a system for doing that. I want to focus on modelling. Python is there to help me but it isn’t the goal itself. I want something I can use very quickly. No fancy stuff. Simplicity and control.

If you’re a software person, you might start thinking:

If I could generalize this a bit, I could build a reusable system!

Stop! We’re here to make art! Ignore side quests!

Here you can see the process I need:

This has been achieved with a simple Python script:

# Put this script in Blender file structure, lib/michal folder. Remember to create
# __init__.py file in that folder.
#
# To reload this script in Blender session:
#
# from importlib import reload
# reload(michal.utils)
#

import bpy
from pathlib import Path

# Path to the project root directory.
PATH_ROOT = Path(r"C:\Users\mc\Desktop\devil_lady\parts")

# Describe the export/import mesh files.
RING = {"file": PATH_ROOT / Path("ring.obj"),
        "mat": "ring",
        "object_name": "ring"}
HEAD = {"file": PATH_ROOT / Path("head.obj"),
        "mat": "head",
        "object_name": "head"}
HAIR_MAIN = {"file": PATH_ROOT / Path("hair_main.obj"),
             "mat": "hair",
             "object_name": "hair_main"}
BODY = {"file": PATH_ROOT / Path("body.obj"),
        "mat": "body",
        "object_name": "body"}
MOUTH_CHAIN = {"file": PATH_ROOT / Path("mouth_chain.obj"),
               "mat": "gold",
               "object_name": "mouth_chain"}


def material_exists(mat: str):
  if mat in bpy.data.materials.keys():
    return bpy.data.materials[mat]
  else:
    return None

def delete_by_name(name: str):
  # TODO(michalc): restore original selection?
  bpy.ops.object.select_all(action='DESELECT')
  try:
    bpy.data.objects[name].select_set(True)
    bpy.ops.object.delete()
  except KeyError:
    print(f"Failed to delete {name}. Not present.")

def import_obj(obj_info):
  delete_by_name(obj_info["object_name"])
  status = bpy.ops.import_scene.obj(filepath=str(obj_info["file"]))
  obj_object = bpy.context.selected_objects[0]
  obj_object.name = obj_info["object_name"]
  # TODO(michalc): rename the obj_object.data ... mesh object inside.
  mat_for_object = material_exists(obj_info["mat"])
  print(f"Imported {obj_object.name}, matching material: {mat_for_object}")
  print(obj_object.data)
  print(bpy.context.view_layer.objects.active)

  if mat_for_object:
    obj_object.data.materials[0] = mat_for_object

What’s so special about it? Nothing. Absolutely nothing. That’s the point - simple script with paths hardcoded. No thinking, brain empty.

I can now re-import the body mesh by running:

michal.utils.import_obj(michal.utils.BODY)

I can quickly add more objects by just adding constants in the script file. You might think that this is the opposite of flexible. Yes, the script is pretty rigid. YOU should be flexible about using Python.

This script is part of the project. When I’m done I’ll archive it with the project. It’s not a system or a plugin. It’s some simple functionality that helps me solve specific issues in the workflow.

It does require some Python knowledge. You can’t be working on you piece and suddenly start learning Python. I mean, you can, but that’s a massive side quest? You turned your PC to sculpt and now you’re suddenly learning to code? I’m lucky enough because I work with Python. That allows me to write scripts like that quickly.

In spite of that I’d like to encourage you to learn scripting languages used by your 3D tools of choice.

Yes, I know about GoB/GoZ. It’s a very limited plugin that doesn’t give a lot of control. It was always underwhelming.