# ##### BEGIN GPL LICENSE BLOCK ##### # # This program is free software; you can redistribute it and/or # modify it under the terms of the GNU General Public License # as published by the Free Software Foundation; either version 2 # of the License, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. # # ##### END GPL LICENSE BLOCK ##### bl_info = { "name": "Animated Render Baker", "author": "Janne Karhu (jahka)", "version": (1, 0), "blender": (2, 5, 8), "location": "Properties > Render > Bake Panel", "description": "Renderbakes a series of frames", "category": "Object", 'wiki_url': 'http://wiki.blender.org/index.php/Extensions:2.6/Py/' \ 'Scripts/Object/Animated_Render_Baker', 'tracker_url': 'https://projects.blender.org/tracker/index.php?'\ 'func=detail&aid=24836'} import bpy from bpy.props import * class OBJECT_OT_animrenderbake(bpy.types.Operator): bl_label = "Animated Render Bake" bl_description= "Bake animated image textures of selected objects" bl_idname = "object.anim_bake_image" bl_register = True def framefile(self, orig, frame): """ Set frame number to file name image.png -> image0013.png """ dot = orig.rfind(".") return orig[:dot] + ('%04d' % frame) + orig[dot:] def invoke(self, context, event): import bpy import shutil scene = context.scene start = scene.animrenderbake_start end = scene.animrenderbake_end # Check for errors before starting if start >= end: self.report({'ERROR'}, "Start frame must be smaller than end frame") return {'CANCELLED'} selected = context.selected_objects # Only single object baking for now if scene.render.use_bake_selected_to_active: if len(selected) > 2: self.report({'ERROR'}, "Select only two objects for animated baking") return {'CANCELLED'} elif len(selected) > 1: self.report({'ERROR'}, "Select only one object for animated baking") return {'CANCELLED'} if context.active_object.type != 'MESH': self.report({'ERROR'}, "The baked object must be a mesh object") return {'CANCELLED'} img = None #find the image that's used for rendering for uvtex in context.active_object.data.uv_textures: if uvtex.active_render == True: for uvdata in uvtex.data: if uvdata.image != None: img = uvdata.image break if img is None: self.report({'ERROR'}, "No valid image found to bake to") return {'CANCELLED'} if img.is_dirty: self.report({'ERROR'}, "Save the image that's used for baking before use") return {'CANCELLED'} # make sure we have an absolute path so that copying works for sure absp = bpy.path.abspath(img.filepath, library=img.library) print("Animated baking for frames " + str(start) + " - " + str(end)) for cfra in range(start, end+1): print("Baking frame " + str(cfra)) # update scene to new frame and bake to template image scene.frame_set(cfra) ret = bpy.ops.object.bake_image() if 'CANCELLED' in ret: return {'CANCELLED'} #currently the api doesn't allow img.save_as(), so just save the template image as usual for every frame and copy to a file with frame specific filename img.save() shutil.copyfile(absp, self.framefile(absp, cfra)) print("Saved " + self.framefile(absp, cfra)) print("Baking done!") return{'FINISHED'} # modified copy of original bake panel draw function def draw_animrenderbake(self, context): layout = self.layout rd = context.scene.render row = layout.row() row.operator("object.bake_image", icon='RENDER_STILL') #----------- beginning of modifications ---------------- row.operator("object.anim_bake_image", text="Animated Bake", icon="RENDER_ANIMATION") row = layout.row(align=True) row.prop(context.scene, "animrenderbake_start") row.prop(context.scene, "animrenderbake_end") #-------------- end of modifications --------------------- layout.prop(rd, "bake_type") multires_bake = False if rd.bake_type in ['NORMALS', 'DISPLACEMENT']: layout.prop(rd, 'use_bake_multires') multires_bake = rd.use_bake_multires if not multires_bake: if rd.bake_type == 'NORMALS': layout.prop(rd, "bake_normal_space") elif rd.bake_type in {'DISPLACEMENT', 'AO'}: layout.prop(rd, "use_bake_normalize") # col.prop(rd, "bake_aa_mode") # col.prop(rd, "use_bake_antialiasing") layout.separator() split = layout.split() col = split.column() col.prop(rd, "use_bake_clear") col.prop(rd, "bake_margin") col.prop(rd, "bake_quad_split", text="Split") col = split.column() col.prop(rd, "use_bake_selected_to_active") sub = col.column() sub.active = rd.use_bake_selected_to_active sub.prop(rd, "bake_distance") sub.prop(rd, "bake_bias") else: if rd.bake_type == 'DISPLACEMENT': layout.prop(rd, "use_bake_lores_mesh") layout.prop(rd, "use_bake_clear") layout.prop(rd, "bake_margin") def register(): bpy.utils.register_module(__name__) bpy.types.Scene.animrenderbake_start = IntProperty( name="Start", description="Start frame of the animated bake", default=1) bpy.types.Scene.animrenderbake_end = IntProperty( name="End", description="End frame of the animated bake", default=250) # replace original panel draw function with modified one panel = bpy.types.RENDER_PT_bake panel.old_draw = panel.draw panel.draw = draw_animrenderbake def unregister(): bpy.utils.unregister_module(__name__) # restore original panel draw function bpy.types.RENDER_PT_bake.draw = bpy.types.RENDER_PT_bake.old_draw del bpy.types.RENDER_PT_bake.old_draw del bpy.types.Scene.animrenderbake_start del bpy.types.Scene.animrenderbake_end if __name__ == "__main__": register()