def execute(self, operation, file_path, **kwargs): """ Main hook entry point :param operation: String Scene operation to perform :param file_path: String File path to use if the operation requires it (e.g. open) :returns: Depends on operation: 'current_path' - Return the current scene file path as a String all others - None """ fb_app = FBApplication() if operation == "current_path": # return the current scene path return fb_app.FBXFileName elif operation == "open": # do new scene as Maya doesn't like opening # the scene it currently has open! fb_app.FileOpen(file_path) elif operation == "save": # save the current scene: # Note - have to pass the current scene name to # avoid showing the save-as dialog fb_app.FileSave(fb_app.FBXFileName)
def _do_motionbuilder_post_publish(self, work_template, progress_cb): """ Do any Motion Builder post-publish work :param work_template: The primary work template used for the publish :param progress_cb: Callback to be used when reporting progress """ from pyfbsdk import FBApplication mb_app = FBApplication() progress_cb(0, "Versioning up the script") # get the current script path: original_path = mb_app.FBXFileName script_path = os.path.abspath(original_path) # increment version and construct new name: progress_cb(25, "Finding next version number") fields = work_template.get_fields(script_path) next_version = self._get_next_work_file_version(work_template, fields) fields["version"] = next_version new_path = work_template.apply_fields(fields) # log info self.parent.log_debug("Version up work file %s --> %s..." % (script_path, new_path)) # save the script: progress_cb(75, "Saving the scene file") mb_app.FileSave(new_path) progress_cb(100)
def _clear_current_scene_motionbuilder(self): """ Clears the current scene. Does a file -> new. Motionbuilder implementation. returns False on cancel, true on success. """ from pyfbsdk import FBApplication status = True fb_app = FBApplication() res = QtGui.QMessageBox.question( self, "Save your scene?", "Your scene has unsaved changes. Save before proceeding?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No | QtGui.QMessageBox.Cancel) if res == QtGui.QMessageBox.Cancel: status = False elif res == QtGui.QMessageBox.No: # don't save! fb_app.FileNew() else: # save before! fb_app.FileSave() fb_app.FileNew() return status
def _do_motionbuilder_publish(self, task, work_template, comment, thumbnail_path, sg_task, progress_cb): """ Publish the main Motion Builder scene :param task: The primary task to publish :param work_template: The primary work template to use :param comment: The publish description/comment :param thumbnail_path: The path to the thumbnail to associate with the published file :param sg_task: The Shotgun task that this publish should be associated with :param progress_cb: A callback to use when reporting any progress to the UI :returns: The path to the file that has been published """ from pyfbsdk import FBApplication mb_app = FBApplication() progress_cb(0.0, "Finding scene dependencies", task) dependencies = self._motionbuilder_find_additional_scene_dependencies() # get scene path scene_path = os.path.abspath(mb_app.FBXFileName) if not work_template.validate(scene_path): raise TankError( "File '%s' is not a valid work path, unable to publish!" % scene_path) # use templates to convert to publish path: output = task["output"] fields = work_template.get_fields(scene_path) fields["TankType"] = output["tank_type"] publish_template = output["publish_template"] publish_path = publish_template.apply_fields(fields) if os.path.exists(publish_path): raise TankError("The published file named '%s' already exists!" % publish_path) # save the scene: progress_cb(10.0, "Saving the scene") self.parent.log_debug("Saving the scene...") mb_app.FileSave(scene_path) # copy the file: progress_cb(50.0, "Copying the file") try: publish_folder = os.path.dirname(publish_path) self.parent.ensure_folder_exists(publish_folder) self.parent.log_debug("Copying %s --> %s..." % (scene_path, publish_path)) self.parent.copy_file(scene_path, publish_path, task) except Exception, e: raise TankError("Failed to copy file from %s to %s - %s" % (scene_path, publish_path, e))
def GetControlRigFKEffectors(character=None): if not character: character = FBApplication().CurrentCharacter if character: fkEffectors = [] for nodeId in FBBodyNodeId.values.itervalues(): if nodeId not in [ FBBodyNodeId.kFBInvalidNodeId, FBBodyNodeId.kFBLastNodeId ]: effector = character.GetCtrlRigModel(nodeId) if effector: fkEffectors.append(effector) if fkEffectors == []: fkEffectors = None return fkEffectors
def GetControlRigEffectors(character=None): if not character: character = FBApplication().CurrentCharacter if character: controlRigEffectors = GetControlRigFKEffectors( character) + GetControlRigIKEffectors(character) return controlRigEffectors
def _do_motionbuilder_pre_publish(self, task, work_template, progress_cb, user_data): """ Do Motion Builder primary pre-publish/scene validation :param task: The primary task to pre-publish :param work_template: The primary work template to use :param progress_cb: A callback to use when reporting any progress to the UI :param user_data: A dictionary containing any data shared by other hooks run prior to this hook. Additional data may be added to this dictionary that will then be accessible from user_data in any hooks run after this one. :returns: A list of any errors or problems that were found during pre-publish """ from pyfbsdk import FBApplication mb_app = FBApplication() progress_cb(0, "Validating current script", task) # get the current script file path: script_file = mb_app.FBXFileName if script_file: script_file = os.path.abspath(script_file) # validate it script_errors = self._validate_work_file(script_file, work_template, task["output"], progress_cb) progress_cb(100) return script_errors
def _do_motionbuilder_pre_publish(self, task, work_template, progress_cb): """ Do Motion Builder primary pre-publish/scene validation :param task: The primary task to pre-publish :param work_template: The primary work template to use :param progress_cb: A callback to use when reporting any progress to the UI :returns: A list of any errors or problems that were found during pre-publish """ from pyfbsdk import FBApplication mb_app = FBApplication() progress_cb(0, "Validating current script", task) # get the current script file path: script_file = mb_app.FBXFileName if script_file: script_file = os.path.abspath(script_file) # validate it script_errors = self._validate_work_file(script_file, work_template, task["output"], progress_cb) progress_cb(100) return script_errors
def CreateNewPose(name=None, character=None): if not character: character = FBApplication().CurrentCharacter if character: if not name: name = "New Pose 1" pose = FBCharacterPose(name) pose.CopyPose(character)
def PastePose(pose, character=None, pivot=None, match=True): if not character: character = FBApplication().CurrentCharacter if character: poseOptions = PoseOptions(pivot, match) FBBeginChangeAllModels() pose.PastePose(character, options) FBEndChangeAllModels() FBSystem().Scene.Evaluate()
def _import(self, path, sg_publish_data): """ Import contents of the given file into the scene. :param path: Path to file. :param sg_publish_data: Shotgun data dictionary with all the standard publish fields. """ from pyfbsdk import FBApplication if not os.path.exists(path): raise Exception("File not found on disk - '%s'" % path) (_, ext) = os.path.splitext(path) if ext.lower() != ".fbx": raise Exception("Unsupported file extension for '%s'. Only FBX files are supported." % path) app = FBApplication() app.FileMerge(path)
def GetCharacterEffectorsAndExtensions(character=None): if not character: character = FBApplication().CurrentCharacter if character: effectors = GetControlRigEffectors(character) extensions = GetCharacterExtensionObjects(character) if not isinstance(effectors, list): effectors = [effectors] if not isinstance(extensions, list): extensions = [extensions] return effectors + extensions
def GetControlRigForCharacter(character=None): if not character: character = FBApplication().CurrentCharacter if character: ctrlRig = character.PropertyList.Find("ControlSet") if len(ctrlRig) != 0: ctrlRig = ctrlRig[0] else: ctrlRig = None if ctrlRig: return ctrlRig
def AddObjectsToExtension(objList, extension=None, character=None): if not character: character = FBApplication().CurrentCharacter if not extension: if character: extension = FBCharacterExtension(character.LongName + "_Extension") else: extension = FBCharacterExtension("Character_Extension") if not isinstance(objList, list) and not isinstance( objList, FBPropertyListComponent): objList = [objList] for obj in objList: FBConnect(obj, extension) extension.AddObjectProperties(obj) extension.UpdateStancePose() if character: FBSystem().SuspendMessageBoxes = True character.AddCharacterExtension(extension) FBSystem().SuspendMessageBoxes = False return extension
def add_file_to_motionbuilder(self, file_path, shotgun_data): """ Load item into motionbuilder. This will attempt to merge the loaded file with the scene. """ from pyfbsdk import FBApplication if not os.path.exists(file_path): self.parent.log_error("The file %s does not exist." % file_path) return # get the slashes right file_path = file_path.replace(os.path.sep, "/") (path, ext) = os.path.splitext(file_path) if ext != ".fbx": self.parent.log_error("Unsupported file extension for %s. Only FBX files are supported." % file_path) else: app = FBApplication() app.FileMerge(file_path)
def AdjustmentBlendCharacter(character = None): if not character: character = FBApplication().CurrentCharacter if character: take = FBSystem().CurrentTake if take.GetLayerCount() > 1: characterObjs = GetCharacterEffectorsAndExtensions(character) for obj in characterObjs: if obj: AdjustmentBlendObject(obj) else: FBMessageBox("Error...", "No additive layer found. Adjustment blending affects interpolation between keys on the the top most additive layer.", "OK") else: FBMessageBox("Error...", "No additive layer found. Adjustment blending affects interpolation between keys on the top most additive layer.", "OK")
def KeyCharacter(character=None, layerName=None, includeScale=False): if not character: character = FBApplication().CurrentCharacter if character: if layerName: take = FBSystem().CurrentTake layer = take.GetLayerByName(layerName) if layer: take.SetCurrentLayer(layer.GetLayerIndex()) characterModels = GetCharacterEffectorsAndExtensions(character) if characterModels: for obj in characterModels: FBSystem().Scene.Evaluate KeyObject(obj, includeScale)
def _do_motionbuilder_post_publish(self, work_template, progress_cb, user_data): """ Do any Motion Builder post-publish work :param work_template: The primary work template used for the publish :param progress_cb: Callback to be used when reporting progress :param user_data: A dictionary containing any data shared by other hooks run prior to this hook. Additional data may be added to this dictionary that will then be accessible from user_data in any hooks run after this one. """ from pyfbsdk import FBApplication mb_app = FBApplication() progress_cb(0, "Versioning up the script") # get the current script path: original_path = mb_app.FBXFileName script_path = os.path.abspath(original_path) # increment version and construct new name: progress_cb(25, "Finding next version number") fields = work_template.get_fields(script_path) next_version = self._get_next_work_file_version(work_template, fields) fields["version"] = next_version new_path = work_template.apply_fields(fields) # log info self.parent.log_debug("Version up work file %s --> %s..." % (script_path, new_path)) # save the script: progress_cb(75, "Saving the scene file") mb_app.FileSave(new_path) progress_cb(100)
def GetControlRigIKEffectors(character=None): if not character: character = FBApplication().CurrentCharacter if character: ctrlRig = GetControlRigForCharacter(character) ikEffectors = [] for nodeId in FBEffectorId.values.itervalues(): if nodeId not in [ FBEffectorId.kFBInvalidEffectorId, FBEffectorId.kFBLastEffectorId ]: effector = ctrlRig.GetIKEffectorModel(nodeId, 0) if effector: ikEffectors.append(effector) if ikEffectors == []: ikEffectors = None return ikEffectors
def GetCharacterExtensionObjects(character=None): if not character: character = FBApplication().CurrentCharacter if character: characterExtentionObjects = [] for ext in FBSystem().Scene.CharacterExtensions: attachedChar = ext.PropertyList.Find("AttachedCharacter") if len(attachedChar) > 0: if attachedChar[0] == character: extObjList = GetObjectsFromExtension(ext) characterExtentionObjects = characterExtentionObjects + extObjList if characterExtentionObjects == []: characterExtentionObjects = None else: characterExtentionObjects = list( dict.fromkeys(characterExtentionObjects)) return characterExtentionObjects
def bootstrap_tank(): try: import tank except Exception as e: FBMessageBox("Shotgun: Error", "Could not import sgtk! Disabling for now: %s" % e, "Ok") return if not "TANK_ENGINE" in os.environ: FBMessageBox("Shotgun: Error", "Missing required environment variable TANK_ENGINE.", "Ok") return engine_name = os.environ.get("TANK_ENGINE") try: context = tank.context.deserialize(os.environ.get("TANK_CONTEXT")) except Exception as e: FBMessageBox( "Shotgun: Error", "Could not create context! Shotgun Pipeline Toolkit will be disabled. Details: %s" % e, "Ok", ) return try: engine = tank.platform.start_engine(engine_name, context.tank, context) except Exception as e: FBMessageBox("Shotgun: Error", "Could not start engine: %s" % e, "Ok") return # if a file was specified, load it now file_to_open = os.environ.get("TANK_FILE_TO_OPEN") if file_to_open: FBApplication.FileOpen(file_to_open) # clean up temp env vars for var in ["TANK_ENGINE", "TANK_CONTEXT", "TANK_FILE_TO_OPEN"]: if var in os.environ: del os.environ[var]
import os from pyfbsdk import FBFilePopup, FBFilePopupStyle, FBApplication, FBSystem import create_actor_from_opticals reload(create_actor_from_opticals) import map_opticals_to_actor reload(map_opticals_to_actor) # Construction of this are expensive, so we create them once. gSYSTEM = FBSystem() gAPPLICATION = FBApplication() # Get take object by it's name. def GetTakeByName(pName): lFound = [lTake for lTake in gSYSTEM.Scene.Takes if lTake.Name==pName] if lFound: return lFound[0] else: return None # Create take with pName. def createTake(pName): #bug in here #lTake = FBTake(pName) #gSYSTEM.Scene.Components.append(lTake) #workaround lIsNew = True lTakeName = pName lTake = GetTakeByName(lTakeName)
# # CONFIDENTIAL AND PROPRIETARY # # This work is provided "AS IS" and subject to the Shotgun Pipeline Toolkit # Source Code License included in this distribution package. See LICENSE. # By accessing, using, copying or modifying this work you indicate your # agreement to the Shotgun Pipeline Toolkit Source Code License. All rights # not expressly granted therein are reserved by Shotgun Software Inc. import os import sgtk from sgtk.util.filesystem import ensure_folder_exists from pyfbsdk import FBApplication, FBFilePopup, FBFilePopupStyle mb_app = FBApplication() HookBaseClass = sgtk.get_hook_baseclass() class MotionBuilderSessionPublishPlugin(HookBaseClass): """ Plugin for publishing an open Motion Builder session. This hook relies on functionality found in the base file publisher hook in the publish2 app and should inherit from it in the configuration. The hook setting for this plugin should look something like this:: hook: "{self}/publish_file.py:{engine}/tk-multi-publish2/basic/publish_session.py" """
elif lRotation[0] < 90: lRotation[0] = lRotation[0] + 180 else: lRotation[0] = -lRotation[0] lRotation[1] = -lRotation[1] lRotation[2] = -lRotation[2] #Setting the values for the corresponding opposite link dstList[i][0].SetVector(lRotation, FBModelTransformationType.kModelRotation, True) ## Calling the functions ## #The current character is the one used lChar = FBApplication().CurrentCharacter if not lChar: FBMessageBox("Warning", "No character is currently selected.", "Ok") print "Script terminated. No character selected." if lChar: if lChar.GetCharacterize(): FBMessageBox( "Warning", "Characterization is already locked.\n Changes will likely be lost.", "Ok") # User input lDirection = FBMessageBox("Mirror Characterization - Select Direction", "(Hips must be facing +Z axis)", "Left to Right",
def __init__(self, filePath='', team='', mobu=0, **kwargs): super(ParseSchema, self).__init__() self.filePath = filePath if not filePath: if mobu: # get sceneName from pyfbsdk import FBApplication # @UnresolvedImport self.filePath = Path(FBApplication().FBXFileName.replace( '\\', '/')) if not self.filePath: self.filePath = None elif self.filePath.basename() == '': self.filePath = None else: try: from pymel.core import sceneName except ImportError: sceneName = None logger.errorDialog( "ParseSchema thinks it is in Maya mode, forgot to pass 'mobu=1' arg?" ) self.filePath = Path(sceneName()) if not self.filePath: self.filePath = None elif self.filePath.basename() == 'untitled.ma': self.filePath = None # if no team passed, use first team listed in globalVariables.py if not team: team = gv.teamA #Parse the schema file relPath = (os.path.dirname(__file__).replace('\\', '/').replace( 'python/common/fileIO', 'schemas/')) self.xmlContent = ET.parse(relPath + team + '.xml') #Gather all global headers self.coreList = self.xmlContent.getiterator('Core')[0] self.characterList = self.xmlContent.getiterator('Character')[0] self.environmentList = self.xmlContent.getiterator('Environment')[0] self.exportList = self.xmlContent.getiterator('Export')[0] self.areaList = self.xmlContent.getiterator('Areas')[0] self.mayaList = self.xmlContent.getiterator('Maya')[0] self.mobuList = self.xmlContent.getiterator('MoBu')[0] self.team = team # Read in the asset hub xml section if self.team == gv.teamA: # Added headers self.riggingList = self.xmlContent.getiterator('Rigging')[0] self.assetHubList = self.xmlContent.getiterator('AssetHubUi')[0] # Self attributes self.fbxModels = eval(self.mobuList.get("FBXmodels")) self.retargetProcessing = eval( self.mobuList.get("retargetProcessing")) self.targetChar = eval(self.mobuList.get("targetCharacter")) self.sourceRetargetChar = eval( self.mobuList.get("sourceRetargetCharacters")) self.targetCharNode = self.mobuList.get("targetCharacterNode") if self.team == gv.teamB: # Added headers self.riggingList = self.xmlContent.getiterator('Rigging')[0] # Self attributes self.fbxModels = eval(self.mobuList.get("FBXmodels")) self.retargetProcessing = eval( self.mobuList.get("retargetProcessing")) self.targetChar = eval(self.mobuList.get("targetCharacter")) self.sourceRetargetChar = eval( self.mobuList.get("sourceRetargetCharacters")) self.targetCharNode = self.mobuList.get("targetCharacterNode") if self.team == gv.teamC: # Added headers self.riggingList = self.xmlContent.getiterator('Rigging')[0] # Self attributes self.fbxModels = eval(self.mobuList.get("FBXmodels")) self.retargetProcessing = eval( self.mobuList.get("retargetProcessing")) self.targetChar = eval(self.mobuList.get("targetCharacter")) self.sourceRetargetChar = eval( self.mobuList.get("sourceRetargetCharacters")) self.targetCharNode = self.mobuList.get("targetCharacterNode")
def ZeroTrans(transNodes): # Subtract the X & Z first keyframe value from # the current X & Z positions to zero out their translations for node in transNodes: if node.Name == "X" or node.Name == "Z": keyOffset = node.FCurve.Keys[0].Value for key in node.FCurve.Keys: key.Value = key.Value - keyOffset # Create an empty Componenet List to populated with the effector models compList = FBComponentList() # Get reference to current character and grab the namespace, if there is one currChar = FBApplication().CurrentCharacter if currChar.LongName.rfind(":") != -1: nameSpace = currChar.LongName.split(":")[0] + ":" else: nameSpace = "" # If Char Controls is active run through the Control Rig joint models # Else apply the calculation to the Skel Hips joint if currChar.ActiveInput: # Find all Effectors, select them, turn down Reach Values, unselect them FBFindObjectsByName("*Effector", compList, False, False) for comp in compList: comp.Selected = True if comp.PropertyList.Find("IK Blend T"): comp.PropertyList.Find("IK Blend T").Data = 0 if comp.PropertyList.Find("IK Blend R"):
def execute(self, operation, file_path, context, parent_action, **kwargs): """ Main hook entry point :operation: String Scene operation to perform :file_path: String File path to use if the operation requires it (e.g. open) :context: Context The context the file operation is being performed in. :parent_action: This is the action that this scene operation is being executed for. This can be one of: - open_file - new_file - save_file_as - version_up :returns: Depends on operation: 'current_path' - Return the current scene file path as a String 'reset' - True if scene was reset to an empty state, otherwise False all others - None """ fb_app = FBApplication() if operation == "current_path": # return the current scene path return fb_app.FBXFileName elif operation == "open": # do new scene as Maya doesn't like opening # the scene it currently has open! fb_app.FileOpen(file_path) elif operation == "save": # save the current scene: # Note - have to pass the current scene name to # avoid showing the save-as dialog fb_app.FileSave(fb_app.FBXFileName) elif operation == "save_as": fb_app.FileSave(file_path) elif operation == "reset": """ Reset the scene to an empty state """ while True: # Note, there doesn't appear to be any way to query if # there are unsaved changes through the MotionBuilder # Python API. Therefore we just assume there are and # prompt the user anyway! res = QtGui.QMessageBox.question( None, "Save your scene?", "Your scene has unsaved changes. Save before proceeding?", QtGui.QMessageBox.Yes | QtGui.QMessageBox.No | QtGui.QMessageBox.Cancel) if res == QtGui.QMessageBox.Cancel: # stop now! return False elif res == QtGui.QMessageBox.No: break else: # save the file first # Note - have to pass the current scene name to # avoid showing the save-as dialog if fb_app.FileSave(fb_app.FBXFileName): break # perform file-new fb_app.FileNew() return True
return engine_name = os.environ.get("TANK_ENGINE") try: context = tank.context.deserialize(os.environ.get("TANK_CONTEXT")) except Exception, e: FBMessageBox( "Shotgun: Error", "Could not create context! Shotgun Pipeline Toolkit will be disabled. Details: %s" % e, "Ok") return try: engine = tank.platform.start_engine(engine_name, context.tank, context) except Exception, e: FBMessageBox("Shotgun: Error", "Could not start engine: %s" % e, "Ok") return # if a file was specified, load it now file_to_open = os.environ.get("TANK_FILE_TO_OPEN") if file_to_open: FBApplication.FileOpen(file_to_open) # clean up temp env vars for var in ["TANK_ENGINE", "TANK_CONTEXT", "TANK_FILE_TO_OPEN"]: if var in os.environ: del os.environ[var] bootstrap_tank()
# Exports snapshotted data from a motion builder scene into BitSquid's # internal data format. Each take in the scene is exported to a separate # file in a directory selected by the user. from pyfbsdk import FBSystem, FBApplication, FBFbxOptions, FBLabel, ShowTool, FBAddRegionParam, FBAttachType, FBImageContainer, FBList, FBFileFormatAndVersion, FBButton, FBButtonStyle, FBTextJustify, FBFolderPopup, FBFilePopup, FBFilePopupStyle, FBPlayerControl, FBTime, FBMatrix, FBModelTransformationMatrix, FBProgress from pyfbsdk_additions import ToolList, DestroyToolByName, CreateUniqueTool, HBoxLayout import re, os, os.path, sys, _winreg # Global objects and settings TOOL_NAME = "BitSquid Exporter" SYSTEM = FBSystem() APP = FBApplication() SCENE = FBSystem().Scene # Class that handles updating the motion builder progress bar based on current take and frame # number. Typical usage: # # progress.begin() # starts displaying the progress bar # progress.takes = 10 # sets the number of takes to export # for t in takes: # progress.frames = 100 # sets the number of frames in the current take # for f in frames: # # export # progress.next_frame() # tells the progress bar that one frame has been processed # progress.next_take() # tells the progerss bar that one take has been processed class Progress: def __init__(self): self.progress = FBProgress()