Node Wrangler: Added proper labels for merging shader nodes
[blender-addons.git] / storypencil / render.py
blob31d07e2fac5ff729c404a31e0143e9c2e0f2082f
1 # SPDX-License-Identifier: GPL-2.0-or-later
3 import bpy
4 import os
5 import shutil
6 import sys
8 from datetime import datetime
9 from bpy.types import Operator
10 from .utils import get_keyframe_list
12 # ------------------------------------------------------
13 # Button: Render VSE
14 # ------------------------------------------------------
17 class STORYPENCIL_OT_RenderAction(Operator):
18 bl_idname = "storypencil.render_vse"
19 bl_label = "Render Strips"
20 bl_description = "Render VSE strips"
22 # Extension by FFMPEG container type
23 video_ext = {
24 "MPEG1": ".mpg",
25 "MPEG2": ".dvd",
26 "MPEG4": ".mp4",
27 "AVI": ".avi",
28 "QUICKTIME": ".mov",
29 "DV": ".dv",
30 "OGG": ".ogv",
31 "MKV": ".mkv",
32 "FLASH": ".flv",
33 "WEBM": ".webm"
35 # Extension by image format
36 image_ext = {
37 "BMP": ".bmp",
38 "IRIS": ".rgb",
39 "PNG": ".png",
40 "JPEG": ".jpg",
41 "JPEG2000": ".jp2",
42 "TARGA": ".tga",
43 "TARGA_RAW": ".tga",
44 "CINEON": ".cin",
45 "DPX": ".dpx",
46 "OPEN_EXR_MULTILAYER": ".exr",
47 "OPEN_EXR": ".exr",
48 "HDR": ".hdr",
49 "TIFF": ".tif",
50 "WEBP": ".webp"
53 # --------------------------------------------------------------------
54 # Format an int adding 4 zero padding
55 # --------------------------------------------------------------------
56 def format_to4(self, value):
57 return f"{value:04}"
59 # --------------------------------------------------------------------
60 # Add frames every N frames
61 # --------------------------------------------------------------------
62 def add_missing_frames(self, sq, step, keyframe_list):
63 missing = []
64 lk = len(keyframe_list)
65 if lk == 0:
66 return
68 # Add mid frames
69 if step > 0:
70 for i in range(0, lk - 1):
71 dist = keyframe_list[i + 1] - keyframe_list[i]
72 if dist > step:
73 delta = int(dist / step)
74 e = 1
75 for x in range(1, delta):
76 missing.append(keyframe_list[i] + (step * e))
77 e += 1
79 keyframe_list.extend(missing)
80 keyframe_list.sort()
82 # ------------------------------
83 # Execute
84 # ------------------------------
85 def execute(self, context):
86 scene = bpy.context.scene
87 image_settings = scene.render.image_settings
88 is_video_output = image_settings.file_format in {
89 'FFMPEG', 'AVI_JPEG', 'AVI_RAW'}
90 step = scene.storypencil_render_step
92 sequences = scene.sequence_editor.sequences_all
93 prv_start = scene.frame_start
94 prv_end = scene.frame_end
95 prv_frame = bpy.context.scene.frame_current
97 prv_path = scene.render.filepath
98 prv_format = image_settings.file_format
99 prv_use_file_extension = scene.render.use_file_extension
100 prv_ffmpeg_format = scene.render.ffmpeg.format
101 rootpath = bpy.path.abspath(scene.storypencil_render_render_path)
102 only_selected = scene.storypencil_render_onlyselected
103 channel = scene.storypencil_render_channel
105 context.window.cursor_set('WAIT')
107 # Create list of selected strips because the selection is changed when adding new strips
108 Strips = []
109 for sq in sequences:
110 if sq.type == 'SCENE':
111 if only_selected is False or sq.select is True:
112 Strips.append(sq)
114 # Sort strips
115 Strips = sorted(Strips, key=lambda strip: strip.frame_start)
117 # For video, clear BL_proxy folder because sometimes the video
118 # is not rendered as expected if this folder has data.
119 # This ensure the output video is correct.
120 if is_video_output:
121 proxy_folder = os.path.join(rootpath, "BL_proxy")
122 if os.path.exists(proxy_folder):
123 for filename in os.listdir(proxy_folder):
124 file_path = os.path.join(proxy_folder, filename)
125 try:
126 if os.path.isfile(file_path) or os.path.islink(file_path):
127 os.unlink(file_path)
128 elif os.path.isdir(file_path):
129 shutil.rmtree(file_path)
130 except Exception as e:
131 print('Failed to delete %s. Reason: %s' %
132 (file_path, e))
134 try:
135 Videos = []
136 Sheets = []
137 # Read all strips and render the output
138 for sq in Strips:
139 strip_name = sq.name
140 strip_scene = sq.scene
141 scene.frame_start = int(sq.frame_start + sq.frame_offset_start)
142 scene.frame_end = int(scene.frame_start + sq.frame_final_duration - 1) # Image
143 if is_video_output is False:
144 # Get list of any keyframe
145 strip_start = sq.frame_offset_start
146 if strip_start < strip_scene.frame_start:
147 strip_start = strip_scene.frame_start
149 strip_end = strip_start + sq.frame_final_duration - 1
150 keyframe_list = get_keyframe_list(
151 strip_scene, strip_start, strip_end)
152 self.add_missing_frames(sq, step, keyframe_list)
154 scene.render.use_file_extension = True
155 foldername = strip_name
156 if scene.storypencil_add_render_byfolder is True:
157 root_folder = os.path.join(rootpath, foldername)
158 else:
159 root_folder = rootpath
161 frame_nrr = 0
162 print("Render:" + strip_name + "/" + strip_scene.name)
163 print("Image From:", strip_start, "To", strip_end)
164 for key in range(int(strip_start), int(strip_end) + 1):
165 if key not in keyframe_list:
166 continue
168 keyframe = key + sq.frame_start
169 if scene.use_preview_range:
170 if keyframe < scene.frame_preview_start:
171 continue
172 if keyframe > scene.frame_preview_end:
173 break
174 else:
175 if keyframe < scene.frame_start:
176 continue
177 if keyframe > scene.frame_end:
178 break
179 # For frame name use only the number
180 if scene.storypencil_render_numbering == 'FRAME':
181 # Real
182 framename = strip_name + '.' + self.format_to4(key)
183 else:
184 # Consecutive
185 frame_nrr += 1
186 framename = strip_name + '.' + \
187 self.format_to4(frame_nrr)
189 filepath = os.path.join(root_folder, framename)
191 sheet = os.path.realpath(filepath)
192 sheet = bpy.path.ensure_ext(
193 sheet, self.image_ext[image_settings.file_format])
194 Sheets.append([sheet, keyframe])
196 scene.render.filepath = filepath
198 # Render Frame
199 scene.frame_set(int(keyframe - 1.0), subframe=0.0)
200 bpy.ops.render.render(
201 animation=False, write_still=True)
203 # Add strip with the corresponding length
204 if scene.storypencil_add_render_strip:
205 frame_start = sq.frame_start + key - 1
206 index = keyframe_list.index(key)
207 if index < len(keyframe_list) - 1:
208 key_next = keyframe_list[index + 1]
209 frame_end = frame_start + (key_next - key)
210 else:
211 frame_end = scene.frame_end + 1
213 if index == 0 and frame_start > scene.frame_start:
214 frame_start = scene.frame_start
216 if frame_end < frame_start:
217 frame_end = frame_start
218 image_ext = self.image_ext[image_settings.file_format]
219 bpy.ops.sequencer.image_strip_add(directory=root_folder,
220 files=[
221 {"name": framename + image_ext}],
222 frame_start=int(frame_start),
223 frame_end=int(frame_end),
224 channel=channel)
225 else:
226 print("Render:" + strip_name + "/" + strip_scene.name)
227 print("Video From:", scene.frame_start,
228 "To", scene.frame_end)
229 # Video
230 filepath = os.path.join(rootpath, strip_name)
232 if image_settings.file_format == 'FFMPEG':
233 ext = self.video_ext[scene.render.ffmpeg.format]
234 else:
235 ext = '.avi'
237 if not filepath.endswith(ext):
238 filepath += ext
240 scene.render.use_file_extension = False
241 scene.render.filepath = filepath
243 # Render Animation
244 bpy.ops.render.render(animation=True)
246 # Add video to add strip later
247 if scene.storypencil_add_render_strip:
248 Videos.append(
249 [filepath, sq.frame_start + sq.frame_offset_start])
251 # Add pending video Strips
252 for vid in Videos:
253 bpy.ops.sequencer.movie_strip_add(filepath=vid[0],
254 frame_start=int(vid[1]),
255 channel=channel)
257 scene.frame_start = prv_start
258 scene.frame_end = prv_end
259 scene.render.use_file_extension = prv_use_file_extension
260 image_settings.file_format = prv_format
261 scene.render.ffmpeg.format = prv_ffmpeg_format
263 scene.render.filepath = prv_path
264 scene.frame_set(int(prv_frame))
266 context.window.cursor_set('DEFAULT')
268 return {'FINISHED'}
270 except:
271 print("Unexpected error:" + str(sys.exc_info()))
272 self.report({'ERROR'}, "Unable to render")
273 scene.frame_start = prv_start
274 scene.frame_end = prv_end
275 scene.render.use_file_extension = prv_use_file_extension
276 image_settings.file_format = prv_format
278 scene.render.filepath = prv_path
279 scene.frame_set(int(prv_frame))
280 context.window.cursor_set('DEFAULT')
281 return {'FINISHED'}