diff --git a/render_shots.py b/render_shots.py deleted file mode 100644 index f9e8f71d186aea7b850135cf4feb610efdf5e42e..0000000000000000000000000000000000000000 --- a/render_shots.py +++ /dev/null @@ -1,726 +0,0 @@ -# ***** BEGIN GPL LICENSE BLOCK ***** -# -# This program is free software; you can redistribute it and/or -# modify it under the terms of the GNU General Public License -# as published by the Free Software Foundation; either version 2 -# of the License, or (at your option) any later version. -# -# This program is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU General Public License -# along with this program; if not, write to the Free Software Foundation, -# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -# -# ***** END GPL LICENCE BLOCK ***** - - -bl_info = { - "name": "Render Shots", - "author": "Aaron Symons", - "version": (0, 3, 2), - "blender": (2, 76, 0), - "location": "Properties > Render > Render Shots", - "description": "Render an image or animation from different camera views", - "warning": "", - "wiki_url": "http://wiki.blender.org/index.php?title=Extensions:2.6/Py"\ - "/Scripts/Render/Render_Shots", - "tracker_url": "https://developer.blender.org/maniphest/task/edit/form/2/", - "category": "Render"} - - -import bpy -from bpy.props import BoolProperty, IntProperty, StringProperty -from bpy.app.handlers import persistent -import os, shutil - - -##################################### -# Update Functions -##################################### -def shape_nav(self, context): - nav = self.rs_shotshape_nav - - if self.rs_shotshape_shape != "": - shapeVerts = bpy.data.objects[self.rs_shotshape_shape].data.vertices - max = len(shapeVerts)-1 - min = max - (max+max) - - if nav > max or nav < min: - nav = 0 - - v = shapeVerts[nav].co - self.location = (v[0], v[1], v[2]) - return None - - -def is_new_object(ob): - try: - isNew = ob["rs_shotshape_use_frames"] - except: - isNew = None - - return True if isNew is None else False - - -def update_shot_list(scn): - scn.rs_is_updating = True - if hasattr(scn, 'rs_create_folders'): - scn.rs_create_folders = False - - for ob in bpy.data.objects: - if ob.type == 'CAMERA': - if is_new_object(ob): - ob["rs_shot_include"] = True - ob["rs_shot_start"] = scn.frame_start - ob["rs_shot_end"] = scn.frame_end - ob["rs_shot_output"] = "" - ob["rs_toggle_panel"] = True - ob["rs_settings_use"] = False - ob["rs_resolution_x"] = scn.render.resolution_x - ob["rs_resolution_y"] = scn.render.resolution_y - ob["rs_cycles_samples"] = 10 - ob["rs_shotshape_use"] = False - ob["rs_shotshape_shape"] = "" - ob["rs_shotshape_nav"] = 0 - ob["rs_shotshape_nav_start"] = False - ob["rs_shotshape_offset"] = 1 - ob["rs_shotshape_use_frames"] = False - else: - ob["rs_shot_include"] - ob["rs_shot_start"] - ob["rs_shot_end"] - ob["rs_shot_output"] - ob["rs_toggle_panel"] - ob["rs_settings_use"] - ob["rs_resolution_x"] - ob["rs_resolution_y"] - ob["rs_cycles_samples"] - ob["rs_shotshape_use"] - ob["rs_shotshape_shape"] - ob["rs_shotshape_nav"] - ob["rs_shotshape_nav_start"] - ob["rs_shotshape_offset"] - ob["rs_shotshape_use_frames"] - - scn.rs_is_updating = False - - -##################################### -# Initialisation -##################################### -def init_props(): - object = bpy.types.Object - scene = bpy.types.Scene - - # Camera properties - object.rs_shot_include = BoolProperty(name="", - description="Include this shot during render", default=True) - - object.rs_shot_start = IntProperty(name="Start", - description="First frame in this shot", - default=0, min=0, max=300000) - - object.rs_shot_end = IntProperty(name="End", - description="Last frame in this shot", - default=0, min=0, max=300000) - - object.rs_shot_output = StringProperty(name="", - description="Directory/name to save to", subtype='DIR_PATH') - - object.rs_toggle_panel = BoolProperty(name="", - description="Show/hide options for this shot", default=True) - - # Render settings - object.rs_settings_use = BoolProperty(name = "", default=False, - description = "Use specific render settings for this shot") - - object.rs_resolution_x = IntProperty(name="X", - description="Number of horizontal pixels in the rendered image", - default=2000, min=4, max=10000) - - object.rs_resolution_y = IntProperty(name="Y", - description = "Number of vertical pixels in the rendered image", - default=2000, min=4, max=10000) - - object.rs_cycles_samples = IntProperty(name="Samples", - description = "Number of samples to render for each pixel", - default=10, min=1, max=2147483647) - - # Shot shapes - object.rs_shotshape_use = BoolProperty(name="", default=False, - description="Use a shape to set a series of shots for this camera") - - object.rs_shotshape_shape = StringProperty(name="Shape:", - description="Select an object") - - object.rs_shotshape_nav = IntProperty(name="Navigate", - description="Navigate through this shape's vertices (0 = first vertex)", - default=0, update=shape_nav) - - object.rs_shotshape_nav_start = BoolProperty(name="Start from here", - default=False, - description="Start from this vertex (skips previous vertices)") - - object.rs_shotshape_offset = IntProperty(name="Offset", - description="Offset between frames (defines animation length)", - default=1, min=1, max=200) - - object.rs_shotshape_use_frames = BoolProperty(name="Use frame range", - description="Use the shot's frame range instead of the object's vertex"\ - " count", default=False) - - # Internal - scene.rs_is_updating = BoolProperty(name="", description="", default=False) - - scene.rs_create_folders = BoolProperty(name="", description="", default=False) - - scene.rs_main_folder = StringProperty(name="Main Folder", - subtype='DIR_PATH', default="", - description="Main folder in which to create the sub folders") - - scene.rs_overwrite_folders = BoolProperty(name="Overwrite", default=False, - description="Overwrite existing folders (this will delete all"\ - " files inside any existing folders)") - - - -##################################### -# Operators and Functions -##################################### -RENDER_DONE = True -RENDER_SETTINGS_HELP = False -TIMELINE = {"start": 1, "end": 250, "current": 1} -RENDER_SETTINGS = {"cycles_samples": 10, "res_x": 1920, "res_y": 1080} - - -@persistent -def render_finished(unused): - global RENDER_DONE - RENDER_DONE = True - - -def using_cycles(scn): - return True if scn.render.engine == 'CYCLES' else False - - -def timeline_handler(scn, mode): - global TIMELINE - - if mode == 'GET': - TIMELINE["start"] = scn.frame_start - TIMELINE["end"] = scn.frame_end - TIMELINE["current"] = scn.frame_current - - elif mode == 'SET': - scn.frame_start = TIMELINE["start"] - scn.frame_end = TIMELINE["end"] - scn.frame_current = TIMELINE["current"] - - -def render_settings_handler(scn, mode, cycles_on, ob): - global RENDER_SETTINGS - - if mode == 'GET': - RENDER_SETTINGS["cycles_samples"] = scn.cycles.samples - RENDER_SETTINGS["res_x"] = scn.render.resolution_x - RENDER_SETTINGS["res_y"] = scn.render.resolution_y - - elif mode == 'SET': - if cycles_on: - scn.cycles.samples = ob["rs_cycles_samples"] - scn.render.resolution_x = ob["rs_resolution_x"] - scn.render.resolution_y = ob["rs_resolution_y"] - - elif mode == 'REVERT': - if cycles_on: - scn.cycles.samples = RENDER_SETTINGS["cycles_samples"] - scn.render.resolution_x = RENDER_SETTINGS["res_x"] - scn.render.resolution_y = RENDER_SETTINGS["res_y"] - - -def frames_from_verts(ob, end, shape, mode): - start = ob.rs_shot_start - frame_range = (end - start)+1 - verts = len(shape.data.vertices) - - if frame_range % verts != 0: - end += 1 - return create_frames_from_verts(ob, end, shape, mode) - else: - if mode == 'OFFSET': - return frame_range / verts - elif mode == 'END': - return end - - -def keyframes_handler(scn, ob, shape, mode): - bpy.ops.object.select_all(action='DESELECT') - ob.select_set(True) - - start = ob.rs_shotshape_nav if ob.rs_shotshape_nav_start else 0 - - if ob.rs_shotshape_use_frames and shape is not None: - firstframe = ob.rs_shot_start - offset = frames_from_verts(ob, ob.rs_shot_end, shape, 'OFFSET') - else: - firstframe = 1 - offset = ob.rs_shotshape_offset - - if mode == 'SET': - scn.frame_current = firstframe - for vert in shape.data.vertices: - if vert.index >= start: - ob.location = vert.co - bpy.ops.anim.keyframe_insert_menu(type='Location') - scn.frame_current += offset - return (len(shape.data.vertices) - start) * offset - - elif mode == 'WIPE': - ob.animation_data_clear() - - -class RENDER_OT_RenderShots_create_folders(bpy.types.Operator): - ''' Create the output folders for all cameras ''' - bl_idname = "render.rendershots_create_folders" - bl_label = "Create Folders" - - mode = IntProperty() - - def execute(self, context): - scn = context.scene - - if self.mode == 1: # Display options - scn.rs_create_folders = True - - elif self.mode == 2: # Create folders - if scn.rs_main_folder != "" and not scn.rs_main_folder.isspace(): - for ob in bpy.data.objects: - if ob.type == 'CAMERA' and not is_new_object(ob): - # Name cleaning - if "." in ob.name: - name = ob.name.split(".") - camName = name[0]+name[1] - else: - camName = ob.name - - mainFolder = scn.rs_main_folder - destination = os.path.join(mainFolder, camName) - - # Folder creation - if scn.rs_overwrite_folders: - if os.path.isdir(destination): - shutil.rmtree(destination) - - os.mkdir(destination) - ob.rs_shot_output = destination+"\\" - else: - if not os.path.isdir(destination): - ob.rs_shot_output = destination+"\\" - - os.makedirs(destination_path) - self.report({'INFO'}, "Output folders created") - scn.rs_overwrite_folders = False - scn.rs_create_folders = False - else: - self.report({'ERROR'}, "No main folder selected") - - elif self.mode == 3: # Cancelled - scn.rs_overwrite_folders = False - scn.rs_create_folders = False - - return {'FINISHED'} - - -class RENDER_OT_RenderShots_settingshelp(bpy.types.Operator): - ''' \ - Edit the resolutions and see the changes in 3D View ('ESC' to finish)\ - ''' - bl_idname = "render.rendershots_settingshelp" - bl_label = "Render Settings Help" - - cam = StringProperty() - - def execute(self, context): - global RENDER_SETTINGS_HELP - RENDER_SETTINGS_HELP = True - - scn = context.scene - - render_settings_handler(scn, 'GET', using_cycles(scn), None) - context.window_manager.modal_handler_add(self) - return {'RUNNING_MODAL'} - - def modal(self, context, event): - scn = context.scene - ob = bpy.data.objects[self.cam] - - if event.type in {'ESC'}: - global RENDER_SETTINGS_HELP - RENDER_SETTINGS_HELP = False - render_settings_handler(scn, 'REVERT', using_cycles(scn), None) - return {'FINISHED'} - - scn.render.resolution_x = ob["rs_resolution_x"] - scn.render.resolution_y = ob["rs_resolution_y"] - - return {'PASS_THROUGH'} - - -class RENDER_OT_RenderShots_constraints_add(bpy.types.Operator): - ''' Add the tracking constraints and Empty for this camera ''' - bl_idname = "render.rendershots_constraints_add" - bl_label = "Create Constraints" - - cam: StringProperty() - - def execute(self, context): - ob = bpy.data.objects[self.cam] - ssName = "LookAt_for_"+ob.name - - bpy.ops.object.add(type="EMPTY") - context.active_object.name = ssName - - target = bpy.data.objects[ssName] - - ob.constraints.new(type="DAMPED_TRACK").name="SS_Damped" - damped_track = ob.constraints["SS_Damped"] - damped_track.target = target - damped_track.track_axis = 'TRACK_NEGATIVE_Z' - damped_track.influence = 0.994 - - ob.constraints.new(type="LOCKED_TRACK").name="SS_Locked" - locked_track = ob.constraints["SS_Locked"] - locked_track.target = target - locked_track.track_axis = 'TRACK_Y' - locked_track.lock_axis = 'LOCK_Z' - locked_track.influence = 1.0 - - return {'FINISHED'} - - -class RENTER_OT_rendershots_refresh(bpy.types.Operator): - ''' Adds newly created cameras to the list ''' - bl_idname = "render.rendershots_refresh" - bl_label = "Refresh" - - def execute(self, context): - update_shot_list(context.scene) - return {'FINISHED'} - - -class RENDER_OT_RenderShots_render(bpy.types.Operator): - ''' Render shots ''' - bl_idname = "render.rendershots_render" - bl_label = "Render" - - animation: BoolProperty(default=False) - _timer = None - _usingShape = False - - def execute(self, context): - global RENDER_DONE - RENDER_DONE = True - - scn = context.scene - self.camList = [] - self.vertTrack = -1 - self.cam = "" - - for ob in bpy.data.objects: - if ob.type == 'CAMERA' and not is_new_object(ob): - if ob["rs_shot_include"]: - output = ob["rs_shot_output"] - - addToList = False - - if output != "" and not output.isspace(): - addToList = True - else: - message = "\"%s\" has no output destination" % ob.name - self.report({'WARNING'}, message) - - if ob["rs_shotshape_use"]: - shotShape = ob["rs_shotshape_shape"] - if shotShape == "": - addToList = False - self.report({'WARNING'}, - "\"%s\" has no shot shape" % ob.name) - elif bpy.data.objects[shotShape].type != 'MESH': - errObj = bpy.data.objects[shotShape].name - addToList = False - self.report({'ERROR'}, - "\"%s\" is not a mesh object" % errObj) - #else: - # bpy.data.objects[shotShape].hide_render = True - if addToList: - self.camList.append(ob.name) - - self.camList.reverse() - timeline_handler(scn, 'GET') - render_settings_handler(scn, 'GET', using_cycles(scn), None) - context.window_manager.modal_handler_add(self) - self._timer = context.window_manager.event_timer_add(3, context.window) - return {'RUNNING_MODAL'} - - - def modal(self, context, event): - global RENDER_DONE - - scn = context.scene - - if event.type in {'ESC'}: - context.window_manager.event_timer_remove(self._timer) - keyframes_handler(scn, bpy.data.objects[self.cam], None, 'WIPE') - render_settings_handler(scn, 'REVERT', using_cycles(scn), None) - timeline_handler(scn, 'SET') - return {'CANCELLED'} - - if RENDER_DONE and self.camList: - RENDER_DONE = False - objs = bpy.data.objects - range = 0 - - if self._usingShape: - keyframes_handler(scn, objs[self.cam], None, 'WIPE') - - self._usingShape = False - - if not self._usingShape and self.camList: - self.cam = self.camList.pop() - - ob = objs[self.cam] - - # Output and name cleaning - scn.camera = ob - output = ob["rs_shot_output"] - - if output[-1] == "/" or output[-1] == "\\": - if "." in self.cam: - camName = self.cam.split(".") - output += camName[0]+camName[1] - else: - output += self.cam - - # Shot shapes - if ob["rs_shotshape_use"]: - self._usingShape = True - shape = ob["rs_shotshape_shape"] - range = keyframes_handler(scn, ob, objs[shape], 'SET') - - # Render settings - if ob["rs_settings_use"]: - render_settings_handler(scn, 'SET', using_cycles(scn), ob) - else: - render_settings_handler(scn, 'REVERT', using_cycles(scn), None) - - context.scene.render.filepath = output - - # Render - ssUsing = ob["rs_shotshape_use"] - if self.animation and not ssUsing and not self._usingShape: - scn.frame_start = ob["rs_shot_start"] - scn.frame_end = ob["rs_shot_end"] - bpy.ops.render.render('INVOKE_DEFAULT', animation=True) - - elif self.animation and ssUsing and self._usingShape: - if ob["rs_shotshape_use_frames"]: - scn.frame_start = ob.rs_shot_start - scn.frame_end = frames_from_verts(ob, ob.rs_shot_end, - objs[shape], 'END') - else: - scn.frame_start = 1 - scn.frame_end = range - bpy.ops.render.render('INVOKE_DEFAULT', animation=True) - - elif not self.animation and not ssUsing and not self._usingShape: - bpy.ops.render.render('INVOKE_DEFAULT', write_still=True) - - elif RENDER_DONE and not self.camList: - context.window_manager.event_timer_remove(self._timer) - keyframes_handler(scn, bpy.data.objects[self.cam], None, 'WIPE') - render_settings_handler(scn, 'REVERT', using_cycles(scn), None) - timeline_handler(scn, 'SET') - return {'FINISHED'} - - return {'PASS_THROUGH'} - - -class RENDER_OT_RenderShots_previewcamera(bpy.types.Operator): - ''' Preview this shot (makes this the active camera in 3D View) ''' - bl_idname = "render.rendershots_preview_camera" - bl_label = "Preview Camera" - - camera = bpy.props.StringProperty() - - def execute(self, context): - scn = context.scene - cam = bpy.data.objects[self.camera] - scn.objects.active = cam - scn.camera = cam - return {'FINISHED'} - - -##################################### -# UI -##################################### -class RENDER_PT_RenderShots(bpy.types.Panel): - bl_label = "Render Shots" - bl_space_type = "PROPERTIES" - bl_region_type = "WINDOW" - bl_context = "render" - - def draw(self, context): - global RENDER_SETTINGS_HELP - - layout = self.layout - scn = context.scene - - ANI_ICO, STILL_ICO = "RENDER_ANIMATION", "RENDER_STILL" - INCL_ICO = "RESTRICT_RENDER_OFF" - RENDER_OP = "render.rendershots_render" - - row = layout.row() - row.operator(RENDER_OP, text="Image", icon=STILL_ICO) - row.operator(RENDER_OP, text="Animation", icon=ANI_ICO).animation=True - - row = layout.row() - - if scn.rs_create_folders: - row.operator("render.rendershots_create_folders", - icon="FILE_TICK").mode=2 - else: - row.operator("render.rendershots_create_folders", - icon="NEWFOLDER").mode=1 - - row.operator("render.rendershots_refresh", icon="FILE_REFRESH") - - if scn.rs_create_folders: - row = layout.row() - col = row.column(align=True) - colrow = col.row() - colrow.label(text="Main Folder:") - colrow = col.row() - colrow.prop(scn, "rs_main_folder", text="") - colrow = col.row() - colrow.prop(scn, "rs_overwrite_folders") - colrow.operator("render.rendershots_create_folders", text="Cancel", - icon="X").mode=3 - - if not scn.rs_is_updating: - for ob in bpy.data.objects: - if ob.type == 'CAMERA' and not is_new_object(ob): - TOGL_ICO = "TRIA_DOWN" if ob["rs_toggle_panel"] else "TRIA_LEFT" - - box = layout.box() - box.active = ob["rs_shot_include"] - col = box.column() - row = col.row() - row.label(text="\""+ob.name+"\"") - row.operator("render.rendershots_preview_camera", text="", - icon="OUTLINER_OB_CAMERA").camera=ob.name - row.prop(ob, "rs_shotshape_use", icon="MESH_DATA") - row.prop(ob, "rs_settings_use", icon="SETTINGS") - row.prop(ob, "rs_shot_include", icon=INCL_ICO) - row.prop(ob, "rs_toggle_panel", icon=TOGL_ICO, emboss=False) - - if ob["rs_toggle_panel"]: - col.separator() - row = col.row() - rowbox = row.box() - col = rowbox.column() - - if ob["rs_shotshape_use"]: - row = col.row() - row.label(text="Shot Shape:") - row = col.row() - row.prop_search(ob, "rs_shotshape_shape", - scn, "objects", text="", - icon="OBJECT_DATA") - row = col.row(align=True) - row.prop(ob, "rs_shotshape_nav") - row.prop(ob, "rs_shotshape_nav_start") - row = col.row(align=True) - row.prop(ob, "rs_shotshape_offset") - row.prop(ob, "rs_shotshape_use_frames") - row = col.row() - row.operator("render.rendershots_constraints_add", - icon="CONSTRAINT_DATA").cam=ob.name - col.separator() - - if ob["rs_settings_use"]: - row = col.row() - row.label(text="Render Settings:") - row = col.row() - rowcol = row.column(align=True) - rowcol.prop(ob, "rs_resolution_x") - rowcol.prop(ob, "rs_resolution_y") - - rowcol = row.column() - if not RENDER_SETTINGS_HELP: - rowcol.operator("render.rendershots_settingshelp", - text="", icon="HELP").cam=ob.name - else: - rowcol.label(icon="TIME") - - if using_cycles(scn): - rowcol.prop(ob, "rs_cycles_samples") - else: - rowcol.label() - - col.separator() - - row = col.row() - row.label(text="Shot Settings:") - row = col.row(align=True) - row.prop(ob, "rs_shot_start") - row.prop(ob, "rs_shot_end") - row = col.row() - out = ob["rs_shot_output"] - row.alert = False if out != "" and not out.isspace() else True - row.prop(ob, "rs_shot_output") - - -def register(): - bpy.utils.register_module(__name__) - init_props() - bpy.app.handlers.render_complete.append(render_finished) - -def unregister(): - bpy.app.handlers.render_complete.remove(render_finished) - bpy.utils.unregister_module(__name__) - - object = bpy.types.Object - scene = bpy.types.Scene - - # Camera properties - del object.rs_shot_include - del object.rs_shot_start - del object.rs_shot_end - del object.rs_shot_output - del object.rs_toggle_panel - - # Render settings - del object.rs_settings_use - del object.rs_resolution_x - del object.rs_resolution_y - del object.rs_cycles_samples - - # Shot shapes - del object.rs_shotshape_use - del object.rs_shotshape_shape - del object.rs_shotshape_nav - del object.rs_shotshape_nav_start - del object.rs_shotshape_offset - del object.rs_shotshape_use_frames - - # Internal - del scene.rs_is_updating - del scene.rs_create_folders - del scene.rs_main_folder - del scene.rs_overwrite_folders - -if __name__ == '__main__': - register()