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
110 if sq
.type == 'SCENE':
111 if only_selected
is False or sq
.select
is True:
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.
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
)
126 if os
.path
.isfile(file_path
) or os
.path
.islink(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' %
137 # Read all strips and render the output
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
)
159 root_folder
= rootpath
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
:
168 keyframe
= key
+ sq
.frame_start
169 if scene
.use_preview_range
:
170 if keyframe
< scene
.frame_preview_start
:
172 if keyframe
> scene
.frame_preview_end
:
175 if keyframe
< scene
.frame_start
:
177 if keyframe
> scene
.frame_end
:
179 # For frame name use only the number
180 if scene
.storypencil_render_numbering
== 'FRAME':
182 framename
= strip_name
+ '.' + self
.format_to4(key
)
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
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
)
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
,
221 {"name": framename
+ image_ext
}],
222 frame_start
=int(frame_start
),
223 frame_end
=int(frame_end
),
226 print("Render:" + strip_name
+ "/" + strip_scene
.name
)
227 print("Video From:", scene
.frame_start
,
228 "To", scene
.frame_end
)
230 filepath
= os
.path
.join(rootpath
, strip_name
)
232 if image_settings
.file_format
== 'FFMPEG':
233 ext
= self
.video_ext
[scene
.render
.ffmpeg
.format
]
237 if not filepath
.endswith(ext
):
240 scene
.render
.use_file_extension
= False
241 scene
.render
.filepath
= filepath
244 bpy
.ops
.render
.render(animation
=True)
246 # Add video to add strip later
247 if scene
.storypencil_add_render_strip
:
249 [filepath
, sq
.frame_start
+ sq
.frame_offset_start
])
251 # Add pending video Strips
253 bpy
.ops
.sequencer
.movie_strip_add(filepath
=vid
[0],
254 frame_start
=int(vid
[1]),
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')
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')