1 # SPDX-License-Identifier: GPL-2.0-or-later
8 from datetime
import datetime
9 from bpy
.types
import Operator
10 from .utils
import get_keyframe_list
12 # ------------------------------------------------------
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
35 # Extension by image format
46 "OPEN_EXR_MULTILAYER": ".exr",
53 # --------------------------------------------------------------------
54 # Format an int adding 4 zero padding
55 # --------------------------------------------------------------------
56 def format_to4(self
, value
):
59 # --------------------------------------------------------------------
60 # Add frames every N frames
61 # --------------------------------------------------------------------
62 def add_missing_frames(self
, sq
, step
, keyframe_list
):
64 lk
= len(keyframe_list
)
70 for i
in range(0, lk
- 1):
71 dist
= keyframe_list
[i
+ 1] - keyframe_list
[i
]
73 delta
= int(dist
/ step
)
75 for x
in range(1, delta
):
76 missing
.append(keyframe_list
[i
] + (step
* e
))
79 keyframe_list
.extend(missing
)
82 # ------------------------------
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
111 if sq
.type in ('SCENE', 'META'):
112 if only_selected
is False or sq
.select
is True:
113 if sq
.type == 'META' and is_video_output
:
116 if sq
.type == 'SCENE' and is_video_output
and sq
.parent_meta():
118 if sq
.type == 'SCENE':
122 Strips
= sorted(Strips
, key
=lambda strip
: strip
.frame_start
)
124 # For video, clear BL_proxy folder because sometimes the video
125 # is not rendered as expected if this folder has data.
126 # This ensure the output video is correct.
128 proxy_folder
= os
.path
.join(rootpath
, "BL_proxy")
129 if os
.path
.exists(proxy_folder
):
130 for filename
in os
.listdir(proxy_folder
):
131 file_path
= os
.path
.join(proxy_folder
, filename
)
133 if os
.path
.isfile(file_path
) or os
.path
.islink(file_path
):
135 elif os
.path
.isdir(file_path
):
136 shutil
.rmtree(file_path
)
137 except Exception as e
:
138 print('Failed to delete %s. Reason: %s' %
143 # Render Meta Strips (Only video)
145 meta_name
= meta
.name
146 scene
.frame_start
= int(meta
.frame_start
+ meta
.frame_offset_start
)
147 scene
.frame_end
= int(meta
.frame_start
+ meta
.frame_final_duration
- 1)
149 print("Meta:" + meta_name
)
150 print("Video From:", scene
.frame_start
,
151 "To", scene
.frame_end
)
153 filepath
= os
.path
.join(rootpath
, meta_name
)
155 if image_settings
.file_format
== 'FFMPEG':
156 ext
= self
.video_ext
[scene
.render
.ffmpeg
.format
]
160 if not filepath
.endswith(ext
):
163 scene
.render
.use_file_extension
= False
164 scene
.render
.filepath
= filepath
167 bpy
.ops
.render
.render(animation
=True)
169 # Add video to add meta strip later
170 if scene
.storypencil_add_render_strip
:
172 [filepath
, meta
.frame_start
+ meta
.frame_offset_start
])
174 # Read all scene strips and render the output (No META)
177 strip_scene
= sq
.scene
178 scene
.frame_start
= int(sq
.frame_start
+ sq
.frame_offset_start
)
179 scene
.frame_end
= int(scene
.frame_start
+ sq
.frame_final_duration
- 1) # Image
180 if is_video_output
is False:
181 # Get list of any keyframe
182 strip_start
= sq
.frame_offset_start
183 if strip_start
< strip_scene
.frame_start
:
184 strip_start
= strip_scene
.frame_start
186 strip_end
= strip_start
+ sq
.frame_final_duration
- 1
187 keyframe_list
= get_keyframe_list(
188 strip_scene
, strip_start
, strip_end
)
189 self
.add_missing_frames(sq
, step
, keyframe_list
)
191 scene
.render
.use_file_extension
= True
192 foldername
= strip_name
193 if scene
.storypencil_add_render_byfolder
is True:
194 root_folder
= os
.path
.join(rootpath
, foldername
)
196 root_folder
= rootpath
199 print("Render:" + strip_name
+ "/" + strip_scene
.name
)
200 print("Image From:", strip_start
, "To", strip_end
)
201 for key
in range(int(strip_start
), int(strip_end
) + 1):
202 if key
not in keyframe_list
:
205 keyframe
= key
+ sq
.frame_start
206 if scene
.use_preview_range
:
207 if keyframe
< scene
.frame_preview_start
:
209 if keyframe
> scene
.frame_preview_end
:
212 if keyframe
< scene
.frame_start
:
214 if keyframe
> scene
.frame_end
:
216 # For frame name use only the number
217 if scene
.storypencil_render_numbering
== 'FRAME':
219 framename
= strip_name
+ '.' + self
.format_to4(key
)
223 framename
= strip_name
+ '.' + \
224 self
.format_to4(frame_nrr
)
226 filepath
= os
.path
.join(root_folder
, framename
)
227 scene
.render
.filepath
= filepath
230 scene
.frame_set(int(keyframe
- 1.0), subframe
=0.0)
231 bpy
.ops
.render
.render(
232 animation
=False, write_still
=True)
234 # Add strip with the corresponding length
235 if scene
.storypencil_add_render_strip
:
236 frame_start
= sq
.frame_start
+ key
- 1
237 index
= keyframe_list
.index(key
)
238 if index
< len(keyframe_list
) - 1:
239 key_next
= keyframe_list
[index
+ 1]
240 frame_end
= frame_start
+ (key_next
- key
)
242 frame_end
= scene
.frame_end
+ 1
244 if index
== 0 and frame_start
> scene
.frame_start
:
245 frame_start
= scene
.frame_start
247 if frame_end
< frame_start
:
248 frame_end
= frame_start
249 image_ext
= self
.image_ext
[image_settings
.file_format
]
250 bpy
.ops
.sequencer
.image_strip_add(directory
=root_folder
,
252 {"name": framename
+ image_ext
}],
253 frame_start
=int(frame_start
),
254 frame_end
=int(frame_end
),
257 print("Render:" + strip_name
+ "/" + strip_scene
.name
)
258 print("Video From:", scene
.frame_start
,
259 "To", scene
.frame_end
)
261 filepath
= os
.path
.join(rootpath
, strip_name
)
263 if image_settings
.file_format
== 'FFMPEG':
264 ext
= self
.video_ext
[scene
.render
.ffmpeg
.format
]
268 if not filepath
.endswith(ext
):
271 scene
.render
.use_file_extension
= False
272 scene
.render
.filepath
= filepath
275 bpy
.ops
.render
.render(animation
=True)
277 # Add video to add strip later
278 if scene
.storypencil_add_render_strip
:
280 [filepath
, sq
.frame_start
+ sq
.frame_offset_start
])
282 # Add pending video Strips
284 bpy
.ops
.sequencer
.movie_strip_add(filepath
=vid
[0],
285 frame_start
=int(vid
[1]),
288 scene
.frame_start
= prv_start
289 scene
.frame_end
= prv_end
290 scene
.render
.use_file_extension
= prv_use_file_extension
291 image_settings
.file_format
= prv_format
292 scene
.render
.ffmpeg
.format
= prv_ffmpeg_format
294 scene
.render
.filepath
= prv_path
295 scene
.frame_set(int(prv_frame
))
297 context
.window
.cursor_set('DEFAULT')
302 print("Unexpected error:" + str(sys
.exc_info()))
303 self
.report({'ERROR'}, "Unable to render")
304 scene
.frame_start
= prv_start
305 scene
.frame_end
= prv_end
306 scene
.render
.use_file_extension
= prv_use_file_extension
307 image_settings
.file_format
= prv_format
309 scene
.render
.filepath
= prv_path
310 scene
.frame_set(int(prv_frame
))
311 context
.window
.cursor_set('DEFAULT')