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:
parent
191b150280
commit
06a661126b
@ -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.
|
||||||
|
Reference in New Issue
Block a user