def _setup_controls(self): # ---- verison node version_note_lbl = QtGui.QLabel("Version description:") self._version_note_edit = QtGui.QLineEdit() # ---- write node write_nodes = [node for node in self.session.nuke.allNodes( filter='Write') if node.knob('product_name')] if not write_nodes: raise DarkKnightError("No WriteProduct nodes to render.") try: default_node = self.session.nuke.selectedNode() except: default_node = write_nodes[0] write_node_lbl = QtGui.QLabel('Rendering:') self._write_node_select = QtGui.QComboBox() default_index = 0 for (i, node) in enumerate(write_nodes): node_name = node.name() node_disp = "{pn} ({nn})".format( pn=node['product_name'].value(), nn=node_name) self._write_node_select.addItem(node_disp, node_name) if node_name == default_node.name(): default_index = i self._write_node_select.setCurrentIndex(default_index) # ---- frame range min_time = self.session.nuke.root().firstFrame() max_time = self.session.nuke.root().lastFrame() start_time = min_time end_time = max_time frange_lbl = QtGui.QLabel("Frame range:") self._make_frame_range_controls( min_time, max_time, start_time, end_time) controls_layout = QtGui.QGridLayout() # ---- queue render_queue_lbl = QtGui.QLabel("Render queue:") self._render_queues = QtGui.QComboBox() self._render_queues.addItems(self.__class__.RENDER_QUEUES) # ---- debug debug_lbl = QtGui.QLabel("Debug mode:") self._debug = QtGui.QCheckBox("") # ---- layout the controls controls_layout.addWidget(version_note_lbl, 0, 0, QtCore.Qt.AlignRight) controls_layout.addWidget(self._version_note_edit, 0, 1) controls_layout.addWidget(write_node_lbl, 1, 0, QtCore.Qt.AlignRight) controls_layout.addWidget(self._write_node_select, 1, 1) controls_layout.addWidget(frange_lbl, 2, 0, QtCore.Qt.AlignRight) controls_layout.addWidget(self._frange_stack, 2, 1, QtCore.Qt.AlignLeft) controls_layout.addWidget(self._frange_btn, 2, 2, QtCore.Qt.AlignLeft) controls_layout.addWidget(render_queue_lbl, 3, 0, QtCore.Qt.AlignRight) controls_layout.addWidget(self._render_queues, 3, 1, QtCore.Qt.AlignLeft) controls_layout.addWidget(debug_lbl, 4, 0, QtCore.Qt.AlignRight) controls_layout.addWidget(self._debug, 4, 1, QtCore.Qt.AlignLeft) controls_layout.setColumnStretch(2, 1000) controls_vbox = QtGui.QVBoxLayout() controls_vbox.addLayout(controls_layout) controls_vbox.addStretch() controls_widget = QtGui.QWidget() controls_widget.setLayout(controls_vbox) return controls_widget
def _product_render_arnold(self): # get timestamp for all the tasks being submitted now = datetime.datetime.now() render_layers = self._get_render_layers() # figure out the total number of operations to perform for the progress num_ops = 1 + len(render_layers) * len(self._frame_list) # layer > frame num_ops += len(self._frame_list) # frame submission if self._remove_scenes: num_ops += 1 if self._generate_scenes: num_ops += 1 cur_op = 0 progress_dialog = QtGui.QProgressDialog( "Product render...", "", cur_op, num_ops, self) progress_dialog.setWindowTitle("Dark Knight is busy...") progress_dialog.setAutoReset(False) progress_dialog.setLabelText("Preparing maya file for rendering...") progress_dialog.show() ptask = self._cur_ptask ptask_version = self._cur_ptask.version(self._version) ptask_dir = self._cur_ptask.area.dir() ver_dir = ptask.area.dir(version=self._version) # need to get the maya file in the version directory maya_file = self.session.cmds.file(q=True, sceneName=True) maya_file = maya_file.replace(ptask_dir, ver_dir) file_base = os.path.splitext(os.path.split(maya_file)[1])[0] self.session.cmds.setAttr('defaultResolution.width', self._resolution.width) self.session.cmds.setAttr('defaultResolution.height', self._resolution.height) # set the output file naming convention to name.#.ext self.session.cmds.setAttr("defaultRenderGlobals.animation", True) self.session.cmds.setAttr("defaultRenderGlobals.putFrameBeforeExt", True) self.session.cmds.setAttr("defaultRenderGlobals.outFormatControl", False) # set all other cameras to not be renderable (this seems to work) cam_shape_list = self.session.cmds.ls(cameras=True) for cam_shape in cam_shape_list: cam_name = str( self.session.cmds.listRelatives(cam_shape, parent=True)[0]) if cam_name == self._camera: self.session.cmds.setAttr(cam_shape + ".renderable", 1) else: self.session.cmds.setAttr(cam_shape + ".renderable", 0) # ---- sync current work area to version snapshot to render from cur_project = self.session.cmds.workspace(query=True, rootDirectory=True) ver_project = cur_project.replace(ptask_dir, ver_dir) progress_dialog.setLabelText("Sync'ing work to current version...") try: self.session.save() self._sync_latest() except Exception as e: self._show_error("Unable to save & sync the latest work: " + str(e)) self.setEnabled(True) progress_dialog.close() return cur_op += 1 progress_dialog.setValue(cur_op) create_action_cls = ActionRegistry().get_action('create', 'product') if not create_action_cls: progress_dialog.close() raise DarkKnightError("Unable to find product creation action.") # ---- clean up ASSs scene_dir = os.path.join(ver_project, 'arnold', file_base, 'ass') if self._remove_scenes: progress_dialog.setLabelText("Removing ass files...") if os.path.isdir(scene_dir): try: shutil.rmtree(scene_dir) except Exception as e: progress_dialog.close() raise DarkKnightError("Unable to clean up ass files: " + str(e)) cur_op += 1 progress_dialog.setValue(cur_op) # ---- get a list of warnings to ignore #prman_config = ptask.area.config(PRMAN_CONFIG_PATH, # composite_ancestors=True, composite_method="override") #prman_warnings = " ".join( # ["-woff " + w for w in prman_config.get('woff', [])]) # ---- construct scripts for the queue render_summary = [] for render_layer in render_layers: progress_dialog.setLabelText( "Creating product for layer: {rl}...".format(rl=render_layer)) # ensure product exists for each render layer create_action = create_action_cls( product=render_layer, ptask=ptask_version.ptask_spec, version=ptask_version.number, category='imgseq', description=render_layer + " render layer", file_type=self._file_type, resolution=self._res_str, note=self._version_note, ) try: create_action() except ActionError as e: progress_dialog.close() raise DarkKnightError("Unable to create product: " + str(e)) product_repr = create_action.product_repr product_repr_area = product_repr.area progress_dialog.setLabelText( "Provisioning 'queue' directory in product...") # make sure queue directory exists try: product_repr_area.provision('queue') except Exception as e: progress_dialog.close() raise DarkKnightError( "Unable to create queue scripts directory: " + str(e)) queue_dir = product_repr_area.dir(dir_name='queue') tasks_info_file = os.path.join(queue_dir, 'tasks_info.cfg') tasks_info_config = Config() # dpaset command to run dpaset_cmd = 'eval "`dpa env ptask {pt}@{vn}`"'.format( pt=ptask.spec, vn=ptask_version.number) # set group permissions on project dir, recursively os.system("chmod g+rw {pd} -R".format(pd=ver_project)) # figure out the render layer if render_layer == 'masterLayer': layer_index = self.session.cmds.getAttr("defaultRenderLayer.rlid") else: layer_index = self.session.cmds.getAttr(render_layer + ".rlid") frame_scripts = [] for frame in self._frame_list: frame_padded = str(frame).zfill(4) progress_dialog.setLabelText( "Building render shell script for {rl} frame {f}".format( rl=render_layer, f=frame_padded)) script_path = os.path.join(queue_dir, "{rl}.{fn}.sh".format(rl=render_layer, fn=frame_padded)) out_dir = product_repr_area.dir() out_file = os.path.join(out_dir, "{rl}.{fn}.{ft}".\ format(rl=render_layer, fn=frame_padded, ft=self._file_type)) simple_scene = "{proj}arnold/{fb}/ass/{fb}.{fn}.ass".format( proj=ver_project, fb=file_base, fn=frame_padded) layer_scene = "{proj}arnold/{fb}/ass/{fb}_{rl}.{fn}.ass".\ format(proj=ver_project, fb=file_base, fn=frame_padded, rl=render_layer) render_cmd = "/opt/solidangle/arnold-maya2016/bin/kick -dw -v 6 -i $ASS_PATH " render_cmd += "-l /opt/solidangle/arnold-maya2016/shaders " render_cmd += "-l /opt/solidangle/arnold-maya2016/procedurals " render_cmd += "-o {od} ".format(od=out_file) #render_cmd += "-f {rl} ".format(rl=render_layer) #render_cmd += "-p {proj} ".format(proj=ver_project) #render_cmd += "--prman '-t:0 -cwd \"{proj}\" {warn}' ".\dd # format(proj=ver_project, warn=prman_warnings) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n") # 'kick' command has to be added to $PATH # Create env variable for Arnold License server script_file.write("export [email protected]\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n") script_file.write("cd " + ver_project + "\n\n") # Add necessary paths to the environment for XGen script_file.write("export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/autodesk/maya2016/plug-ins/xgen/lib:/usr/autodesk/maya2016/lib:/opt/solidangle/arnold-maya2016/bin\n\n") # the logic for determining which ass will be generated is # unclear at this point. So we'll build a conditional script_file.write("if [[ -f {lr} ]]; then\n".format(lr=layer_scene)) script_file.write(" export ASS_PATH={lr}\n".format(lr=layer_scene)) script_file.write("else\n") script_file.write(" export ASS_PATH={sr}\n".format(sr=simple_scene)) script_file.write("fi\n") script_file.write("# render!\n") script_file.write(render_cmd + "\n\n") script_file.write("chmod 660 {of}\n\n".format( of=os.path.join(out_dir, render_layer + "*." + self._file_type))) os.chmod(script_path, 0770) frame_scripts.append((frame_padded, script_path, out_file)) cur_op += 1 progress_dialog.setValue(cur_op) frame_tasks = [] task_id_base = get_unique_id(product_repr_area.spec, dt=now) tasks_info_config.add('base_id', task_id_base) if self._generate_scenes: frame_queue = 'hold' else: frame_queue = self._render_queue # create frame tasks for (frame, frame_script, out_file) in frame_scripts: progress_dialog.setLabelText( "Submitting frame: " + frame_script) task_id = task_id_base + "_" + frame if not self._debug_mode: # create tasks, don't actually submit yet create_queue_task(frame_queue, frame_script, task_id, output_file=out_file, submit=False, log_path=frame_script + '.log') frame_tasks.append((frame, task_id)) # # resubmit frame-by-frame because # group submit seems to be occasionally # having problems. os.system("cqresubmittask {qn} {tid}".format( qn=frame_queue, tid=task_id)) cur_op += 1 progress_dialog.setValue(cur_op) frame_info = Config() for (frame, task_id) in frame_tasks: frame_info.add(str(frame), task_id) tasks_info_config.add('frame_ids', frame_info) # resubmit all at once (workaround for slow individual submissions) # # This approach seems to be having problems with the server # communications. Switch to frame-by-frame resubmit because # that has worked where this fails #os.system("cqresubmittask {qn} {tid}".format( # qn=frame_queue, tid=task_id_base)) if self._generate_scenes: progress_dialog.setLabelText("Creating ass generation script...") script_path = os.path.join(queue_dir, "{rl}_assgen.sh".format(rl=render_layer)) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n") script_file.write("cd " + ver_project + "\n\n") script_file.write("# generate the ass files...\n") current_render_layer = render_layer if render_layer == 'masterLayer': current_render_layer = "defaultRenderLayer" switch_render_layer_cmd = "editRenderLayerGlobals " switch_render_layer_cmd += "-currentRenderLayer \"{rl}\"".\ format(rl=current_render_layer) arnold_export_cmd = "arnoldExportAss -f \"{ad}/{fb}_{rl}.ass\" ".\ format(ad=scene_dir, fb=file_base, rl=render_layer) arnold_export_cmd += "-startFrame {sf} -endFrame {ef} -frameStep 1 ".\ format(li=layer_index, sf=self._frange.start, ef=self._frange.end) arnold_export_cmd += "-mask 255 -lightLinks 1 -shadowLinks 1 -cam {cam} ".\ format(cam=self._camera) arnold_export_cmd += "-expandProcedurals " maya_batch_cmd = 'maya2016 -batch -proj "{proj}" '.format( proj=ver_project) maya_batch_cmd += '-command \'{srlc}; {ar}\' '.\ format(srlc=switch_render_layer_cmd, ar=arnold_export_cmd) maya_batch_cmd += '-file "{mf}"'.format(mf=maya_file) script_file.write(maya_batch_cmd + "\n") script_file.write( "\n# make sure project dir has group permissions\n") script_file.write( "chmod g+rw {pd} -R\n\n".format(pd=ver_project)) # submit the frames to render script_file.write("# Submit frames after ass gen \n") for (frame, frame_task) in frame_tasks: script_file.write("cqmovetask {qn} {tid}\n".format( qn=self._render_queue, tid=frame_task)) # changed to move group #script_file.write("cqmovetask {qn} {tid}\n".format( #qn=self._render_queue, tid=task_id_base)) os.chmod(script_path, 0770) # submit the scenegen script progress_dialog.setLabelText( "Submitting ass gen: " + script_path) task_id = task_id_base + "_asses" tasks_info_config.add('assgen_id', task_id) if not self._debug_mode: create_queue_task(self._scenegen_queue, script_path, task_id, output_file=scene_dir, submit=True, log_path=script_path + '.log') cur_op += 1 progress_dialog.setValue(cur_op) cur_op += 1 progress_dialog.setValue(cur_op) progress_dialog.close() render_summary.append( (render_layer, task_id_base, product_repr, queue_dir)) # For now, disable wrangling tickets. bsddb is causing problems # - zshore, 2015-10-23 # if not self._debug_mode: # # ---- dpa specific queue stuff # from cheesyq import DPAWrangler # # create wrangling ticket # wrangle = DPAWrangler.WrangleRecord(task_id_base) # wrangle.frames = self._frame_list # db = DPAWrangler.GetWranglingDB() # db.set(wrangle.baseId, wrangle) # DPAWrangler.AssignWranglerTask("none", task_id_base) wranglecmd = 'cqcreatewrangleitem ' + task_id_base + ' ' for f in self._frame_list: wranglecmd = wranglecmd + str(f) + ' ' print wranglecmd os.system(wranglecmd) tasks_info_config.write(tasks_info_file) os.chmod(tasks_info_file, 0660) if not self._debug_mode: # send msg... msg_title = "Queue submission report: " + \ now.strftime("%Y/%m/%d %H:%M:%S") msg_body = "Submitted the following tasks for " + \ ptask.spec + ":\n\n" msg_body += " Description: " + self._version_note + "\n" msg_body += " Resolution: " + self._res_str + "\n" msg_body += " File type: " + self._file_type + "\n" msg_body += " Camera: " + self._camera + "\n" if self._generate_scenes: msg_body += " Ass gen queue: " + self._scenegen_queue + "\n" msg_body += " Render queue: " + self._render_queue + "\n" msg_body += " Frames: " + str(self._frange) + "\n" msg_body += " Ass directory: " + scene_dir + "\n" msg_body += "\n" for (layer, task_id_base, product_repr, queue_dir) in render_summary: msg_body += " Render layer: " + layer + "\n" msg_body += " Base task ID: " + task_id_base + "\n" msg_body += " Product representation: " + \ product_repr.spec + "\n" msg_body += " Scripts directory: " + queue_dir + "\n" msg_body += "\n" dk_config = ptask.area.config(DK_CONFIG_PATH, composite_ancestors=True, composite_method="append") recipients = dk_config.get('notify', []) recipients.append(current_username()) recipients = emails_from_unames(recipients) notification = Notification(msg_title, msg_body, recipients, sender=User.current().email) notification.send_email()
def _render_to_product(self): # add the version note for the product render_node = self.session.nuke.toNode(self._node_to_render) render_node['product_ver_note'].setValue(self._version_note) # ---- progress dialog num_ops = 6 cur_op = 0 progress_dialog = QtGui.QProgressDialog( "Product render...", "", cur_op, num_ops, self) progress_dialog.setWindowTitle("Dark Knight is busy...") progress_dialog.setAutoReset(False) progress_dialog.setLabelText("Preparing nuke file for rendering...") progress_dialog.show() # ensure the product has been created progress_dialog.setLabelText("Creating product...") product_repr = create_product_before_render(node=render_node) product_repr_area = product_repr.area cur_op += 1 progress_dialog.setValue(cur_op) # get timestamp for all the tasks being submitted now = datetime.datetime.now() ptask_area = PTaskArea.current() ptask = PTask.get(ptask_area.spec) if ptask_area.version: ptask_version = ptask.version(ptask_area.version) else: ptask_version = ptask.latest_version ptask_dir = ptask_area.dir() ver_dir = ptask_area.dir(version=ptask_version.number) nuke_file = self.session.nuke.root().name() nuke_file = nuke_file.replace(ptask_dir, ver_dir) file_base = os.path.splitext(os.path.split(nuke_file)[1])[0] # ---- sync current work area to version snapshot to render from progress_dialog.setLabelText("Sync'ing the latest work...") try: self.session.save() self._sync_latest() except Exception as e: self._show_error("Unable to save & sync the latest work: " + str(e)) self.setEnabled(True) progress_dialog.close() return cur_op += 1 progress_dialog.setValue(cur_op) # make sure queue directory exists progress_dialog.setLabelText("Provisioning the queue directory...") try: product_repr_area.provision('queue') except Exception as e: raise DarkKnightError( "Unable to create queue scripts directory: " + str(e)) cur_op += 1 progress_dialog.setValue(cur_op) queue_dir = product_repr_area.dir(dir_name='queue') tasks_info_file = os.path.join(queue_dir, 'tasks_info.cfg') tasks_info_config = Config() progress_dialog.setLabelText("Building the queue script...") # dpaset command to run dpaset_cmd = 'eval "`dpa env ptask {pt}@{vn}`"'.format( pt=ptask.spec, vn=ptask_version.number) frange_str = str(self._frange).replace("-", "_").replace(":", "_") script_path = os.path.join(queue_dir, "{pn}.{fr}.sh".format(pn=render_node['product_name'].value(), fr=frange_str)) render_cmd = "nuke --cont -f -F {fs}-{fe}x{step} -X {rn} -V 2 -x {nf}".\ format( fs=self._frange.start, fe=self._frange.end, step=self._frange.step, rn=self._node_to_render, nf=nuke_file, ) with open(script_path, "w") as script_file: script_file.write("#!/bin/bash\n\n") # XXX these should happen automatically in the queue... script_file.write("source /DPA/wookie/dpa/bash/startup.bash\n") script_file.write("pipeup\n\n") script_file.write("# set the ptask version to render\n") script_file.write(dpaset_cmd + "\n\n") script_file.write("# render!\n") script_file.write(render_cmd + "\n\n") os.chmod(script_path, 0770) cur_op += 1 progress_dialog.setValue(cur_op) task_id = get_unique_id(product_repr_area.spec, dt=now) task_id += "_" + frange_str tasks_info_config.add('task_id', task_id) out_file = self.session.nuke.filename(render_node, self.session.nuke.REPLACE) if not self._debug_mode: progress_dialog.setLabelText("Submitting to the queue...") create_queue_task(self._render_queue, script_path, task_id, output_file=out_file, submit=True, log_path=script_path + '.log') tasks_info_config.write(tasks_info_file) os.chmod(tasks_info_file, 0660) cur_op += 1 progress_dialog.setValue(cur_op) if not self._debug_mode: progress_dialog.setLabelText("Sending submission report...") # send msg... msg_title = "Queue submission report: " + \ now.strftime("%Y/%m/%d %H:%M:%S") msg_body = "Submitted the following task for " + \ ptask.spec + ":\n\n" msg_body += " Product representation: " + product_repr.spec + "\n" msg_body += " Description: " + self._version_note + "\n" msg_body += " Render queue: " + self._render_queue + "\n" msg_body += " Frames: " + str(self._frange) + "\n" msg_body += " Task ID: " + task_id + "\n" msg_body += " Scripts directory: " + queue_dir + "\n" msg_body += "\n" dk_config = ptask.area.config(DK_CONFIG_PATH, composite_ancestors=True, composite_method="append") recipients = dk_config.get('notify', []) recipients.append(current_username()) recipients = emails_from_unames(recipients) notification = Notification(msg_title, msg_body, recipients, sender=User.current().email) notification.send_email() cur_op += 1 progress_dialog.setValue(cur_op) progress_dialog.close()