properties_output.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. # ##### BEGIN GPL LICENSE BLOCK #####
  2. #
  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.
  7. #
  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.
  12. #
  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.
  16. #
  17. # ##### END GPL LICENSE BLOCK #####
  18. # <pep8 compliant>
  19. import bpy
  20. from bpy.types import Menu, Panel, UIList
  21. from bl_ui.utils import PresetPanel
  22. from bpy.app.translations import pgettext_tip as tip_
  23. class RENDER_PT_presets(PresetPanel, Panel):
  24. bl_label = "Render Presets"
  25. preset_subdir = "render"
  26. preset_operator = "script.execute_preset"
  27. preset_add_operator = "render.preset_add"
  28. class RENDER_PT_ffmpeg_presets(PresetPanel, Panel):
  29. bl_label = "FFMPEG Presets"
  30. preset_subdir = "ffmpeg"
  31. preset_operator = "script.python_file_run"
  32. class RENDER_MT_framerate_presets(Menu):
  33. bl_label = "Frame Rate Presets"
  34. preset_subdir = "framerate"
  35. preset_operator = "script.execute_preset"
  36. draw = Menu.draw_preset
  37. class RenderOutputButtonsPanel:
  38. bl_space_type = 'PROPERTIES'
  39. bl_region_type = 'WINDOW'
  40. bl_context = "output"
  41. # COMPAT_ENGINES must be defined in each subclass, external engines can add themselves here
  42. @classmethod
  43. def poll(cls, context):
  44. return (context.engine in cls.COMPAT_ENGINES)
  45. class RENDER_PT_dimensions(RenderOutputButtonsPanel, Panel):
  46. bl_label = "Dimensions"
  47. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  48. _frame_rate_args_prev = None
  49. _preset_class = None
  50. def draw_header_preset(self, _context):
  51. RENDER_PT_presets.draw_panel_header(self.layout)
  52. @staticmethod
  53. def _draw_framerate_label(*args):
  54. # avoids re-creating text string each draw
  55. if RENDER_PT_dimensions._frame_rate_args_prev == args:
  56. return RENDER_PT_dimensions._frame_rate_ret
  57. fps, fps_base, preset_label = args
  58. if fps_base == 1.0:
  59. fps_rate = round(fps)
  60. else:
  61. fps_rate = round(fps / fps_base, 2)
  62. # TODO: Change the following to iterate over existing presets
  63. custom_framerate = (fps_rate not in {23.98, 24, 25, 29.97, 30, 50, 59.94, 60})
  64. if custom_framerate is True:
  65. fps_label_text = tip_("Custom (%.4g fps)") % fps_rate
  66. show_framerate = True
  67. else:
  68. fps_label_text = tip_("%.4g fps") % fps_rate
  69. show_framerate = (preset_label == "Custom")
  70. RENDER_PT_dimensions._frame_rate_args_prev = args
  71. RENDER_PT_dimensions._frame_rate_ret = args = (fps_label_text, show_framerate)
  72. return args
  73. @staticmethod
  74. def draw_framerate(layout, sub, rd):
  75. if RENDER_PT_dimensions._preset_class is None:
  76. RENDER_PT_dimensions._preset_class = bpy.types.RENDER_MT_framerate_presets
  77. args = rd.fps, rd.fps_base, RENDER_PT_dimensions._preset_class.bl_label
  78. fps_label_text, show_framerate = RENDER_PT_dimensions._draw_framerate_label(*args)
  79. sub.menu("RENDER_MT_framerate_presets", text=fps_label_text)
  80. if show_framerate:
  81. col = layout.column(align=True)
  82. col.prop(rd, "fps")
  83. col.prop(rd, "fps_base", text="Base")
  84. def draw(self, context):
  85. layout = self.layout
  86. layout.use_property_split = True
  87. layout.use_property_decorate = False # No animation.
  88. scene = context.scene
  89. rd = scene.render
  90. col = layout.column(align=True)
  91. col.prop(rd, "resolution_x", text="Resolution X")
  92. col.prop(rd, "resolution_y", text="Y")
  93. col.prop(rd, "resolution_percentage", text="%")
  94. col = layout.column(align=True)
  95. col.prop(rd, "pixel_aspect_x", text="Aspect X")
  96. col.prop(rd, "pixel_aspect_y", text="Y")
  97. col = layout.column(align=True)
  98. col.prop(rd, "use_border")
  99. sub = col.column(align=True)
  100. sub.active = rd.use_border
  101. sub.prop(rd, "use_crop_to_border")
  102. col = layout.column(align=True)
  103. col.prop(scene, "frame_start", text="Frame Start")
  104. col.prop(scene, "frame_end", text="End")
  105. col.prop(scene, "frame_step", text="Step")
  106. col = layout.split()
  107. col.alignment = 'RIGHT'
  108. col.label(text="Frame Rate")
  109. self.draw_framerate(layout, col, rd)
  110. class RENDER_PT_frame_remapping(RenderOutputButtonsPanel, Panel):
  111. bl_label = "Time Remapping"
  112. bl_parent_id = "RENDER_PT_dimensions"
  113. bl_options = {'DEFAULT_CLOSED'}
  114. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  115. def draw(self, context):
  116. layout = self.layout
  117. layout.use_property_split = True
  118. layout.use_property_decorate = False # No animation.
  119. rd = context.scene.render
  120. col = layout.column(align=True)
  121. col.prop(rd, "frame_map_old", text="Old")
  122. col.prop(rd, "frame_map_new", text="New")
  123. class RENDER_PT_post_processing(RenderOutputButtonsPanel, Panel):
  124. bl_label = "Post Processing"
  125. bl_options = {'DEFAULT_CLOSED'}
  126. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  127. def draw(self, context):
  128. layout = self.layout
  129. layout.use_property_split = True
  130. rd = context.scene.render
  131. flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
  132. col = flow.column()
  133. col.prop(rd, "use_compositing")
  134. col = flow.column()
  135. col.prop(rd, "use_sequencer")
  136. layout.prop(rd, "dither_intensity", text="Dither", slider=True)
  137. class RENDER_PT_stamp(RenderOutputButtonsPanel, Panel):
  138. bl_label = "Metadata"
  139. bl_options = {'DEFAULT_CLOSED'}
  140. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  141. def draw(self, context):
  142. layout = self.layout
  143. layout.use_property_split = True
  144. layout.use_property_decorate = False # No animation.
  145. rd = context.scene.render
  146. flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
  147. col = flow.column()
  148. col.prop(rd, "use_stamp_date", text="Date")
  149. col = flow.column()
  150. col.prop(rd, "use_stamp_time", text="Time")
  151. col = flow.column()
  152. col.prop(rd, "use_stamp_render_time", text="Render Time")
  153. col = flow.column()
  154. col.prop(rd, "use_stamp_frame", text="Frame")
  155. col = flow.column()
  156. col.prop(rd, "use_stamp_frame_range", text="Frame Range")
  157. col = flow.column()
  158. col.prop(rd, "use_stamp_memory", text="Memory")
  159. col = flow.column()
  160. col.prop(rd, "use_stamp_hostname", text="Hostname")
  161. col = flow.column()
  162. col.prop(rd, "use_stamp_camera", text="Camera")
  163. col = flow.column()
  164. col.prop(rd, "use_stamp_lens", text="Lens")
  165. col = flow.column()
  166. col.prop(rd, "use_stamp_scene", text="Scene")
  167. col = flow.column()
  168. col.prop(rd, "use_stamp_marker", text="Marker")
  169. col = flow.column()
  170. col.prop(rd, "use_stamp_filename", text="Filename")
  171. col = flow.column()
  172. col.prop(rd, "use_stamp_sequencer_strip", text="Strip Name")
  173. if rd.use_sequencer:
  174. col = flow.column()
  175. col.prop(rd, "use_stamp_strip_meta", text="Use Strip Metadata")
  176. class RENDER_PT_stamp_note(RenderOutputButtonsPanel, Panel):
  177. bl_label = "Note"
  178. bl_parent_id = "RENDER_PT_stamp"
  179. bl_options = {'DEFAULT_CLOSED'}
  180. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  181. def draw_header(self, context):
  182. rd = context.scene.render
  183. self.layout.prop(rd, "use_stamp_note", text="")
  184. def draw(self, context):
  185. layout = self.layout
  186. rd = context.scene.render
  187. layout.active = rd.use_stamp_note
  188. layout.prop(rd, "stamp_note_text", text="")
  189. class RENDER_PT_stamp_burn(RenderOutputButtonsPanel, Panel):
  190. bl_label = "Burn Into Image"
  191. bl_parent_id = "RENDER_PT_stamp"
  192. bl_options = {'DEFAULT_CLOSED'}
  193. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  194. def draw_header(self, context):
  195. rd = context.scene.render
  196. self.layout.prop(rd, "use_stamp", text="")
  197. def draw(self, context):
  198. layout = self.layout
  199. rd = context.scene.render
  200. layout.use_property_split = True
  201. col = layout.column()
  202. col.active = rd.use_stamp
  203. col.prop(rd, "stamp_font_size", text="Font Size")
  204. col.column().prop(rd, "stamp_foreground", slider=True)
  205. col.column().prop(rd, "stamp_background", slider=True)
  206. col.prop(rd, "use_stamp_labels", text="Include Labels")
  207. class RENDER_PT_output(RenderOutputButtonsPanel, Panel):
  208. bl_label = "Output"
  209. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  210. def draw(self, context):
  211. layout = self.layout
  212. layout.use_property_split = False
  213. layout.use_property_decorate = False # No animation.
  214. rd = context.scene.render
  215. image_settings = rd.image_settings
  216. layout.prop(rd, "filepath", text="")
  217. layout.use_property_split = True
  218. flow = layout.grid_flow(row_major=True, columns=0, even_columns=True, even_rows=False, align=False)
  219. col = flow.column()
  220. col.active = not rd.is_movie_format
  221. col.prop(rd, "use_overwrite")
  222. col = flow.column()
  223. col.active = not rd.is_movie_format
  224. col.prop(rd, "use_placeholder")
  225. col = flow.column()
  226. col.prop(rd, "use_file_extension")
  227. col = flow.column()
  228. col.prop(rd, "use_render_cache")
  229. layout.template_image_settings(image_settings, color_management=False)
  230. class RENDER_PT_output_views(RenderOutputButtonsPanel, Panel):
  231. bl_label = "Views"
  232. bl_parent_id = "RENDER_PT_output"
  233. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  234. @classmethod
  235. def poll(cls, context):
  236. rd = context.scene.render
  237. return rd.use_multiview
  238. def draw(self, context):
  239. layout = self.layout
  240. layout.use_property_split = False
  241. layout.use_property_decorate = False # No animation.
  242. rd = context.scene.render
  243. layout.template_image_views(rd.image_settings)
  244. class RENDER_PT_encoding(RenderOutputButtonsPanel, Panel):
  245. bl_label = "Encoding"
  246. bl_parent_id = "RENDER_PT_output"
  247. bl_options = {'DEFAULT_CLOSED'}
  248. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  249. def draw_header_preset(self, _context):
  250. RENDER_PT_ffmpeg_presets.draw_panel_header(self.layout)
  251. @classmethod
  252. def poll(cls, context):
  253. rd = context.scene.render
  254. return rd.image_settings.file_format in {'FFMPEG', 'XVID', 'H264', 'THEORA'}
  255. def draw(self, context):
  256. layout = self.layout
  257. layout.use_property_split = True
  258. layout.use_property_decorate = False
  259. rd = context.scene.render
  260. ffmpeg = rd.ffmpeg
  261. layout.prop(rd.ffmpeg, "format")
  262. layout.prop(ffmpeg, "use_autosplit")
  263. class RENDER_PT_encoding_video(RenderOutputButtonsPanel, Panel):
  264. bl_label = "Video"
  265. bl_parent_id = "RENDER_PT_encoding"
  266. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  267. @classmethod
  268. def poll(cls, context):
  269. rd = context.scene.render
  270. return rd.image_settings.file_format in {'FFMPEG', 'XVID', 'H264', 'THEORA'}
  271. def draw(self, context):
  272. layout = self.layout
  273. layout.use_property_split = True
  274. layout.use_property_decorate = False
  275. self.draw_vcodec(context)
  276. def draw_vcodec(self, context):
  277. """Video codec options."""
  278. layout = self.layout
  279. ffmpeg = context.scene.render.ffmpeg
  280. needs_codec = ffmpeg.format in {'AVI', 'QUICKTIME', 'MKV', 'OGG', 'MPEG4'}
  281. if needs_codec:
  282. layout.prop(ffmpeg, "codec")
  283. if needs_codec and ffmpeg.codec == 'NONE':
  284. return
  285. if ffmpeg.codec == 'DNXHD':
  286. layout.prop(ffmpeg, "use_lossless_output")
  287. # Output quality
  288. use_crf = needs_codec and ffmpeg.codec in {'H264', 'MPEG4', 'WEBM'}
  289. if use_crf:
  290. layout.prop(ffmpeg, "constant_rate_factor")
  291. # Encoding speed
  292. layout.prop(ffmpeg, "ffmpeg_preset")
  293. # I-frames
  294. layout.prop(ffmpeg, "gopsize")
  295. # B-Frames
  296. split = layout.split(factor=0.5)
  297. split.prop(ffmpeg, "use_max_b_frames", text="Max B-frames")
  298. pbox = split.column()
  299. pbox.prop(ffmpeg, "max_b_frames", text="")
  300. pbox.enabled = ffmpeg.use_max_b_frames
  301. if not use_crf or ffmpeg.constant_rate_factor == 'NONE':
  302. col = layout.column()
  303. sub = col.column(align=True)
  304. sub.prop(ffmpeg, "video_bitrate")
  305. sub.prop(ffmpeg, "minrate", text="Minimum")
  306. sub.prop(ffmpeg, "maxrate", text="Maximum")
  307. col.prop(ffmpeg, "buffersize", text="Buffer")
  308. col.separator()
  309. col.prop(ffmpeg, "muxrate", text="Mux Rate")
  310. col.prop(ffmpeg, "packetsize", text="Mux Packet Size")
  311. class RENDER_PT_encoding_audio(RenderOutputButtonsPanel, Panel):
  312. bl_label = "Audio"
  313. bl_parent_id = "RENDER_PT_encoding"
  314. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  315. @classmethod
  316. def poll(cls, context):
  317. rd = context.scene.render
  318. return rd.image_settings.file_format in {'FFMPEG', 'XVID', 'H264', 'THEORA'}
  319. def draw(self, context):
  320. layout = self.layout
  321. layout.use_property_split = True
  322. layout.use_property_decorate = False
  323. rd = context.scene.render
  324. ffmpeg = rd.ffmpeg
  325. if ffmpeg.format != 'MP3':
  326. layout.prop(ffmpeg, "audio_codec", text="Audio Codec")
  327. if ffmpeg.audio_codec != 'NONE':
  328. layout.prop(ffmpeg, "audio_bitrate")
  329. layout.prop(ffmpeg, "audio_volume", slider=True)
  330. class RENDER_UL_renderviews(UIList):
  331. def draw_item(self, _context, layout, _data, item, icon, _active_data, _active_propname, index):
  332. view = item
  333. if self.layout_type in {'DEFAULT', 'COMPACT'}:
  334. if view.name in {"left", "right"}:
  335. layout.label(text=view.name, icon_value=icon + (not view.use))
  336. else:
  337. layout.prop(view, "name", text="", index=index, icon_value=icon, emboss=False)
  338. layout.prop(view, "use", text="", index=index)
  339. elif self.layout_type == 'GRID':
  340. layout.alignment = 'CENTER'
  341. layout.label(text="", icon_value=icon + (not view.use))
  342. class RENDER_PT_stereoscopy(RenderOutputButtonsPanel, Panel):
  343. bl_label = "Stereoscopy"
  344. COMPAT_ENGINES = {'BLENDER_RENDER', 'BLENDER_EEVEE', 'BLENDER_WORKBENCH'}
  345. bl_options = {'DEFAULT_CLOSED'}
  346. def draw_header(self, context):
  347. rd = context.scene.render
  348. self.layout.prop(rd, "use_multiview", text="")
  349. def draw(self, context):
  350. layout = self.layout
  351. scene = context.scene
  352. rd = scene.render
  353. rv = rd.views.active
  354. layout.active = rd.use_multiview
  355. basic_stereo = rd.views_format == 'STEREO_3D'
  356. row = layout.row()
  357. row.use_property_split = True
  358. row.use_property_decorate = False
  359. row.prop(rd, "views_format")
  360. if basic_stereo:
  361. row = layout.row()
  362. row.template_list("RENDER_UL_renderviews", "name", rd, "stereo_views", rd.views, "active_index", rows=2)
  363. row = layout.row()
  364. row.use_property_split = True
  365. row.use_property_decorate = False
  366. row.prop(rv, "file_suffix")
  367. else:
  368. row = layout.row()
  369. row.template_list("RENDER_UL_renderviews", "name", rd, "views", rd.views, "active_index", rows=2)
  370. col = row.column(align=True)
  371. col.operator("scene.render_view_add", icon='ADD', text="")
  372. col.operator("scene.render_view_remove", icon='REMOVE', text="")
  373. row = layout.row()
  374. row.use_property_split = True
  375. row.use_property_decorate = False
  376. row.prop(rv, "camera_suffix")
  377. classes = (
  378. RENDER_PT_presets,
  379. RENDER_PT_ffmpeg_presets,
  380. RENDER_MT_framerate_presets,
  381. RENDER_PT_dimensions,
  382. RENDER_PT_frame_remapping,
  383. RENDER_PT_stereoscopy,
  384. RENDER_PT_output,
  385. RENDER_PT_output_views,
  386. RENDER_PT_encoding,
  387. RENDER_PT_encoding_video,
  388. RENDER_PT_encoding_audio,
  389. RENDER_PT_stamp,
  390. RENDER_PT_stamp_note,
  391. RENDER_PT_stamp_burn,
  392. RENDER_UL_renderviews,
  393. RENDER_PT_post_processing,
  394. )
  395. if __name__ == "__main__": # only for live edit.
  396. from bpy.utils import register_class
  397. for cls in classes:
  398. register_class(cls)