Support rendering for selection of shots.

Also chooses middle frame of shot when rendering a single shot AND the
current frame isn't on that shot.
This commit is contained in:
Sybren A. Stüvel 2016-11-03 17:24:44 +01:00
parent 191b150280
commit 06a661126b

View File

@ -127,7 +127,8 @@ class ToolsPanel(Panel):
text='', icon='FILE_REFRESH') text='', icon='FILE_REFRESH')
row.operator(ATTRACT_OT_shot_open_in_browser.bl_idname, row.operator(ATTRACT_OT_shot_open_in_browser.bl_idname,
text='', icon='WORLD') text='', icon='WORLD')
sub.operator(ATTRACT_OT_make_shot_thumbnail.bl_idname) sub.operator(ATTRACT_OT_make_shot_thumbnail.bl_idname,
text='Render thumbnail for %s' % noun)
# Group more dangerous operations. # Group more dangerous operations.
dangerous_sub = layout.column(align=True) dangerous_sub = layout.column(align=True)
@ -494,8 +495,22 @@ class ATTRACT_OT_open_meta_blendfile(AttractOperatorMixin, Operator):
subprocess.Popen(cmd) subprocess.Popen(cmd)
class ATTRACT_OT_make_shot_thumbnail(AttractOperatorMixin,
async_loop.AsyncModalOperatorMixin,
Operator):
bl_idname = 'attract.make_shot_thumbnail'
bl_label = 'Render shot thumbnail'
bl_description = 'Renders the current frame, and uploads it as thumbnail for the shot'
stop_upon_exception = True
@classmethod
def poll(cls, context):
return bool(context.selected_sequences)
@contextlib.contextmanager @contextlib.contextmanager
def thumbnail_render_settings(context, thumbnail_width=512): def thumbnail_render_settings(self, context, thumbnail_width=512):
# Remember current settings so we can restore them later.
orig_res_x = context.scene.render.resolution_x orig_res_x = context.scene.render.resolution_x
orig_res_y = context.scene.render.resolution_y orig_res_y = context.scene.render.resolution_y
orig_percentage = context.scene.render.resolution_percentage orig_percentage = context.scene.render.resolution_percentage
@ -520,26 +535,75 @@ def thumbnail_render_settings(context, thumbnail_width=512):
context.scene.render.image_settings.file_format = orig_file_format context.scene.render.image_settings.file_format = orig_file_format
context.scene.render.image_settings.quality = orig_quality context.scene.render.image_settings.quality = orig_quality
@contextlib.contextmanager
class ATTRACT_OT_make_shot_thumbnail(AttractOperatorMixin, def temporary_current_frame(self, context):
async_loop.AsyncModalOperatorMixin, current_frame = context.scene.frame_current
Operator): try:
bl_idname = 'attract.make_shot_thumbnail' yield
bl_label = 'Render shot thumbnail' finally:
bl_description = 'Renders the current frame, and uploads it as thumbnail for the shot' context.scene.frame_current = current_frame
stop_upon_exception = True
async def async_execute(self, context): async def async_execute(self, context):
# Later: for strip in context.selected_sequences: nr_of_strips = len(context.selected_sequences)
do_multishot = nr_of_strips > 1
with self.temporary_current_frame(context):
if do_multishot:
context.window_manager.progress_begin(0, nr_of_strips)
try:
self.report({'INFO'}, 'Rendering thumbnails for %i selected shots.' %
nr_of_strips)
for idx, strip in enumerate(context.selected_sequences):
context.window_manager.progress_update(idx)
# For multi-shot we can't just use the current frame (each thumb would be
# identical), so instead we use the middle frame. The first/last frames
# cannot be reliably used due to transitions with other shots.
self.set_middle_frame(context, strip)
await self.thumbnail_strip(context, strip)
if self._state == 'QUIT':
return
context.window_manager.progress_update(nr_of_strips)
finally:
context.window_manager.progress_end()
else:
strip = active_strip(context) strip = active_strip(context)
if not strip.frame_final_start <= context.scene.frame_current <= strip.frame_final_end:
self.report({'WARNING'}, 'Rendering middle frame as thumbnail for active shot.')
self.set_middle_frame(context, strip)
else:
self.report({'INFO'}, 'Rendering current frame as thumbnail for active shot.')
context.window_manager.progress_begin(0, 1)
context.window_manager.progress_update(0)
try:
await self.thumbnail_strip(context, strip)
finally:
context.window_manager.progress_update(1)
context.window_manager.progress_end()
if self._state == 'QUIT':
return
self.report({'INFO'}, 'Thumbnail uploaded to Attract')
self.quit()
def set_middle_frame(self, context, strip):
"""Sets the current frame to the middle frame of the strip."""
middle = round((strip.frame_final_start + strip.frame_final_end) / 2)
context.scene.frame_set(middle)
async def thumbnail_strip(self, context, strip):
atc_object_id = getattr(strip, 'atc_object_id', None) atc_object_id = getattr(strip, 'atc_object_id', None)
if not atc_object_id: if not atc_object_id:
self.report({'ERROR'}, 'Strip %s not set up for Attract' % strip.name) self.report({'ERROR'}, 'Strip %s not set up for Attract' % strip.name)
self.quit() self.quit()
return return
with thumbnail_render_settings(context): with self.thumbnail_render_settings(context):
bpy.ops.render.render() bpy.ops.render.render()
file_id = await self.upload_via_tempdir(bpy.data.images['Render Result'], file_id = await self.upload_via_tempdir(bpy.data.images['Render Result'],
'attract_shot_thumbnail.jpg') 'attract_shot_thumbnail.jpg')
@ -559,8 +623,6 @@ class ATTRACT_OT_make_shot_thumbnail(AttractOperatorMixin,
} }
}) })
self.report({'INFO'}, 'Thumbnail uploaded to Attract')
self.quit()
async def upload_via_tempdir(self, datablock, filename_on_cloud) -> pillarsdk.Node: async def upload_via_tempdir(self, datablock, filename_on_cloud) -> pillarsdk.Node:
"""Saves the datablock to file, and uploads it to the cloud. """Saves the datablock to file, and uploads it to the cloud.