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 #####
20 "name": "Animated Render Baker",
21 "author": "Janne Karhu (jahka)",
23 "blender": (2, 75, 0),
24 "location": "Properties > Render > Bake Panel",
25 "description": "Renderbakes a series of frames",
27 "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.6/Py/"
28 "Scripts/Object/Animated_Render_Baker",
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"
41 def framefile(self
, filepath
, frame
):
43 Set frame number to file name image.png -> image0013.png
46 fn
, ext
= os
.path
.splitext(filepath
)
47 return "%s%04d%s" % (fn
, frame
, ext
)
49 def invoke(self
, context
, event
):
51 is_cycles
= (context
.scene
.render
.engine
== 'CYCLES')
55 start
= scene
.animrenderbake_start
56 end
= scene
.animrenderbake_end
58 # Check for errors before starting
60 self
.report({'ERROR'}, "Start frame must be smaller than end frame")
63 selected
= context
.selected_objects
65 # Only single object baking for now
66 if scene
.render
.use_bake_selected_to_active
:
68 self
.report({'ERROR'}, "Select only two objects for animated baking")
70 elif len(selected
) > 1:
71 self
.report({'ERROR'}, "Select only one object for animated baking")
74 if context
.active_object
.type != 'MESH':
75 self
.report({'ERROR'}, "The baked object must be a mesh object")
78 if context
.active_object
.mode
== 'EDIT':
79 self
.report({'ERROR'}, "Can't bake in edit-mode")
84 # find the image that's used for rendering
85 # TODO: support multiple images per bake
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?
91 for mat_slot
in context
.active_object
.material_slots
:
92 mat
= mat_slot
.material
93 if not mat
or not mat
.node_tree
:
95 trees
= [mat
.node_tree
]
96 while trees
and not img
:
98 node
= tree
.nodes
.active
99 if node
.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'}:
102 for node
in tree
.nodes
:
103 if node
.type in {'TEX_IMAGE', 'TEX_ENVIRONMENT'} and node
.image
:
110 elif node
.type == 'GROUP':
111 trees
.add(node
.node_tree
)
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:
128 self
.report({'ERROR'}, "No valid image found to bake to")
132 self
.report({'ERROR'}, "Save the image that's used for baking before use")
135 if img
.packed_file
is not None:
136 self
.report({'ERROR'}, "Can't animation-bake packed file")
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
)
150 ret
= bpy
.ops
.object.bake()
152 ret
= bpy
.ops
.object.bake_image()
153 if 'CANCELLED' in ret
:
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
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!")
169 def draw(self
, context
):
172 scene
= context
.scene
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")
182 bpy
.utils
.register_module(__name__
)
184 bpy
.types
.Scene
.animrenderbake_start
= IntProperty(
186 description
="Start frame of the animated bake",
189 bpy
.types
.Scene
.animrenderbake_end
= IntProperty(
191 description
="End frame of the animated bake",
194 bpy
.types
.RENDER_PT_bake
.prepend(draw
)
195 cycles_panel
= getattr(bpy
.types
, "CYCLES_RENDER_PT_bake", None)
197 cycles_panel
.prepend(draw
)
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)
210 cycles_panel
.remove(draw
)
213 if __name__
== "__main__":