# This script is available under the terms of the Bugroff license: # http://tunes.org/legalese/bugroff.html # # Authors: Matt Ebb import bpy import os import subprocess from bpy.props import PointerProperty, StringProperty, BoolProperty, EnumProperty, IntProperty, CollectionProperty bl_addon_info = { "name": "Render: Submit to Backburner", "author": "Matt Ebb", "version": "2.0 2010/08/05", "blender": (2, 5, 3), "category": "Render", "location": "Property Editor > Render > Backburner", "description": "Submit the .blend file for network rendering on Autodesk Backburner", "warning": '', # used for warning icon and text in addons panel "wiki_url": "http://wiki.blender.org/index.php/Extensions:2.5/Py/Scripts/My_Script", "tracker_url": "http://projects.blender.org/tracker/index.php?func=detail&aid=#&group_id=#&atid=#", "doc": """\ This allows network rendering .blend files on Autodesk Backburner. Quick setup In the Backburner panel: 1. Fill in the path to the blender executable that you wish to render with in 'Blender Path'. This must be accessible over the network with the same path, from all render nodes running Backburner Server. 2. Fill in the path to cmdjob.exe in your local Backburner installation This is the command line backburner job submitter which this script uses to submit jobs for rendering """} default_backburner_path = 'C:\\Program Files (x86)\\Autodesk\\Backburner\\cmdjob.exe' default_blender_path = '' class BackburnerSettings(bpy.types.IDPropertyGroup): pass # entry point for settings collection bpy.types.Scene.backburner = PointerProperty( type=BackburnerSettings, name='Backburner Submission', description='Backburner Submission Settings') # fill the new struct BackburnerSettings.job_name = StringProperty( name='Job Name', description='Name of the job to be shown in Backburner', maxlen=256, default='') BackburnerSettings.frames_per_task = IntProperty( name='Frames per Task', description='Number of frames to give each render node', min=1, max=1000, soft_min=1, soft_max=64, default=1) BackburnerSettings.timeout = IntProperty( name='Timeout', description='Timeout per task', min=1, max=1000, soft_min=1, soft_max=64, default=120) BackburnerSettings.priority = IntProperty(name='Priority', description='Priority of this job', min=1, max=1000, soft_min=1, soft_max=64, default=50) BackburnerSettings.override_frame_range = BoolProperty( name='Override Frame Range', description='Override scene start and end frames', default=False) BackburnerSettings.frame_start = IntProperty( name='Start Frame', description='Start frame of animation sequence to render', min=1, max=1000, soft_min=1, soft_max=64, default=1) BackburnerSettings.frame_end = IntProperty( name='End Frame', description='End frame of animation sequence to render', min=1, max=1000, soft_min=1, soft_max=64, default=250) BackburnerSettings.group = StringProperty( name='Group', description='Render this job only with the servers in this group', maxlen=400, default='Default') BackburnerSettings.servers = StringProperty( name='Servers', description='Render this job only with the servers specified (semi-colon separated list - ignored if group is used)', maxlen=400, default='') BackburnerSettings.path_backburner = StringProperty( name='Backburner Path', description='Path to Backburner cmdjob.exe', maxlen=400, default=default_backburner_path, subtype='FILE_PATH') BackburnerSettings.path_blender = StringProperty( name='Blender Path', description='Path to blender.exe', maxlen=400, default=default_blender_path, subtype='FILE_PATH') class RENDER_PT_Backburner(bpy.types.Panel): bl_space_type = 'PROPERTIES' bl_region_type = 'WINDOW' bl_context = "render" bl_label = 'Backburner' bl_default_closed = True def draw(self, context): layout = self.layout scene = context.scene bb = scene.backburner layout.operator('render.submit_to_backburner', icon='RENDER_ANIMATION') layout.separator() layout.prop(bb, 'job_name') layout.prop(bb, 'frames_per_task') row = layout.row() row.prop(bb, 'timeout') row.prop(bb, 'priority') if (bb.override_frame_range == False): bb.frame_start = scene.frame_start bb.frame_end = scene.frame_end layout.prop(bb, 'override_frame_range') row = layout.row() row.enabled = bb.override_frame_range row.prop(bb, 'frame_start') row.prop(bb, 'frame_end') layout.separator() layout.prop(bb, 'group') layout.prop(bb, 'servers') layout.prop(bb, 'path_backburner') layout.prop(bb, 'path_blender') def write_tasklist(step, sframe, eframe, filename): dir = os.path.dirname(filename) # if we need a unique ID? # id = str(time.time())[:-3] # tasklist_path = dir + '\\submit_temp_' + id + '.txt' tasklist_path = dir + '\\submit_temp.txt' try: file = open(tasklist_path, 'w') except: print("couldn't open " + tasklist_path + " for writing") # dodgy method of writing out the frame sequences since I'm too # tired to think of a fancy mathematical construct # ... it works though! curframe = sframe while(curframe <= eframe): seq_sta = curframe seq_end = curframe + (step-1) if seq_end > eframe: seq_end = eframe curframe = seq_end + 1 task = 'Frame ' + str(seq_sta) if seq_sta != seq_end: task += ' - ' + str(seq_end) task += ' ' task += str(seq_sta) + ' ' + str(seq_end) + '\n' file.write(task) file.close() return tasklist_path def submit(scene): bb = scene.backburner filename = bpy.data.filepath blenderdir = os.path.dirname(bb.path_blender) print('--- Submitting: ') description = 'Rendering_to_' + scene.render.filepath tasklist_path = write_tasklist(bb.frames_per_task, bb.frame_start, bb.frame_end, filename) cmd = '"' + bb.path_backburner + '"' cmd += ' -jobName:' + bb.job_name cmd += ' -jobNameAdjust' cmd += ' -description:' + description cmd += ' -priority:' + str(bb.priority) cmd += ' -timeout:' + str(bb.timeout) if bb.group != '': cmd += ' -group:' + bb.group if bb.servers != '': cmd += ' -servers:' + bb.servers cmd += ' -workPath:' + blenderdir cmd += ' -taskList:' + tasklist_path cmd += ' -taskName: 1' cmd += ' "' + bb.path_blender + '" --background ' cmd += filename cmd += ' --frame-start %tp2 --frame-end %tp3 --render-anim' print(cmd) result = subprocess.call(cmd) if result != 0: print('Submit Failed for: ' + filename) submit_status = "FAILED" else: print('Submit Suceeded for: ' + filename) submit_status = "OK" os.remove(tasklist_path) class SubmitToBackburner(bpy.types.Operator): '''''' bl_idname = "render.submit_to_backburner" bl_label = "Submit to Backburner" @classmethod def poll(cls, context): return context.scene != None def invoke(self, context, event): bb = context.scene.backburner if bb.path_blender == '': self.report({'ERROR'}, "Network path to Blender hasn't been set") return {'CANCELLED'} if bb.path_backburner == '': self.report({'ERROR'}, "Path to Backburner cmdjob.exe hasn't been set") return {'CANCELLED'} submit(context.scene) def execute(self, context): submit(context.scene) return {'FINISHED'}