3
$\begingroup$

Problem

I'm writing an addon that needs to:

  1. Do some pre-baking setup.

  2. Bake multiple objects' lighting to the same image texture (already UV mapped to not overlap).

    (Note: this step should not block the UI. I feel like this should be obvious, but freezing the UI for long bakes with no way to cancel is not acceptable UX.)

  3. Revert/clean up the setup from step 1 only after baking is complete, because the setup is necessary for the bake results we need, but is not something the user should be left with.

Step 1 and step 2 are pretty straightforward to accomplish by just running the setup code and then calling bpy.ops.object.bake() with INVOKE_DEFAULT. But so far step 3 seems impossible, because there doesn't seem to be a way to determine when baking is finished when run in a non-blocking way.

Things I've tried

Attempt 1

My first thought was that I could accomplish this the same way I have for rendering in the past, via the bpy.app.handlers.render_post and bpy.app.handlers.render_cancel hooks as in this answer: https://blender.stackexchange.com/a/71830/146003

Unfortunately, similar hooks do not exist for baking, and the hooks for rendering do not work for baking.

Attempt 2

The second thing I tried was to simply do the clean up immediately after starting the bake. When baking a single object this works fine, because the bake works from the state of the scene when it was called, and ignores subsequent concurrent changes.

Unfortunately, this doesn't work when baking multiple objects because the baking operator updates its version of the scene before baking each object. So the first object bakes correctly, but the rest of the objects use the version of the scene with the setup already torn down (and as mentioned earlier, the setup is necessary for the bake results we need).

Attempt 3

The third thing I tried is from this answer: https://blender.stackexchange.com/a/253550/146003

This solution uses the dirty state of the image to which we're baking to determine when the bake has finished. Unfortunately, this solution has two problems:

  1. Unlike in that answer, my addon works from an already existing image. And if the image is already dirty when the bake starts (which is common), this obviously won't work.
  2. Even if the image starts non-dirty, we still run into a similar problem as attempt #2: after the first object finishes baking the image is immediately marked dirty and the setup is torn down, and subsequent objects will therefore bake with the setup already torn down (and again, the setup is necessary for the bake results we need).

Attempt 4

As suggested by @XY below, I tried checking if img.bindcode changes when baking is finished. Unfortunately, it doesn't change at all, so that won't work.

@XY also suggested checking if the actual pixel data changed (e.g. img.pixels[0]), but that won't work because there is no guarantee that any particular pixel--or indeed any pixel--actually changes. The user may, for example, re-bake "just to be sure", resulting in a bake with exactly the same pixels.

Ideal solution

An ideal solution would present a general way to wait for any modal operator to finish/cancel, not just the baking operator. Because I suspect this is an issue that I (and others) will run into with other operators as well.

Short of that, a baking-operator-specific solution would work for my specific situation.

$\endgroup$
3
  • $\begingroup$ Is it work if check the img.bindcode or img.pixels[0] are changed ? $\endgroup$
    – X Y
    Commented Apr 5, 2022 at 23:56
  • $\begingroup$ @XY: if img.bindcode is guaranteed to change whenever a bake finishes, then it would basically be like attempt #3, just without the first problem. In which case I could probably make it work by manually calling bpy.ops.object.bake() for each object, rather than baking them all with a single call. So it might work! I'll give it a try. (Having said that, it seems hacky and brittle, much like attempt #3 itself.) $\endgroup$
    – Cessen
    Commented Apr 6, 2022 at 1:45
  • $\begingroup$ @XY: I just tested, and unfortunately img.bindcode does not change at all with baking. Also, just to clarify: checking img.pixels[0] definitely wouldn't work, because there is no guarantee that any of the pixels change. Imagine, for example, that the user re-bakes just to be sure, but the resulting baked image is exactly the same. Anyway, I'll add this as "Attempt 4" in the question. $\endgroup$
    – Cessen
    Commented Apr 6, 2022 at 17:17

2 Answers 2

0
$\begingroup$

Try this

If this is not work, you have to consider modifying the source code.

import bpy

def run_every_01_sec():
    if RETURN in ({'CANCELLED'}, {'FINISHED'}):
        print("op end")
        bpy.app.timers.unregister(run_every_01_sec)
    else:
        print("op running")
    return 0.1

RETURN = bpy.ops.object.bake_image()

bpy.app.timers.register(run_every_01_sec)
$\endgroup$
1
  • $\begingroup$ This doesn't work. RETURN only gets set once to {'RUNNING_MODAL'} when you first call bpy.ops.object.bake_image(). $\endgroup$
    – Cessen
    Commented Apr 7, 2022 at 23:48
0
$\begingroup$

To answer my own question: this is not currently possible in Blender's Python API.

But it looks like there is interest in addressing this, and making it possible. For more info, see https://developer.blender.org/D14587#391718

I will update this question and answer once it's been addressed in a stable Blender release.

$\endgroup$

You must log in to answer this question.

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