bpy.ops.object.bake('INVOKE_DEFAULT', type='DIFFUSE')
save_image()
Does not work because the bake uses 'INVOKE_DEFAULT' and thus save_image will be executed directly.
How to run save_image after the baking has finished?
bpy.ops.object.bake('INVOKE_DEFAULT', type='DIFFUSE')
save_image()
Does not work because the bake uses 'INVOKE_DEFAULT' and thus save_image will be executed directly.
How to run save_image after the baking has finished?
Although the question is old, can help more developers.
You can create a modal operator and check the state of the 'is_dirty' property of the image.
import bpy
class ModalTimerOperator(bpy.types.Operator):
bl_idname = "wm.modal_timer_operator"
bl_label = "Modal Timer Operator"
_timer = None
_img = None
def modal(self, context, event):
if event.type in {'RIGHTMOUSE', 'ESC'}:
self.cancel(context)
return {'CANCELLED'}
if event.type == 'TIMER':
if self._img.is_dirty: # <--- Wait until the image is marked as dirty
self.finish(context)
return {'FINISHED'}
return {'PASS_THROUGH'}
def execute(self, context: bpy.context):
if context.scene.render.engine != 'CYCLES':
context.scene.render.engine = 'CYCLES'
if not context.active_object.select_get() or not context.active_object.type == 'MESH':
self.report({'WARNING'}, "No valid selected objects")
return {'FINISHED'}
obj = context.active_object
mats = obj.data.materials
mats_len = len(mats)
if mats_len == 0 or not mats[0]:
mat_new = bpy.data.materials.new('BakeMaterial')
if mats_len == 0:
mats.append(mat_new)
else:
mats[0] = mat_new
pass
mat = mats[0]
mat.use_nodes = True
nodes = mat.node_tree.nodes
tex_node = nodes.new('ShaderNodeTexImage')
tex_node.name = 'BakeNode'
tex_node.select = True
nodes.active = tex_node
self._img = bpy.data.images.new('BakeResult', 1024, 1024)
tex_node.image = self._img
self.report({'INFO'}, "Execute")
result = bpy.ops.object.bake('INVOKE_DEFAULT', type='DIFFUSE')
if result != {'RUNNING_MODAL'}: # <--- Important to check the result
self.report({'WARNING'}, "Failed to start baking")
return {'FINISHED'}
wm = context.window_manager
self._timer = wm.event_timer_add(0.5, window=context.window)
wm.modal_handler_add(self)
return {'RUNNING_MODAL'}
def cancel(self, context):
self.report({'INFO'}, "Baking map cancelled")
def finish(self, context):
wm = context.window_manager
wm.event_timer_remove(self._timer)
self.report({'INFO'}, "Baking map completed")
#save_image() # <--- Call the function after baking
def register():
bpy.utils.register_class(ModalTimerOperator)
def unregister():
bpy.utils.unregister_class(ModalTimerOperator)
if name == "main":
register()
bpy.ops.wm.modal_timer_operator()
Thank you. Great script. As a side note, once the image data is baked, is_dity returns true. If you want to bake that image over again, you need to pack the image once. In
self._img.pack()
, the value of is_dirty returns false. Now you can create a modus operandi that bakes images on top of each other.
When overlapping bakes, scene settings should also be reviewed.
bpy.context.scene.render.bake.use_clear = False
render_postnorrender_completeis triggered – Florian Ludewig Mar 21 '20 at 11:31