I have created a texture bake tool that adds an extra Texture node to all materials of a set of selected objects. In order to not block the Blender user interface while the bake tool is running i am using the baker as follows:
i also embedded the call to the object.bake inside a modal operator and i added a panel to the properties bar where i want to show the bake state for all affected objects/materials:
However the bake tool seems to run silently in the background without telling any information about its current progress. So my bake tool is completely blind and can not figure out when the object.bake is finished. and it also can not figure out which Object it currently processes. and finally it does not know when to finish the modal operation.
But the cleanup_after_bake() function is only called when i start a render with F12, so this does also not solve my issue.
I hope that i just do not know how to get the information. However in the case that object.bake operator does not send out progress information of any kind, then isn’t that actually a strongly needed feature after all?.
There is no way to get the granular progress as you suggest (without significant changes in the code base). There is already a progress bar when baking, and that should be reliable enough.
If you want control over the baking process I recommend baking one object at a time. This is what the code does internally anyways
It still would be very helpful if at least the end of bake is comunicated. It could tag the objects for example. But i think its even better if the baker sends an Event when it is done. Then we can capture this event and modal operators which call object.bake() now get informed when they can terminate cleanly.
I wrote this some time ago as a proof of concept. Added some more comments on how it works and a progress report.
Essentially we create a macro, then add bakes to the macro and set their properties etc. At the end of a queue, we put in an operator that says the bakes are done.
All this happens inside a modal, because for some reason successive bakes won’t start unless there’s an event callback.
import bpy, _bpy
class WM_OT_bake_report(bpy.types.Operator):
bl_idname = "wm.bake_report"
bl_label = "Bake Report"
bl_options = {'INTERNAL'}
queue_position: bpy.props.IntProperty()
queue_size: bpy.props.IntProperty()
def execute(self, context):
p = self.queue_position
s = self.queue_size
self.report({'INFO'}, f"{round(100 * p / s)}%")
return {'FINISHED'}
class WM_OT_set_finished(bpy.types.Operator):
bl_idname = "wm.bake_set_finished"
bl_label = "Bake Set Finished"
bl_options = {'INTERNAL'}
def execute(self, context):
# this is the last operator to run in the bake queue
# signal the modal operator to end
WM_OT_bake_modal._ready = True
return {'FINISHED'}
def get_macro():
class OBJECT_OT_bake_macro(bpy.types.Macro):
bl_idname = "object.bake_macro"
bl_label = "Bake Macro"
bl_options = {'INTERNAL'}
# unregister any previous macro
if hasattr(bpy.types, "OBJECT_OT_bake_macro"):
bpy.utils.unregister_class(bpy.types.OBJECT_OT_bake_macro)
bpy.utils.register_class(OBJECT_OT_bake_macro)
return OBJECT_OT_bake_macro
# bake operator
class WM_OT_bake_modal(bpy.types.Operator):
bl_idname = "wm.bake_modal"
bl_label = "Bake Modal"
def modal(self, context, event):
# keep checking the attribute
if getattr(__class__, '_ready', False):
__class__._ready = False
context.window_manager.event_timer_remove(self.timer)
self.report({'INFO'}, "Finished")
print("Done")
return {'FINISHED'}
return {'PASS_THROUGH'}
def invoke(self, context, event):
# macro is a container that lets
# us define any number of sub-operators
macro = get_macro()
# we can add sub-operators to a list for easy access
bake_queue = []
# let's do 16 bakes
num_bakes = 16
for i in range(num_bakes):
bake = _bpy.ops.macro_define(macro, 'OBJECT_OT_bake')
bake_queue.append(bake)
# add a progress report operator
report = _bpy.ops.macro_define(macro, 'WM_OT_bake_report')
report.properties.queue_size = num_bakes
report.properties.queue_position = i
# set some bake properties
bake_queue[0].properties.margin = 24
# define a last operator that tells the modal to end
_bpy.ops.macro_define(macro, 'WM_OT_bake_set_finished')
# the macro container is ready. remember
# to use 'INVOKE_DEFAULT' to keep the ui responsive
bpy.ops.object.bake_macro('INVOKE_DEFAULT')
__class__._ready = False
wm = context.window_manager
# a timer is needed to jumpstart each successive macro
self.timer = wm.event_timer_add(0.1, window=context.window)
wm.modal_handler_add(self)
return {'RUNNING_MODAL'}
def register():
bpy.utils.register_class(WM_OT_bake_report)
bpy.utils.register_class(WM_OT_set_finished)
bpy.utils.register_class(WM_OT_bake_modal)
if __name__ == '__main__':
register()