0
$\begingroup$

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.

$\endgroup$
  • $\begingroup$ Hey, can you take a look at this $\endgroup$ – Sum-Al Jul 18 '16 at 10:24
  • 1
    $\begingroup$ Well this means you can't use the same material in multiple objects you are baking and thus you can't even bake multiple instances of the same object, correct? Each material has its img node set to that of the last object that uses it. Baaaaaaad. bake() should give a handle that we could use to progress with baking scripts but I've been unable to find any documentation or experimenting with this. Ideally Blender could sort their baking stuff out (assigning images to objects) so everybody and their cousins wouldn't have to make baking scripts in the first place. $\endgroup$ – kheetor Oct 10 '16 at 19:40
  • $\begingroup$ Yep. you are absolutely correct. When cycles baking was added to Blender, they improved the quality no end. Unfortunately the user interface (for artists and programmers) to both systems leaves a lot to be desired. You can work around these problems by creating proxy materials for each clone and swap them back at the end, but it's a complete pain. $\endgroup$ – mseddon Oct 11 '16 at 10:02
0
$\begingroup$

In this case, (doh), simply selecting the objects during the loop and then invoking the bake operation does what I need.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.