I'm trying to build a simple bake tool that will allow me to bake a set of objects. Everything works if I EXEC_DEFAULT
the bake operator, but this doesn't give me a progress bar and completely freezes the UI.
Switching to INVOKE_DEFAULT
causes a problem with async- the loop executes but only the first bake operation is performed.
The relevant code is here:
class BakeryOperation(bpy.types.Operator):
bl_idname = "bakery.bake"
bl_label = "Bake textures"
def invoke(self, context, event):
scn = context.scene
materials = set()
for o in scn.bake_objects:
materials = materials.union(set(map(lambda x: (o.name, x.name), bpy.data.objects[o.name].data.materials)))
for (name, material) in materials:
bpy.data.materials[material].use_nodes = True
node_tree = bpy.data.materials[material].node_tree
found = False
img_name = name+"_bake"
for n in [n for n in node_tree.nodes if n.type == 'TEX_IMAGE']:
found = False
if n.image != None and n.image.name == img_name:
n.select = True
node_tree.nodes.active = n
found = True
break
if not found:
node = node_tree.nodes.new("ShaderNodeTexImage")
node.select = True
node_tree.nodes.active = node
if img_name in bpy.data.images:
node.image = bpy.data.images[img_name]
else:
node.image = bpy.data.images.new(name + "_bake", 512, 512)
img = bpy.data.images[img_name]
bpy.ops.object.select_all("EXEC_DEFAULT", action="DESELECT")
bpy.ops.object.select_pattern(pattern = name)
bpy.context.scene.objects.active = bpy.data.objects[name]
bpy.ops.uv.lightmap_pack("EXEC_SCREEN")
print("Baking " + name)
bpy.ops.object.bake("INVOKE_DEFAULT") # or EXEC_DEFAULT
return {'FINISHED'}
I've seen posts about using macros to order sequences of operators, but it doesn't seem possible to configure specific settings (in this case, which object to bake). This is my first foray into Blender scripting, so I'm surely missing something.
How would I achieve this, and are there any best practices I should be be aware of?
UPDATE
After a bit of playing around, I've solved this particular case. The solution is as follows:
class BakeryOperation(bpy.types.Operator):
bl_idname = "bakery.bake"
bl_label = "Bake Textures"
def invoke(self, context, event):
scn = context.scene
materials = set()
bake_pass = scn.bake_passes[scn.bake_passes_index]
scn.cycles.samples = bake_pass.samples
for o in scn.bake_objects:
materials = materials.union(set(map(lambda x: (o.name, x.name), bpy.data.objects[o.name].data.materials)))
bpy.ops.object.select_all("EXEC_SCREEN", action="DESELECT")
for (name, material) in materials:
bpy.data.materials[material].use_nodes = True
node_tree = bpy.data.materials[material].node_tree
found = False
img_name = name+"_bake"
for n in [n for n in node_tree.nodes if n.type == 'TEX_IMAGE']:
found = False
if n.image != None and n.image.name == img_name:
n.select = True
node_tree.nodes.active = n
found = True
break
if not found:
node = node_tree.nodes.new("ShaderNodeTexImage")
node.select = True
node_tree.nodes.active = node
if img_name in bpy.data.images:
node.image = bpy.data.images[img_name]
else:
node.image = bpy.data.images.new(name + "_bake", 512, 512)
img = bpy.data.images[img_name]
bpy.ops.uv.lightmap_pack("EXEC_SCREEN", PREF_CONTEXT="ALL_FACES")
bpy.ops.object.select_pattern("EXEC_SCREEN", pattern = name)
bpy.ops.object.bake("INVOKE_SCREEN")
return {'FINISHED'}
Simply selecting all objects and then invoking the bake operation fixes the problem.