Merge branch 'master' into blender2.8
[blender-addons.git] / object_animrenderbake.py
blobea9713bb71451f20fd149c8120be1e64c7c1d3d2
1 # ##### BEGIN GPL LICENSE BLOCK #####
3 # This program is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU General Public License
5 # as published by the Free Software Foundation; either version 2
6 # of the License, or (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program; if not, write to the Free Software Foundation,
15 # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 # ##### END GPL LICENSE BLOCK #####
19 bl_info = {
20 "name": "Animated Render Baker",
21 "author": "Janne Karhu (jahka)",
22 "version": (2, 0),
23 "blender": (2, 75, 0),
24 "location": "Properties > Render > Bake Panel",
25 "description": "Renderbakes a series of frames",
26 "category": "Render",
27 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
28 "Scripts/Object/Animated_Render_Baker",
32 import bpy
33 from bpy.props import IntProperty
35 class OBJECT_OT_animrenderbake(bpy.types.Operator):
36 bl_label = "Animated Render Bake"
37 bl_description= "Bake animated image textures of selected objects"
38 bl_idname = "object.anim_bake_image"
39 bl_register = True
41 def framefile(self, filepath, frame):
42 """
43 Set frame number to file name image.png -> image0013.png
44 """
45 import os
46 fn, ext = os.path.splitext(filepath)
47 return "%s%04d%s" % (fn, frame, ext)
49 def invoke(self, context, event):
50 import shutil
51 is_cycles = (context.scene.render.engine == 'CYCLES')
53 scene = context.scene
55 start = scene.animrenderbake_start
56 end = scene.animrenderbake_end
58 # Check for errors before starting
59 if start >= end:
60 self.report({'ERROR'}, "Start frame must be smaller than end frame")
61 return {'CANCELLED'}
63 selected = context.selected_objects
65 # Only single object baking for now
66 if scene.render.use_bake_selected_to_active:
67 if len(selected) > 2:
68 self.report({'ERROR'}, "Select only two objects for animated baking")
69 return {'CANCELLED'}
70 elif len(selected) > 1:
71 self.report({'ERROR'}, "Select only one object for animated baking")
72 return {'CANCELLED'}
74 if context.active_object.type != 'MESH':
75 self.report({'ERROR'}, "The baked object must be a mesh object")
76 return {'CANCELLED'}
78 if context.active_object.mode == 'EDIT':
79 self.report({'ERROR'}, "Can't bake in edit-mode")
80 return {'CANCELLED'}
82 img = None
84 # find the image that's used for rendering
85 # TODO: support multiple images per bake
86 if is_cycles:
87 # XXX This tries to mimic nodeGetActiveTexture(), but we have no access to 'texture_active' state from RNA...
88 # IMHO, this should be a func in RNA nodetree struct anyway?
89 inactive = None
90 selected = None
91 for mat_slot in context.active_object.material_slots:
92 mat = mat_slot.material
93 if not mat or not mat.node_tree:
94 continue
95 trees = [mat.node_tree]
96 while trees and not img:
97 tree = trees.pop()
98 node = tree.nodes.active
99 if node.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'}:
100 img = node.image
101 break
102 for node in tree.nodes:
103 if node.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'} and node.image:
104 if node.select:
105 if not selected:
106 selected = node
107 else:
108 if not inactive:
109 inactive = node
110 elif node.type == 'GROUP':
111 trees.add(node.node_tree)
112 if img:
113 break
114 if not img:
115 if selected:
116 img = selected.image
117 elif inactive:
118 img = inactive.image
119 else:
120 for uvtex in context.active_object.data.uv_textures:
121 if uvtex.active_render == True:
122 for uvdata in uvtex.data:
123 if uvdata.image is not None:
124 img = uvdata.image
125 break
127 if img is None:
128 self.report({'ERROR'}, "No valid image found to bake to")
129 return {'CANCELLED'}
131 if img.is_dirty:
132 self.report({'ERROR'}, "Save the image that's used for baking before use")
133 return {'CANCELLED'}
135 if img.packed_file is not None:
136 self.report({'ERROR'}, "Can't animation-bake packed file")
137 return {'CANCELLED'}
139 # make sure we have an absolute path so that copying works for sure
140 img_filepath_abs = bpy.path.abspath(img.filepath, library=img.library)
142 print("Animated baking for frames (%d - %d)" % (start, end))
144 for cfra in range(start, end + 1):
145 print("Baking frame %d" % cfra)
147 # update scene to new frame and bake to template image
148 scene.frame_set(cfra)
149 if is_cycles:
150 ret = bpy.ops.object.bake()
151 else:
152 ret = bpy.ops.object.bake_image()
153 if 'CANCELLED' in ret:
154 return {'CANCELLED'}
156 # Currently the api doesn't allow img.save_as()
157 # so just save the template image as usual for
158 # every frame and copy to a file with frame specific filename
159 img.save()
160 img_filepath_new = self.framefile(img_filepath_abs, cfra)
161 shutil.copyfile(img_filepath_abs, img_filepath_new)
162 print("Saved %r" % img_filepath_new)
164 print("Baking done!")
166 return{'FINISHED'}
169 def draw(self, context):
170 layout = self.layout
172 scene = context.scene
174 row = layout.row()
175 row.operator("object.anim_bake_image", text="Animated Bake", icon="RENDER_ANIMATION")
176 rowsub = row.row(align=True)
177 rowsub.prop(scene, "animrenderbake_start")
178 rowsub.prop(scene, "animrenderbake_end")
181 def register():
182 bpy.utils.register_module(__name__)
184 bpy.types.Scene.animrenderbake_start = IntProperty(
185 name="Start",
186 description="Start frame of the animated bake",
187 default=1)
189 bpy.types.Scene.animrenderbake_end = IntProperty(
190 name="End",
191 description="End frame of the animated bake",
192 default=250)
194 bpy.types.RENDER_PT_bake.prepend(draw)
195 cycles_panel = getattr(bpy.types, "CYCLES_RENDER_PT_bake", None)
196 if cycles_panel:
197 cycles_panel.prepend(draw)
200 def unregister():
201 bpy.utils.unregister_module(__name__)
203 # restore original panel draw function
204 del bpy.types.Scene.animrenderbake_start
205 del bpy.types.Scene.animrenderbake_end
207 bpy.types.RENDER_PT_bake.remove(draw)
208 cycles_panel = getattr(bpy.types, "CYCLES_RENDER_PT_bake", None)
209 if cycles_panel:
210 cycles_panel.remove(draw)
213 if __name__ == "__main__":
214 register()