def getFilePath(self, name): asset = Project().get_asset(name) path = asset.get_filepath() path = os.path.join(path, Asset.RIG) self.element = asset.get_element(Asset.RIG) path = os.path.join(path, name) last_version = self.element.get_last_version() current_version = last_version + 1 path = path + "_v" + str(current_version).zfill(3) + ".mb" print(path) return path
def getFilePath(self, name): asset = Project().get_asset(name) path = asset.get_filepath() path = os.path.join(path, Asset.GEO) self.element = Element(path) self.element.update_app_ext(".obj") path = os.path.join(path, name) last_version = self.element.get_last_version() current_version = last_version + 1 path = path + "_v" + str(current_version).zfill(3) + ".obj" # print(path) return path
class Publisher: def __init__(self): print("starting publisher") self.body = None self.fileName = None self.element = None def export_usd_files(): # TODO: export usd from the whole asset node tree to the asset folder (same as .body) pass # command necessary for publish_asset dialog box to load correctly def set_new_version_button_command(self, node, version_window): """ :param node: hou.Node :param version_window: VersionWindow :return: None """ library_filepath = node.type().definition().libraryFilePath() name_base = '::'.join(node.type().name().split('::')[:-1]) new_version = '{major}.{minor}.{revision}'.format( major=version_window.major_version.value(), minor=version_window.minor_version.value(), revision=version_window.revision_version.textFromValue( version_window.revision_version.value())) new_name = '{name_base}::{new_version}'.format(name_base=name_base, new_version=new_version) # Here we can compare the current version to new version and detect if the new version is lower or equal if not StrictVersion(new_version) > StrictVersion( node.type().nameComponents()[-1]): hou.ui.displayMessage( 'The version number is the same as the current version - please increase number' ) return # Create a pop up to give the user a chance to confirm or cancel answer = hou.ui.displayMessage( 'Creating a new version for {node_name}\nNew Version - {new_version}' .format(node_name=node.type().name(), new_version=new_version), title='Setting New Version', buttons=['OK', 'Cancel']) # If answer 'OK', create new version and set to latest version if answer == 0: node.type().definition().copyToHDAFile(library_filepath, new_name) all_definitions = hou.hda.definitionsInFile(library_filepath) node.changeNodeType(all_definitions[-1].nodeTypeName()) # Close version window def publish_asset(self, solaris): ''' this is what's called when the button is pushed. ''' project = Project() asset_list = project.list_assets() self.item_gui = sfl.SelectFromList(l=asset_list, parent=hou.ui.mainQtWindow(), title="Select an asset to publish") # call asset results function here to be used once selected if solaris: self.item_gui.submitted.connect(self.solaris_asset_results) else: self.item_gui.submitted.connect(self.asset_results) def solaris_asset_results(self, value): self.asset_name = value[0] body = Project().get_asset(self.asset_name) if not body: body = Project().create_asset(self.asset_name) if not body: qd.error("Something broke :'(") return self.element = body.get_element(Asset.USD) self.path = os.path.join(self.element._filepath, "temp.usda") selection = hou.selectedNodes() if len(selection) != 1: qd.error( "Please select the last node in the network and try again.") return out = selection[0] rop = hou.node("/stage").createNode("usd_rop") rop.setInput(0, out) rop.parm("lopoutput").set(self.path) rop.parm("enableoutputprocessor_simplerelativepaths").set(0) rop.parm("execute").pressButton() def asset_comment(self, value): comment = value[0] username = Environment().get_user().get_username() self.element.publish(username, self.path, comment, self.asset_name) def asset_results(self, value): self.fileName = value[0] selection = hou.selectedNodes() # If it is more than one or none, abort if len(selection) != 1: qd.error( 'Please select a single Houdini Digital Asset node to save and update version on.' ) return # If it is not a Houdini Digital Asset, abort if not selection[0].type().definition(): qd.error( 'Please select a single Houdini Digital Asset node to save and update version on.' ) return #add code to force node name/node type name hda_node = selection[0] definition = hda_node.type().definition() libraryFilePath = definition.libraryFilePath() self.filepath = libraryFilePath '''current_full_name = hda_node.type().name() # last index is current version current_version_string = hda_node.type().nameComponents()[-1] #current_major = current_version_string.split('.')[0] #print('major version is ' + current_major) #current_minor = current_version_string.split('.')[1] # Set the 3 digit revision number to 0 if the HDA is only using the single float versioning (1.0 and not 1.0.005) current_revision = 0 if len(current_version_string.split( '.')) < 3 else current_version_string.split('.')[2] all_definitions = hou.hda.definitionsInFile(libraryFilePath) # This sets the node to the latest version of those stored hda_node.changeNodeType(all_definitions[-1].nodeTypeName())''' definition.updateFromNode(hda_node) self.body = Project().create_asset(self.fileName) if self.body is None: self.body = Project().get_asset(self.fileName) if self.body is None: print("Stephanie did an oopsie whoopsie :'(") return self.input = qd.HoudiniInput(parent=hou.qt.mainWindow(), title="Comment ") self.input.submitted.connect(self.comment_results) def comment_results(self, value): comment = str(value) path = os.path.join(self.body.get_filepath(), Asset.HDA) self.element = Element(path) username = Environment().get_user().get_username() name = self.fileName self.element.update_app_ext(".hda") self.element.publish(username, self.filepath, comment, name) # this bit will display the version info. Probably unneeded, as we'll be using element stuff instead. #f = open('/users/animation/martinje/Desktop/info.txt', 'r') #file_contents = f.read() # print(file_contents) # f.close() # qd.info(file_contents) # Instantiate the VersionWindow class - at this point, I would jump over to that code - keep in mind, we will # return a window object and afterwards we will set its initial state, using everything we just learned about # this HDA. #version_window = qd.VersionWindow(hou.qt.mainWindow()) #version_window.setWindowTitle('Versioning for: {hda_name}'.format(hda_name=current_full_name)) # version_window.current_version_label.setText(current_version_string) # version_window.current_path_label.setText(libraryFilePath) # Set value of the integer editor and set the editor to not go down from there # version_window.major_version.setValue(int(current_major)) # version_window.major_version.setMinimum(int(current_major)) # Set value of the integer editor and set the editor to not go down from there # version_window.minor_version.setValue(int(current_minor)) # version_window.minor_version.setMinimum(int(current_minor)) # Set value of the integer editor and set the editor to not go down from there # version_window.revision_version.setValue(int(current_revision)) # version_window.revision_version.setMinimum(int(current_revision)) # Connect the button signal to the set new version command and pass the hda node and the version window as arguments #version_window.set_version.clicked.connect(partial(self.set_new_version_button_command, hda_node, version_window)) # Show the window # version_window.show() def publish_tool(self): pass def publish_shot(self): ''' publishes shot (entire .hip file). Nothing needs to be selected for this to work. ''' scene = hou.hipFile.name() print('file name is ' + scene) project = Project() # get project and its shots asset_list = project.list_shots() self.item_gui = sfl.SelectFromList( l=asset_list, parent=hou.qt.mainWindow(), title="Select a shot to publish to, my friend.") self.item_gui.submitted.connect(self.shot_results) def shot_results(self, value): # chosen shot is the only asset in the list self.chosen_shot = value[0] self.comment = qd.HoudiniInput(parent=hou.qt.mainWindow(), title="Comments?") self.comment.submitted.connect(self.shot_comment) def shot_comment(self, value): # hou.hipFile.setName('newName_v01') #this will change the name of the file: can be versioned. So that's nice. comment = value if comment is None: comment = "Publish by " + \ str(user.get_username()) + \ '. Ask them to leave a comment next time lol' # FIXME: make variable more specific to shot? chosen_shot = self.chosen_shot project = Project() print('Selected shot name: ' + chosen_shot) # print(project.get_body(chosen_shot)) shot_body = project.create_shot(chosen_shot) if shot_body is None: shot_body = project.get_shot(chosen_shot) if shot_body is None: print("Something is horribly wrong. Talk to Stephanie") return filepath = shot_body.get_filepath() shot_element = shot_body.get_element("hip") shot_element.update_app_ext(".hip") prev_vers = shot_element.get_last_version() # path = os.path.abspath(inspect.getfile(project.get_body(chosen_shot))) #trying to get path here. filepath = os.path.join(shot_element._filepath, chosen_shot + ".hip") # print('file path is ', filepath) hou.hipFile.setName(filepath) src = hou.hipFile.save() # # hou.hipFile.saveAndIncrementFileName() #this actually works! Dunno if I want to use it though. # # #Publish user = Environment().get_user() pipeline_io.set_permissions(src) # try that asset name is body name. dst = self.publish_element(shot_element, user, filepath, comment) #pipeline_io.set_permissions(dst) # # message = "Successfully published " + str(self.body.get_name()) + "!" # self.print_success_message(message) def publish_element(self, element, user, src, comment="None"): print('username is', user.get_username()) dst = element.publish(user.get_username(), src, comment, self.chosen_shot) # Ensure file has correct permissions # Permissions are actually taken care of as part of the element.publish() function, so this isn't necessary '''try: os.chmod(dst, 0660) except Exception as e: print("Error setting file permissions: " + str(e))''' return dst def publish_layout(self): if len(hou.selectedNodes()) != 1: qd.error("Select only the last node in the network") return layout_list = Project().list_layouts() self.item_gui = sfl.SelectFromList( l=layout_list, parent=hou.qt.mainWindow(), title="Which layout are you publishing?") self.item_gui.submitted.connect(self.layout_results) def layout_results(self, value): self.layout_name = value[0] self.layout = Project().get_layout(self.layout_name) if self.layout is None: self.layout = Project().create_layout(self.layout_name) if self.layout is None: qd.error("Stephanie done messed up") return self.element = self.layout.get_element(Asset.LAYOUT) # make a USD ROP node rop = hou.node("/stage").createNode("usd_rop") # connect the selected node to the ROP out = hou.selectedNodes()[0] rop.setInput(0, out) # set the necessary values in the ROP self.savePath = os.path.join(self.element._filepath, self.layout_name + ".usda") rop.parm("lopoutput").set(self.savePath) rop.parm("enableoutputprocessor_simplerelativepaths").set(0) # save to disk rop.parm("execute").pressButton() #add attributes to stage reqired for proper unpacking later self.create_attributes(out) # publish :) self.comment = qd.HoudiniInput(parent=hou.qt.mainWindow(), title="Comment for publish?") self.comment.submitted.connect(self.layout_comment) def create_attributes(self, node): stage = Usd.Stage.Open(self.savePath) node_list = [] node_list = self.listInputs(node, node_list) for n in node_list: #create a ref_path attribute for each reference in the stage '''if n.type().name() == "reference": prim = stage.GetPrimAtPath(n.parm("primpath").eval()) if prim.IsValid(): path = prim.CreateAttribute("ref_path", Sdf.ValueTypeNames.String) path.Set(n.parm("filepath1").eval()) else: print("uh oh")''' #create a hda_path attribute for each material in the stage if n.type().name() == "materiallibrary": mats = n.children() for mat in mats: if mat.type().definition(): print(mat.type().definition().libraryFilePath()) prim = stage.GetPrimAtPath( n.parm("matpathprefix").eval() + mat.name()) if prim.IsValid(): path = prim.CreateAttribute( "hda_path", Sdf.ValueTypeNames.String) path.Set(mat.type().definition().libraryFilePath()) else: print("hmmmmmmmqow;ugh") else: #send error message qd.error( "Material " + mat.name() + " is not an HDA and won't load properly in the scene." ) stage.Save() def listInputs(self, n, list): list.append(n) inputs = n.inputs() for input in inputs: if input is not None: blank = [] list.extend(self.listInputs(input, blank)) return list def layout_comment(self, value): comment = value[0] username = Environment().get_user().get_username() self.element.update_app_ext(".usda") self.element.publish(username, self.savePath, comment, self.layout_name) if self.element.get_last_version() == 0: # if it is the first publish, we have to make the referencing file as well # create a reference node ref = hou.node("/stage").createNode("reference") # set the values to reference the main publish file, etc. ref.parm("primpath").set("/layout") ref.parm("filepath1").set(self.element.get_last_publish()[3]) # create a USD ROP node and connect it to the ref node refrop = hou.node("/stage").createNode("usd_rop") refrop.setInput(0, ref) # set the values in the ROP and save to disk alongside the main publish refrop.parm("lopoutput").set( os.path.join(self.element._filepath, self.layout_name + "_ref.usda")) refrop.parm("enableoutputprocessor_simplerelativepaths").set(0) refrop.parm("execute").pressButton() # this only needs to be done once since with every new publish, the file being referenced gets updated ref.destroy() refrop.destroy()
class AlembicExporter: def __init__(self, frame_range=1): self.frame_range = frame_range pm.loadPlugin("AbcExport") def asset_results(self, value): chosen_asset = value[0] print(chosen_asset) def exportSelected(self, asset_name, shot_name, camera=False): self.shot_name = shot_name self.shot = Project().get_shot(shot_name) if self.shot is None: return None start_frame = 0 frame_range = str(self.shot.get_frame_range()) #This means there's been no cameras even published for this shot but they're trying to save an animation. shouldn't ever happen if int(frame_range) == 0 and not camera: qd.error("How are you publishing without a camera yet? Get that done, bestie.") return #if this is previs we need to know where to move the keyframes to if camera: #ask the artist what frame the shot starts at and make sure that's a valid input start_frame = qd.input("Enter the starting frame for this shot") if start_frame is None or start_frame == u'': qd.error("Invalid frame range. Try publishing again.") return start_frame = str(start_frame) if not start_frame.isdigit(): qd.error("Invalid frame range. Try publishing again.") return #ask the artist for the last frame and make sure that's valid input #we'll do this every time in case the length of the shot changes end_frame = qd.input("Enter the last frame for this shot") if end_frame is None or end_frame == u'': qd.error("Invalid frame range. Try publishing again.") return end_frame = str(end_frame) if not end_frame.isdigit(): qd.error("Invalid frame range. Try publishing again.") return #calculate the frame-range and save it to the shot's .body file frame_range = int(end_frame) - int(start_frame) + 1 self.shot.set_frame_range(frame_range) #now move the keyframes and build the command timeChange = int(start_frame) - 1 anim_curves = mc.ls(type=['animCurveTA', 'animCurveTL', 'animCurveTT', 'animCurveTU']) for each in anim_curves: mc.keyframe(each, edit=True, relative=True, timeChange=-timeChange) path = self.getCameraPath(asset_name) command = self.buildAlembicCommand(path, uv=False) else: path = self.getFilePath(asset_name) command = self.buildAlembicCommand(path) cont = qd.yes_or_no("This asset is about to be saved with " + str(frame_range) + " frames. Is that correct?") if not cont: qd.error("Nothing was published.") return pm.Mel.eval(command) #now move the keyframes back for previs if camera: timeChange = int(start_frame) - 1 anim_curves = mc.ls(type=['animCurveTA', 'animCurveTL', 'animCurveTT', 'animCurveTU']) for each in anim_curves: mc.keyframe(each, edit=True, relative=True, timeChange=timeChange) publish_info = [self.element, path] return publish_info def buildAlembicCommand(self, path, uv=True): #get selected nodes selected = mc.ls(sl=True,long=True) nodes = "" for node in selected: nodes += node nodes += " " frame_range = str(self.shot.get_frame_range()) options = "-frameRange -48 " + frame_range if uv: options += " -uvWrite -writeUVSets" options += " -worldSpace -dataFormat ogawa -root " + nodes + "-file " + path command = "AbcExport -verbose -j \"" + options +"\"" print(command) return command def getFilePath(self, asset_name): path = self.shot.get_filepath() dept = os.path.join(Asset.ANIMATION, asset_name) #this is to make it so we can keep track of the versions of animations for each asset path = os.path.join(path, dept) #try to create the element if it doesn't exist self.element = self.shot.create_element(dept, Element.DEFAULT_NAME) #retrieve it if it does already if self.element is None: self.element = self.shot.get_element(dept) self.element.update_app_ext(".abc") path = os.path.join(path, asset_name) last_version = self.element.get_last_version() current_version = last_version + 1 path = path + "_v" + str(current_version).zfill(3) + ".abc" print(path) return path def getCameraPath(self, camera_name): path = self.shot.get_filepath() dept = os.path.join(Asset.CAMERA, camera_name) #this is to make it so we can keep track of the versions of animations for each camera path = os.path.join(path, dept) #try to create the element if it doesn't exist self.element = self.shot.create_element(dept, Element.DEFAULT_NAME) #retrieve it if it does already if self.element is None: self.element = self.shot.get_element(dept) self.element.update_app_ext(".abc") path = os.path.join(path, camera_name) last_version = self.element.get_last_version() current_version = last_version + 1 path = path + "_v" + str(current_version).zfill(3) + ".abc" print(path) return path