‰PNG #################################################################### # Adds current character as a story track with current take added #################################################################### from pyfbsdk import * def addCharacterTrack(): track = FBStoryTrack(FBStoryTrackType.kFBStoryTrackCharacter, story.RootFolder) track.Details.append(app.CurrentCharacter) track.Label = system.CurrentTake.Name try: clip = track.CopyTakeIntoTrack(system.CurrentTake.LocalTimeSpan, system.CurrentTake) clip.Selected = True except AttributeError: FBMessageBox("Error", "Other Layer have keys", "Ok") track.FBDelete() pass app = FBApplication() system = FBSystem() story = FBStory() scene = system.Scene addCharacterTrack() ‰PNG #################################################################### # Find Clip Dictionaries in ClipDictionaryMetadatas which have custom substring in name and add new Flags attribute to them # Print out entire modified ClipDictionaryMetadatas xml element to allow user to copy and paste back into clip set file #################################################################### from pyfbsdk import * import xml.etree.ElementTree as ET import tempfile import os import RS.Config def prettyPrint(elem, level=0): i = "\n" + level * "\t" if len( elem ): if not elem.text or not elem.text.strip(): elem.text = i + "\t" if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: prettyPrint(elem, level+1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i def getSubString(): value = None result = FBMessageBoxGetUserValue( "Add Flags to ClipDictionaryMetadatas", "Enter partial folder path (e.g. MECH_LOCO_M@GENERIC@)", value , FBPopupInputType.kFBPopupString, "Go" ) return result[1] def chooseClipSet(): # Create the popup and set necessary initial values. lFp = FBFilePopup() lFp.Caption = "Choose ClipSet to import" lFp.Style = FBFilePopupStyle.kFBFilePopupOpen # BUG: If we do not set the filter, we will have an exception. lFp.Filter = "*.meta" # Set the default path. lFp.Path = RS.Config.Project.Path.Assets + "\\export\\data\\anim\\clip_sets\\clip_sets_groups\\" # Get the GUI to show. lRes = lFp.Execute() # If we select files, show them, otherwise indicate that the selection was canceled. if lRes: return lFp else: return False #################################################################### # custom class so we can find line numbers # from: http://stackoverflow.com/questions/6949395/is-there-a-way-to-get-a-line-number-from-an-elementtree-element #################################################################### class LineNumberingParser(ET.XMLParser): def _start_list(self, *args, **kwargs): # Here we assume the default XML parser which is expat # and copy its element position attributes into output Elements element = super(self.__class__, self)._start_list(*args, **kwargs) element._start_line_number = self.parser.CurrentLineNumber element._start_column_number = self.parser.CurrentColumnNumber element._start_byte_index = self.parser.CurrentByteIndex return element def _end(self, *args, **kwargs): element = super(self.__class__, self)._end(*args, **kwargs) element._end_line_number = self.parser.CurrentLineNumber element._end_column_number = self.parser.CurrentColumnNumber element._end_byte_index = self.parser.CurrentByteIndex return element #################################################################### # main #################################################################### search_substring = getSubString() result = chooseClipSet() if result: xml_file = ( result.Path + "\\" + result.FileName ) tree = ET.parse( xml_file, parser=LineNumberingParser() ) root = tree.getroot() search_tag = 'key' #search_substring = 'MECH_LOCO_F@GENERIC' ############################################################## # only search in the children section of each meta file ############################################################## clipDictionaryMetadataElement = root.find('ClipDictionaryMetadatas') if clipDictionaryMetadataElement: children = clipDictionaryMetadataElement.getchildren() for element in children: if element.tag == 'Item': ############################################################## # find items which match 'search_tag' and 'search_substring' ############################################################## if search_tag in element.attrib: if search_substring in element.attrib[ search_tag ]: ############################################################## # add Flags attribute with DMF_ExcludeFromPopulationBudget ############################################################## new_sub_entry = ET.SubElement( element, 'Flags') new_sub_entry.text = 'DMF_ExcludeFromPopulationBudget' prettyPrint( clipDictionaryMetadataElement ) ############################################################## # Save out results and open file ############################################################## output_file_path = tempfile.gettempdir() + '\\mj_LocoMetaSearch_output.txt' with open(output_file_path,'w') as f: output = ET.tostring( clipDictionaryMetadataElement ) f.write( output ) f.closed os.startfile( output_file_path )‰PNG #################################################################### # Adds current character as a story track with current take added #################################################################### from pyfbsdk import * def addOverrideToStory(): track = FBStoryTrack(FBStoryTrackType.kFBStoryTrackCharacter, story.RootFolder) track.Details.append(app.CurrentCharacter) track.Label = system.CurrentTake.Name subtrack = track.CreateSubTrack( FBStoryTrackType.kFBStoryTrackCharacter, FBStoryTrackRefMode.kFBStoryTrackOverride ) #subtrack = FBStoryTrack(FBStoryTrackType.kFBStoryTrackCharacter, story.RootFolder) subtrack.Label = "(Override)" subtrack.ReferenceMode = FBStoryTrackRefMode.kFBStoryTrackOverride subtrack.PassThrough = True subtrack.AcceptKey = False app = FBApplication() system = FBSystem() story = FBStory() scene = system.Scene addOverrideToStory() ‰PNG from pyfbsdk import * from pyfbsdk_additions import * from RS.Utils import Scene def clamp(n, minn, maxn): return max( min(maxn, n), minn ) def clearSelection(): ''' Clear selection function ''' # Get selected models modelList = FBModelList () FBGetSelectedModels (modelList, None, True) # Deselect models for model in modelList: model.Selected = False def getCharacterHeirarchyList(character): characterNodeList = [] hips = character.GetModel( FBBodyNodeId.kFBHipsNodeId ) dummy = Scene.GetParent( hips ) Scene.GetChildren( dummy, characterNodeList, "", True ) return characterNodeList def plotOptions(): plot_options = FBPlotOptions() plot_options.PlotAllTakes = False plot_options.PlotOnFrame = True plot_options.PlotPeriod = FBTime(0, 0, 0, 1) plot_options.RotationFilterToApply = FBRotationFilter.kFBRotationFilterUnroll plot_options.UseConstantKeyReducer = False plot_options.ConstantKeyReducerKeepOneKey = True plot_options.PlotTranslationOnRootOnly = False return plot_options def RemoveXandYRotationKeys( object ): for key in object.Rotation.GetAnimationNode().Nodes[0].FCurve.Keys: key.Value = 0.0 for key in object.Rotation.GetAnimationNode().Nodes[1].FCurve.Keys: key.Value = 0.0 # enforce Time Independent Tangent mode on Z rotation keys for key in object.Rotation.GetAnimationNode().Nodes[2].FCurve.Keys: key.TangentMode = FBTangentMode.kFBTangentModeTimeIndependent def ResampleFilterRotationKeys( object, desired_density ): ############################################################################################################### # process z rotation fcurve and remove all keys except for: # * the start and end keys # * any keys which fall at frame_step intervals # Note: we use a cached fcurve to evaluate since we are deleting the keys as we go and it destroys our index ############################################################################################################### take_start_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() take_end_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() take_frame_range = float( take_end_frame - take_start_frame ) frame_step = int( take_frame_range / desired_density ) # z rotation key fcurve fcurve = object.Rotation.GetAnimationNode().Nodes[2].FCurve # make a copy of fcurve for reference fcurve_cache = FBFCurve() fcurve_cache.KeyReplaceBy( fcurve ) # loop backwards through fcurve keys for i in range( len( fcurve.Keys ) - 1, -1, -1 ): if frame_step != 0: time_step_modulo = ( fcurve_cache.Keys[ i ].Time - fcurve_cache.Keys[ 0 ].Time ).GetFrame() % frame_step else: time_step_modulo = 0 if fcurve_cache.Keys[ i ].Time != FBTime( 0,0,0, take_start_frame ) \ and fcurve_cache.Keys[ i ].Time != FBTime( 0,0,0, take_end_frame ) \ and time_step_modulo != 0: fcurve.KeyDelete( i, i ) def getSceneObject( name, characterNodeList ): for node in characterNodeList: if node.Name.lower() == name: return node # Error node not found in scene! FBMessageBox( "Error", "Error: '" + name + "' object not found in scene!", "OK" ) raise Exception( "Error: " + name + " not found in scene!" ) def goButtonCallBack(control, event): constraints = FBSystem().Scene.Constraints ##################################################################################################### # Get Character Nodes ##################################################################################################### currentCharacter = FBApplication().CurrentCharacter characterNodeList = getCharacterHeirarchyList(currentCharacter) # Find required nodes for constraints OH_UpperFixup = getSceneObject( 'oh_upperfixupdirection', characterNodeList ) OH_Facing = getSceneObject( 'oh_facingdirection', characterNodeList ) OH_Torso = getSceneObject( 'oh_torsodirection', characterNodeList ) OH_Independent = getSceneObject( 'oh_independentmoverdirection', characterNodeList ) OH_MoverOffset = getSceneObject( 'oh_moveroffset', characterNodeList ) skel_torso = getSceneObject( 'skel_spine3', characterNodeList ) skel_head = getSceneObject( 'skel_head', characterNodeList ) skel_shoulders = getSceneObject( 'skel_spine5', characterNodeList ) mover_node = getSceneObject( 'mover', characterNodeList ) skel_root = getSceneObject( 'skel_root', characterNodeList ) lMgr = FBConstraintManager() ##################################################################################################### # Create Facing Constraint ##################################################################################################### if t.facing_enable_button.State: # get index to rotation constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Rotation': lIndex = i break # create constraint oh_facing_constraint = lMgr.TypeCreateConstraint(lIndex) oh_facing_constraint.Name = 'OH_Facing_Direction_Constraint' # add ref objects for i in range(0, oh_facing_constraint.ReferenceGroupGetCount()): if oh_facing_constraint.ReferenceGroupGetName(i) == 'Constrained Object': oh_facing_constraint.ReferenceAdd(i, OH_Facing ) elif oh_facing_constraint.ReferenceGroupGetName(i) == 'Source': oh_facing_constraint.ReferenceAdd(i, skel_head ) # snap/activate constraint oh_facing_constraint.Active = True oh_facing_constraint.PropertyList.Find("Rotation").Data = FBVector3d( 0.0, 90.0, 0.0 ) ##################################################################################################### # Create Upper Fixup Constraint ##################################################################################################### if t.fixup_enable_button.State: # get index to rotation constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Rotation': lIndex = i break # create constraint oh_upperfixup_constraint = lMgr.TypeCreateConstraint(lIndex) oh_upperfixup_constraint.Name = 'OH_Upperfixup_Constraint' # add ref objects for i in range(0, oh_upperfixup_constraint.ReferenceGroupGetCount()): if oh_upperfixup_constraint.ReferenceGroupGetName(i) == 'Constrained Object': oh_upperfixup_constraint.ReferenceAdd(i, OH_UpperFixup ) elif oh_upperfixup_constraint.ReferenceGroupGetName(i) == 'Source': if t.hips_mode_button.State: oh_upperfixup_constraint.ReferenceAdd(i, skel_root ) else: oh_upperfixup_constraint.ReferenceAdd(i, mover_node ) # snap/activate constraint oh_upperfixup_constraint.Active = True oh_upperfixup_constraint.PropertyList.Find("Rotation").Data = FBVector3d( 0.0, 0.0, 0.0 ) ##################################################################################################### # Create Torso Constraint ##################################################################################################### if t.torso_enable_button.State: # get index to rotation constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Rotation': lIndex = i break # create constraint oh_torso_constraint = lMgr.TypeCreateConstraint(lIndex) oh_torso_constraint.Name = 'OH_Torso_Constraint' # add ref objects for i in range(0, oh_torso_constraint.ReferenceGroupGetCount()): if oh_torso_constraint.ReferenceGroupGetName(i) == 'Constrained Object': oh_torso_constraint.ReferenceAdd(i, OH_Torso ) elif oh_torso_constraint.ReferenceGroupGetName(i) == 'Source': oh_torso_constraint.ReferenceAdd(i, skel_torso ) # snap/activate constraint oh_torso_constraint.Active = True oh_torso_constraint.PropertyList.Find("Rotation").Data = FBVector3d( 0.0, 90.0, 0.0 ) ##################################################################################################### # Create Independent Constraint ##################################################################################################### if t.independent_enable_button.State: # get index to rotation constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Rotation': lIndex = i break # create constraint oh_independent_constraint = lMgr.TypeCreateConstraint(lIndex) oh_independent_constraint.Name = 'oh_independent_constraint' # add ref objects for i in range(0, oh_independent_constraint.ReferenceGroupGetCount()): if oh_independent_constraint.ReferenceGroupGetName(i) == 'Constrained Object': oh_independent_constraint.ReferenceAdd(i, OH_Independent ) elif oh_independent_constraint.ReferenceGroupGetName(i) == 'Source': oh_independent_constraint.ReferenceAdd(i, mover_node ) # snap/activate constraint oh_independent_constraint.Active = True oh_independent_constraint.PropertyList.Find("Rotation").Data = FBVector3d( 0.0, 0.0, 0.0 ) ##################################################################################################### # Create Mover Offset Constraint ##################################################################################################### if t.mover_offset_enable_button.State: #--------------------------------------------------------------------------------------------------- # Rotation Constraint # -------------------------------------------------------------------------------------------------- lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Rotation': lIndex = i break # create constraint oh_mover_offset_rot_constraint = lMgr.TypeCreateConstraint(lIndex) oh_mover_offset_rot_constraint.Name = 'oh_mover_offset_rot_constraint' # add ref objects for i in range(0, oh_mover_offset_rot_constraint.ReferenceGroupGetCount()): if oh_mover_offset_rot_constraint.ReferenceGroupGetName(i) == 'Constrained Object': oh_mover_offset_rot_constraint.ReferenceAdd(i, OH_MoverOffset) elif oh_mover_offset_rot_constraint.ReferenceGroupGetName(i) == 'Source': oh_mover_offset_rot_constraint.ReferenceAdd(i, mover_node) # snap/activate constraint oh_mover_offset_rot_constraint.Active = True if t.mover_offset_hips_mode_button.State == 1: oh_mover_offset_rot_constraint.PropertyList.Find("Rotation").Data = FBVector3d(0.0, 0.0, -180.0) else: oh_mover_offset_rot_constraint.PropertyList.Find("Rotation").Data = FBVector3d(0.0, 0.0, 0.0) #--------------------------------------------------------------------------------------------------- # Position Constraint # -------------------------------------------------------------------------------------------------- for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Position': lIndex = i break # create constraint oh_mover_offset_pos_constraint = lMgr.TypeCreateConstraint(lIndex) oh_mover_offset_pos_constraint.Name = 'oh_mover_offset_pos_constraint' # add ref objects for i in range(0, oh_mover_offset_pos_constraint.ReferenceGroupGetCount()): if oh_mover_offset_pos_constraint.ReferenceGroupGetName(i) == 'Constrained Object': oh_mover_offset_pos_constraint.ReferenceAdd(i, OH_MoverOffset) elif oh_mover_offset_pos_constraint.ReferenceGroupGetName(i) == 'Source': oh_mover_offset_pos_constraint.ReferenceAdd(i, mover_node) # snap/activate constraint oh_mover_offset_pos_constraint.Active = True if t.mover_offset_hips_mode_button.State == 1: oh_mover_offset_pos_constraint.PropertyList.Find("Translation").Data = FBVector3d(0.0, 0.0, 0.0) else: oh_mover_offset_pos_constraint.PropertyList.Find("Translation").Data = FBVector3d(0.0, -100.0, 0.0) ##################################################################################################### # Plot and delete constraint ##################################################################################################### selected_takes_list = [] # how many takes has user selected? for iTake in FBSystem().Scene.Takes: if ( iTake.Selected == True ): selected_takes_list.append(iTake) clearSelection() OH_Facing.Selected = t.facing_enable_button.State OH_UpperFixup.Selected = t.fixup_enable_button.State OH_Torso.Selected = t.torso_enable_button.State OH_Independent.Selected = t.independent_enable_button.State OH_MoverOffset.Selected = t.mover_offset_enable_button.State # if user hasn't selected any takes then just use the current one if len( selected_takes_list ) == 0: # make sure system in on base layer FBSystem().CurrentTake.SetCurrentLayer(0) FBSystem().CurrentTake.PlotTakeOnSelected( plotOptions() ) # flatten torso contraint by zeroing keys on X and Y axes if t.facing_enable_button.State: RemoveXandYRotationKeys( OH_Facing ) ResampleFilterRotationKeys( OH_Facing, t.desired_oh_facing_key_density ) if t.torso_enable_button.State: RemoveXandYRotationKeys( OH_Torso ) ResampleFilterRotationKeys( OH_Torso, t.desired_oh_torso_key_density ) if t.fixup_enable_button.State: RemoveXandYRotationKeys( OH_UpperFixup ) ResampleFilterRotationKeys( OH_UpperFixup, t.desired_oh_fixup_key_density ) if t.independent_enable_button.State: RemoveXandYRotationKeys( OH_Independent ) ResampleFilterRotationKeys( OH_Independent, t.desired_oh_fixup_key_density ) if t.mover_offset_enable_button.State: RemoveXandYRotationKeys(OH_MoverOffset) ResampleFilterRotationKeys(OH_MoverOffset, t.desired_oh_fixup_key_density) # else plot each selected take else: for take in selected_takes_list: FBSystem().CurrentTake = take # make sure system in on base layer FBSystem().CurrentTake.SetCurrentLayer(0) FBSystem().CurrentTake.PlotTakeOnSelected( plotOptions() ) # flatten torso contraint by zeroing keys on X and Y axes if t.facing_enable_button.State: RemoveXandYRotationKeys( OH_Facing ) ResampleFilterRotationKeys( OH_Facing, t.desired_oh_facing_key_density ) if t.torso_enable_button.State: RemoveXandYRotationKeys( OH_Torso ) ResampleFilterRotationKeys( OH_Torso, t.desired_oh_torso_key_density ) if t.fixup_enable_button.State: RemoveXandYRotationKeys( OH_UpperFixup ) ResampleFilterRotationKeys( OH_UpperFixup, t.desired_oh_fixup_key_density ) if t.independent_enable_button.State: RemoveXandYRotationKeys( OH_Independent ) ResampleFilterRotationKeys( OH_Independent, t.desired_oh_fixup_key_density ) if t.mover_offset_enable_button.State: RemoveXandYRotationKeys(OH_MoverOffset) ResampleFilterRotationKeys(OH_MoverOffset, t.desired_oh_fixup_key_density) clearSelection() # delete constraints if t.facing_enable_button.State: oh_facing_constraint.FBDelete() if t.fixup_enable_button.State: oh_upperfixup_constraint.FBDelete() if t.torso_enable_button.State: oh_torso_constraint.FBDelete() if t.independent_enable_button.State: oh_independent_constraint.FBDelete() if t.mover_offset_enable_button.State: oh_mover_offset_rot_constraint.FBDelete() oh_mover_offset_pos_constraint.FBDelete() #################################################################### # UI definition and callbacks #################################################################### def oh_facing_key_density_sliderUpdated(control, event): take_start_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() take_end_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() take_frame_range = take_end_frame - take_start_frame density_value = int( control.Value * t.max_key_density ) density_value = clamp( density_value, 1 , t.max_key_density ) t.oh_facing_key_density_label.Caption = str ( density_value ) t.desired_oh_facing_key_density = density_value def oh_torso_key_density_sliderUpdated(control, event): take_start_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() take_end_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() take_frame_range = take_end_frame - take_start_frame density_value = int( control.Value * t.max_key_density ) density_value = clamp( density_value, 1 , t.max_key_density ) t.oh_torso_key_density_label.Caption = str ( density_value ) t.desired_oh_torso_key_density = density_value def oh_fixup_key_density_sliderUpdated(control, event): take_start_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() take_end_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() take_frame_range = take_end_frame - take_start_frame density_value = int( control.Value * t.max_key_density ) density_value = clamp( density_value, 1 , t.max_key_density ) t.oh_fixup_key_density_label.Caption = str ( density_value ) t.desired_oh_fixup_key_density = density_value def modeButtonsCallback(control, event): if t.mover_mode_button.State: t.oh_fixup_key_density_slider.Enabled = False if t.hips_mode_button.State: t.oh_fixup_key_density_slider.Enabled = True def EnableButtonCallback(control, event): t.oh_facing_key_density_slider.Enabled = t.facing_enable_button.State t.oh_facing_key_density_label.Enabled = t.facing_enable_button.State t.oh_torso_key_density_slider.Enabled = t.torso_enable_button.State t.oh_torso_key_density_label.Enabled = t.torso_enable_button.State t.oh_fixup_key_density_slider.Enabled = t.fixup_enable_button.State t.oh_fixup_key_density_label.Enabled = t.fixup_enable_button.State t.hips_mode_button.Enabled = t.fixup_enable_button.State t.mover_mode_button.Enabled = t.fixup_enable_button.State #t.independent_hips_mode_button.Enabled = t.independent_enable_button.State t.independent_mover_mode_button.Enabled = t.independent_enable_button.State t.mover_offset_mover_mode_button.Enabled = t.mover_offset_enable_button.State t.mover_offset_hips_mode_button.Enabled = t.mover_offset_enable_button.State def PopulateTool(t): ui_x_offset = 5 ui_y_offset = 5 ui_x_width = 250 ui_y_height = 70 ui_button_height = 30 ui_button_width = 40 ui_column_a = 50 ui_column_b = 130 ui_column_c = 180 ########################################################################################## # Mode Border and Controls ########################################################################################## ui_y_offset += 5 ######################################################################################### # SLIDER DEFAULTS ########################################################################################## t.min_key_density = 0 t.max_key_density = 20 t.oh_facing_key_density_default = 12 t.oh_torso_key_density_default = 18 t.oh_fixup_key_density_default = 4 ########################################################################################## # OH Facing Sample Label & oh_torso_key_density_slider ########################################################################################## x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_y_height,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_facing_key_density_slider_border","OH Facing Key Density", x, y, w, h) t.SetBorder("oh_facing_key_density_slider_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_y_offset += 25 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_facing_density_label","oh_facing_density_label", x, y, w, h) t.desired_oh_facing_key_density = t.oh_facing_key_density_default t.oh_facing_key_density_label = FBLabel() t.SetControl("oh_facing_density_label", t.oh_facing_key_density_label) t.oh_facing_key_density_label.Justify = FBTextJustify.kFBTextJustifyCenter t.oh_facing_key_density_label.Visible = True t.oh_facing_key_density_label.ReadOnly = False t.oh_facing_key_density_label.Enabled = True t.oh_facing_key_density_label.Caption = str( t.desired_oh_facing_key_density ) ui_y_offset += 20 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_facing_key_density_slider","oh_facing_key_density_slider", x, y, w, h) t.oh_facing_key_density_slider = FBSlider() t.oh_facing_key_density_slider.Value = ( 1.0 / t.max_key_density ) * t.desired_oh_facing_key_density t.oh_facing_key_density_slider.Orientation = FBOrientation.kFBHorizontal t.oh_facing_key_density_slider.OnTransaction.Add( oh_facing_key_density_sliderUpdated ) t.SetControl("oh_facing_key_density_slider", t.oh_facing_key_density_slider) #------------------------------------------------------------------------------------ # Enable Button ui_y_offset -= 35 x = FBAddRegionParam(ui_x_offset + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset + 5,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("facing_enable_button","facing_enable_button", x, y, w, h) t.facing_enable_button = FBButton() t.SetControl("facing_enable_button", t.facing_enable_button) t.facing_enable_button.Visible = True t.facing_enable_button.ReadOnly = False t.facing_enable_button.Enabled = True t.facing_enable_button.Hint = "Animate OH_FacingDirection?" t.facing_enable_button.Caption = "" t.facing_enable_button.State = 1 t.facing_enable_button.Style = FBButtonStyle.kFBCheckbox t.facing_enable_button.Justify = FBTextJustify.kFBTextJustifyLeft t.facing_enable_button.Look = FBButtonLook.kFBLookNormal t.facing_enable_button.OnClick.Add( EnableButtonCallback ) ui_y_offset += 35 #------------------------------------------------------------------------------------ ########################################################################################## # OH Torso Sample Label & oh_torso_key_density_slider ########################################################################################## ui_y_offset += 35 x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_y_height,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_torso_key_density_slider_border","OH Torso Key Density", x, y, w, h) t.SetBorder("oh_torso_key_density_slider_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_y_offset += 30 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_torso_density_label","oh_torso_density_label", x, y, w, h) t.desired_oh_torso_key_density = t.oh_torso_key_density_default t.oh_torso_key_density_label = FBLabel() t.SetControl("oh_torso_density_label", t.oh_torso_key_density_label) t.oh_torso_key_density_label.Justify = FBTextJustify.kFBTextJustifyCenter t.oh_torso_key_density_label.Visible = True t.oh_torso_key_density_label.ReadOnly = False t.oh_torso_key_density_label.Enabled = True t.oh_torso_key_density_label.Caption = str( t.desired_oh_torso_key_density ) ui_y_offset += 20 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_torso_key_density_slider","oh_torso_key_density_slider", x, y, w, h) t.oh_torso_key_density_slider = FBSlider() t.oh_torso_key_density_slider.Value = ( 1.0 / t.max_key_density ) * t.desired_oh_torso_key_density t.oh_torso_key_density_slider.Orientation = FBOrientation.kFBHorizontal t.oh_torso_key_density_slider.OnTransaction.Add( oh_torso_key_density_sliderUpdated ) t.SetControl("oh_torso_key_density_slider", t.oh_torso_key_density_slider) #------------------------------------------------------------------------------------ # Enable Button ui_y_offset -= 35 x = FBAddRegionParam(ui_x_offset + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset + 5,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("torso_enable_button","torso_enable_button", x, y, w, h) t.torso_enable_button = FBButton() t.SetControl("torso_enable_button", t.torso_enable_button) t.torso_enable_button.Visible = True t.torso_enable_button.ReadOnly = False t.torso_enable_button.Enabled = True t.torso_enable_button.Hint = "Animate OH_TorsoDirection?" t.torso_enable_button.Caption = "" t.torso_enable_button.State = 1 t.torso_enable_button.Style = FBButtonStyle.kFBCheckbox t.torso_enable_button.Justify = FBTextJustify.kFBTextJustifyLeft t.torso_enable_button.Look = FBButtonLook.kFBLookNormal t.torso_enable_button.OnClick.Add( EnableButtonCallback ) ui_y_offset += 35 #------------------------------------------------------------------------------------ ########################################################################################## # OH Fixup Sample Label & oh_torso_key_density_slider ########################################################################################## ui_y_offset += 30 x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_y_height + 30,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_fixup_key_density_slider_border","OH Fixup Follow Mode & Key Density", x, y, w, h) t.SetBorder("oh_fixup_key_density_slider_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_y_offset += 30 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"mover_mode_button") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(95,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion("mover_mode_button","mover_mode_button", x, y, w, h) t.mover_mode_button = FBButton() t.SetControl("mover_mode_button", t.mover_mode_button) t.mover_mode_button.Visible = True t.mover_mode_button.ReadOnly = False t.mover_mode_button.Enabled = True t.mover_mode_button.Hint = "Simply match mover direction" t.mover_mode_button.Caption = "Mover" t.mover_mode_button.State = 1 t.mover_mode_button.Style = FBButtonStyle.kFBRadioButton t.mover_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.mover_mode_button.Look = FBButtonLook.kFBLookNormal t.mover_mode_button.OnClick.Add( modeButtonsCallback ) x = FBAddRegionParam(ui_column_b,FBAttachType.kFBAttachTop,"hips_mode_button") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(90,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion("hips_mode_button","hips_mode_button", x, y, w, h) t.hips_mode_button = FBButton() t.SetControl("hips_mode_button", t.hips_mode_button) t.hips_mode_button.Visible = True t.hips_mode_button.ReadOnly = False t.hips_mode_button.Enabled = True t.hips_mode_button.Hint = "Follow hip direction using key density options" t.hips_mode_button.Caption = "Hips" t.hips_mode_button.State = 0 t.hips_mode_button.Style = FBButtonStyle.kFBRadioButton t.hips_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.hips_mode_button.Look = FBButtonLook.kFBLookNormal t.hips_mode_button.OnClick.Add( modeButtonsCallback ) t.fixup_mode_radio_buttons = FBButtonGroup() t.fixup_mode_radio_buttons.Add(t.mover_mode_button) t.fixup_mode_radio_buttons.Add(t.hips_mode_button) ui_y_offset += 30 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_fixup_density_label","oh_fixup_density_label", x, y, w, h) t.desired_oh_fixup_key_density = t.oh_fixup_key_density_default t.oh_fixup_key_density_label = FBLabel() t.SetControl("oh_fixup_density_label", t.oh_fixup_key_density_label) t.oh_fixup_key_density_label.Justify = FBTextJustify.kFBTextJustifyCenter t.oh_fixup_key_density_label.Visible = True t.oh_fixup_key_density_label.ReadOnly = False t.oh_fixup_key_density_label.Enabled = True t.oh_fixup_key_density_label.Caption = str( t.desired_oh_fixup_key_density ) ui_y_offset += 20 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_fixup_key_density_slider","oh_fixup_key_density_slider", x, y, w, h) t.oh_fixup_key_density_slider = FBSlider() t.oh_fixup_key_density_slider.Value = ( 1.0 / t.max_key_density ) * t.desired_oh_fixup_key_density t.oh_fixup_key_density_slider.Orientation = FBOrientation.kFBHorizontal t.oh_fixup_key_density_slider.OnTransaction.Add( oh_fixup_key_density_sliderUpdated ) t.SetControl("oh_fixup_key_density_slider", t.oh_fixup_key_density_slider) #------------------------------------------------------------------------------------ # Enable Button ui_y_offset -= 70 x = FBAddRegionParam(ui_x_offset + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset + 5,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("fixup_enable_button","fixup_enable_button", x, y, w, h) t.fixup_enable_button = FBButton() t.SetControl("fixup_enable_button", t.fixup_enable_button) t.fixup_enable_button.Visible = True t.fixup_enable_button.ReadOnly = False t.fixup_enable_button.Enabled = True t.fixup_enable_button.Hint = "Animate OH_FixupDirection?" t.fixup_enable_button.Caption = "" t.fixup_enable_button.State = 1 t.fixup_enable_button.Style = FBButtonStyle.kFBCheckbox t.fixup_enable_button.Justify = FBTextJustify.kFBTextJustifyLeft t.fixup_enable_button.Look = FBButtonLook.kFBLookNormal t.fixup_enable_button.OnClick.Add( EnableButtonCallback ) ui_y_offset += 70 #------------------------------------------------------------------------------------ ########################################################################################## # OH Independent Mover Direction ########################################################################################## ui_y_offset += 30 x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_y_height,FBAttachType.kFBAttachNone,"") t.AddRegion("oh_independent_mover_border","OH Independent Follow Mode", x, y, w, h) t.SetBorder("oh_independent_mover_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_y_offset += 30 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"independent_mover_mode_button") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(95,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion("independent_mover_mode_button","independent_mover_mode_button", x, y, w, h) t.independent_mover_mode_button = FBButton() t.SetControl("independent_mover_mode_button", t.independent_mover_mode_button) t.independent_mover_mode_button.Visible = True t.independent_mover_mode_button.ReadOnly = False t.independent_mover_mode_button.Enabled = True t.independent_mover_mode_button.Hint = "Simply match mover direction" t.independent_mover_mode_button.Caption = "Mover" t.independent_mover_mode_button.State = 1 t.independent_mover_mode_button.Style = FBButtonStyle.kFBRadioButton t.independent_mover_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.independent_mover_mode_button.Look = FBButtonLook.kFBLookNormal t.independent_mover_mode_button.OnClick.Add( modeButtonsCallback ) x = FBAddRegionParam(ui_column_b,FBAttachType.kFBAttachTop,"independent_hips_mode_button") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(90,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion("independent_hips_mode_button","independent_hips_mode_button", x, y, w, h) t.independent_hips_mode_button = FBButton() t.SetControl("independent_hips_mode_button", t.independent_hips_mode_button) t.independent_hips_mode_button.Visible = True t.independent_hips_mode_button.ReadOnly = True t.independent_hips_mode_button.Enabled = False t.independent_hips_mode_button.Hint = "Follow hip direction using key density options" t.independent_hips_mode_button.Caption = "Hips" t.independent_hips_mode_button.State = 0 t.independent_hips_mode_button.Style = FBButtonStyle.kFBRadioButton t.independent_hips_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.independent_hips_mode_button.Look = FBButtonLook.kFBLookNormal t.independent_hips_mode_button.OnClick.Add( modeButtonsCallback ) t.independent_mode_radio_buttons = FBButtonGroup() t.independent_mode_radio_buttons.Add(t.independent_mover_mode_button) t.independent_mode_radio_buttons.Add(t.independent_hips_mode_button) # Enable Button ui_y_offset -= 20 x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset + 5, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(15, FBAttachType.kFBAttachNone, "") t.AddRegion("independent_enable_button", "independent_enable_button", x, y, w, h) t.independent_enable_button = FBButton() t.SetControl("independent_enable_button", t.independent_enable_button) t.independent_enable_button.Visible = True t.independent_enable_button.ReadOnly = False t.independent_enable_button.Enabled = True t.independent_enable_button.Hint = "Animate OH_IndependentDirection?" t.independent_enable_button.Caption = "" t.independent_enable_button.State = 1 t.independent_enable_button.Style = FBButtonStyle.kFBCheckbox t.independent_enable_button.Justify = FBTextJustify.kFBTextJustifyLeft t.independent_enable_button.Look = FBButtonLook.kFBLookNormal t.independent_enable_button.OnClick.Add(EnableButtonCallback) ui_y_offset += 70 # ------------------------------------------------------------------------------------ ########################################################################################## # OH Mover Offset Direction ########################################################################################## ui_y_offset += 0 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_y_height, FBAttachType.kFBAttachNone, "") t.AddRegion("oh_mover_offset_border", "OH Mover Offset Follow Mode", x, y, w, h) t.SetBorder("oh_mover_offset_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ui_y_offset += 30 # Buttons x = FBAddRegionParam(ui_column_a, FBAttachType.kFBAttachTop, "mover_offset_mover_mode_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(95, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("mover_offset_mover_mode_button", "mover_offset_mover_mode_button", x, y, w, h) t.mover_offset_mover_mode_button = FBButton() t.SetControl("mover_offset_mover_mode_button", t.mover_offset_mover_mode_button) t.mover_offset_mover_mode_button.Visible = True t.mover_offset_mover_mode_button.ReadOnly = False t.mover_offset_mover_mode_button.Enabled = True t.mover_offset_mover_mode_button.Hint = "Match bottom of mover" t.mover_offset_mover_mode_button.Caption = "Mover" t.mover_offset_mover_mode_button.State = 0 t.mover_offset_mover_mode_button.Style = FBButtonStyle.kFBRadioButton t.mover_offset_mover_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.mover_offset_mover_mode_button.Look = FBButtonLook.kFBLookNormal #t.mover_offset_mover_mode_button.OnClick.Add(modeButtonsCallback) x = FBAddRegionParam(ui_column_b, FBAttachType.kFBAttachTop, "mover_offset_hips_mode_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("mover_offset_hips_mode_button", "mover_offset_hips_mode_button", x, y, w, h) t.mover_offset_hips_mode_button = FBButton() t.SetControl("mover_offset_hips_mode_button", t.mover_offset_hips_mode_button) t.mover_offset_hips_mode_button.Visible = True t.mover_offset_hips_mode_button.ReadOnly = False t.mover_offset_hips_mode_button.Enabled = True t.mover_offset_hips_mode_button.Hint = "Follow the reverse of mover direction (default)" t.mover_offset_hips_mode_button.Caption = "Default" t.mover_offset_hips_mode_button.State = 1 t.mover_offset_hips_mode_button.Style = FBButtonStyle.kFBRadioButton t.mover_offset_hips_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.mover_offset_hips_mode_button.Look = FBButtonLook.kFBLookNormal #t.mover_offset_hips_mode_button.OnClick.Add(modeButtonsCallback) t.mover_offset_mode_radio_buttons = FBButtonGroup() t.mover_offset_mode_radio_buttons.Add(t.mover_offset_mover_mode_button) t.mover_offset_mode_radio_buttons.Add(t.mover_offset_hips_mode_button) # Enable Button ui_y_offset -= 20 x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset + 5, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(15, FBAttachType.kFBAttachNone, "") t.AddRegion("mover_offset_enable_button", "mover_offset_enable_button", x, y, w, h) t.mover_offset_enable_button = FBButton() t.SetControl("mover_offset_enable_button", t.mover_offset_enable_button) t.mover_offset_enable_button.Visible = True t.mover_offset_enable_button.ReadOnly = False t.mover_offset_enable_button.Enabled = True t.mover_offset_enable_button.Hint = "Animate OH_MoverOffset?" t.mover_offset_enable_button.Caption = "" t.mover_offset_enable_button.State = 1 t.mover_offset_enable_button.Style = FBButtonStyle.kFBCheckbox t.mover_offset_enable_button.Justify = FBTextJustify.kFBTextJustifyLeft t.mover_offset_enable_button.Look = FBButtonLook.kFBLookNormal t.mover_offset_enable_button.OnClick.Add(EnableButtonCallback) ui_y_offset += 70 #------------------------------------------------------------------------------------ ########################################################################################## # Go button ########################################################################################## x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion("go_button_region","go_button_region", x, y, w, h) t.go_button = FBButton() t.SetControl("go_button_region", t.go_button) t.go_button.Visible = True t.go_button.ReadOnly = False t.go_button.Enabled = True t.go_button.Hint = "" t.go_button.Caption = "Animate OH Bones" t.go_button.State = 0 t.go_button.Style = FBButtonStyle.kFBPushButton t.go_button.Justify = FBTextJustify.kFBTextJustifyCenter t.go_button.Look = FBButtonLook.kFBLookNormal t.go_button.OnClick.Add(goButtonCallBack) ########################################################################################## # Calculate Tool Window Size and Draw ########################################################################################## t.StartSizeX = ui_x_width + 27 t.StartSizeY = ui_y_offset + 75 ShowTool(t) #################################################################### # Main #################################################################### def CreateTool(): global t version = 1.0 t = FBCreateUniqueTool("AnimateOHBones v" + str( version ) ) PopulateTool( t ) CreateTool()‰PNG from fbx import * from FbxCommon import * from pyfbsdk_additions import * from pyfbsdk import * import RS.Globals import RS.Config import fbx global SKEL_NODES_LIST def GetSkelNodes(): SKEL_NODES_LIST = [] HUMAN_NODES_LIST = ( RS.Globals.gSkelArray ) ANIMAL_NODES_LIST = ( RS.Globals.gAnimalSkelArray ) # construct master SKEL_NODES_LIST from human and animals # first add mover and badly named horse tail ( url:bugstar:2594825 - 2014 RS.Globals.gAnimalSkelTailArray is incorrect ) SKEL_NODES_LIST.append( 'mover' ) SKEL_NODES_LIST.append( 'SKEL_Tail1' ) SKEL_NODES_LIST.append( 'SKEL_Tail2' ) SKEL_NODES_LIST.append( 'SKEL_Tail3' ) SKEL_NODES_LIST.append( 'SKEL_Tail4' ) SKEL_NODES_LIST.append( 'PH_R_Hand' ) SKEL_NODES_LIST.append( 'PH_L_Hand' ) for entry in HUMAN_NODES_LIST: SKEL_NODES_LIST.append( entry ) for entry in ANIMAL_NODES_LIST: SKEL_NODES_LIST.append( RS.Globals.gAnimalPrefixSkel + entry ) return SKEL_NODES_LIST def GetFBXCharacterNamespace( name ): try: namespace = str( name ).split( ':' )[ 0 ] # Multiple ':' in the namespace, so deal with that situation. except ValueError: namespaces = str( name ).split( ':' )[ :-1 ] namespace = '' for i in namespaces: namespace += '{0}:'.format( i ) namespace = self.namespace.rstrip( ':' ) return namespace def findAnimationNode( pName, pNode ): lResult = None lName = pName.split( '/' ) for lNode in pNode.Nodes: if lNode.Name == lName[0]: if len( lName ) > 1: lResult = findAnimationNode( pName.replace( '%s/' % lName[0], '' ), lNode ) else: lResult = lNode return lResult def copyAnimationFromCurve( curve, animationNode ): ''' copies all animation from fbx curve, to MB animation node ''' num_of_keys = curve.KeyGetCount() fcurve = animationNode.FCurve # Clear existing animation on model. fcurve.EditClear() # Begin adding the new keys. fcurve.EditBegin() for key_index in range( num_of_keys ): value = curve.KeyGetValue( key_index ) time = curve.KeyGetTime( key_index ).Get() fcurve.KeyAdd( FBTime( time ), value ) fcurve.EditEnd() def fbxGetMatchingAnimationFromLayer( animLayer, node ): ''' Description: Inspects the supplied fbx animation layer, and finds the animation for each model on the layer. Author: Jason Hayes Arguments: animLayer: The fbx animation layer object. node: The fbx object to inspect. ''' # Iterate over each node and only look at meshes. for modelId in range( node.GetChildCount() ): model = node.GetChild( modelId ) #if model.GetTypeName() == 'LimbNode': if True: model_name = str( model.GetName() ) try: namespace, name = model_name.split( ':' ) # Hit multiple : in the name. For now, it isn't what we except ValueError: namespaces = model_name.split( ':' )[ :-1 ] namespace = '' for i in namespaces: namespace += '{0}:'.format( i ) namespace = namespace.rstrip( ':' ) name = model_name.split( ':' )[ -1 ] desired_source_namespace = GetFBXCharacterNamespace( t.source_char_list.Items[ t.source_char_list.ItemIndex ] ) #print "model_name is " + str( model_name ) #print "name is " + str( name ) #print "namespace is " + str( namespace ) #print "desired_source_namespace is " + str( desired_source_namespace ) if name in SKEL_NODES_LIST and namespace == desired_source_namespace: #print "processing source curve for " + str(namespace) + ":" + str(name) desired_model_long_name = t.selected_dest_char_namespace + ":" + name destinationModel = FBFindModelByLabelName( str( desired_model_long_name ) ) # If the model exists in the scene... if destinationModel: # transfer the fcurve data. # Translation xPosCurve = model.LclTranslation.GetCurve( animLayer, 'X' ) yPosCurve = model.LclTranslation.GetCurve( animLayer, 'Y' ) zPosCurve = model.LclTranslation.GetCurve( animLayer, 'Z' ) if xPosCurve: animationNode = findAnimationNode( 'Lcl Translation/X', destinationModel.AnimationNode ) if animationNode: copyAnimationFromCurve( xPosCurve, animationNode ) if yPosCurve: animationNode = findAnimationNode( 'Lcl Translation/Y', destinationModel.AnimationNode ) if animationNode: copyAnimationFromCurve( yPosCurve, animationNode ) if zPosCurve: animationNode = findAnimationNode( 'Lcl Translation/Z', destinationModel.AnimationNode ) if animationNode: copyAnimationFromCurve( zPosCurve, animationNode ) # Rotation xRotCurve = model.LclRotation.GetCurve( animLayer, 'X' ) yRotCurve = model.LclRotation.GetCurve( animLayer, 'Y' ) zRotCurve = model.LclRotation.GetCurve( animLayer, 'Z' ) if xRotCurve: animationNode = findAnimationNode( 'Lcl Rotation/X', destinationModel.AnimationNode ) if animationNode: copyAnimationFromCurve( xRotCurve, animationNode ) if yRotCurve: animationNode = findAnimationNode( 'Lcl Rotation/Y', destinationModel.AnimationNode ) if animationNode: copyAnimationFromCurve( yRotCurve, animationNode ) if zRotCurve: animationNode = findAnimationNode( 'Lcl Rotation/Z', destinationModel.AnimationNode ) if animationNode: copyAnimationFromCurve( zRotCurve, animationNode ) fbxGetMatchingAnimationFromLayer( animLayer, model ) def loadFBXIntoMemory( filepath ): # Initialize the fbx sdk. t.sdkManager, t.fbxScene = InitializeSdkObjects() # Load the fbx file into memory. result = LoadScene( t.sdkManager, t.fbxScene, filepath ) return result def fbxGetCharactersFromFBX( fbxScene ): num_characters = fbxScene.GetCharacterCount() characters = [] for char_index in range( num_characters ): character = fbxScene.GetCharacter( char_index ) characters.append( character.GetName() ) return characters def importCharacterAnimation(): ''' ''' # fetch list of selected takes selected_takes = [] for item in range( len(t.takeSelectionUI.list.Items) ): if( t.takeSelectionUI.list.IsSelected( item ) ): selected_takes.append( t.takeSelectionUI.list.Items[ item ] ) system = FBSystem() numTakes = t.fbxScene.GetSrcObjectCount( FbxAnimStack.ClassId ) for takeId in range( numTakes ): take = t.fbxScene.GetSrcObject( FbxAnimStack.ClassId, takeId ) take_name = str( take.GetName() ) if take_name in selected_takes: # create take FBSystem().CurrentTake.CopyTake("copy") # go to new take FBSystem().CurrentTake = FBSystem().Scene.Takes[-1] # clear all the animation on the new take FBSystem().CurrentTake.ClearAllProperties( False ) # rename take currentTake = system.CurrentTake currentTake.Name = take_name timespan = FbxAnimStack.GetLocalTimeSpan( take ) # set correct time range start = FBTime( timespan.GetStart().GetTime()[1], timespan.GetStart().GetTime()[2], timespan.GetStart().GetTime()[3], timespan.GetStart().GetTime()[4], 0 ) stop = FBTime( timespan.GetStop().GetTime()[1], timespan.GetStop().GetTime()[2], timespan.GetStop().GetTime()[3], timespan.GetStop().GetTime()[4], 0 ) FBSystem().CurrentTake.LocalTimeSpan = FBTimeSpan( start, stop ) # Get the number of animation layers for this take. num_layers = take.GetSrcObjectCount( FbxAnimLayer.ClassId ) for layer_index in range( num_layers ): # Get the animation layer object. layer = take.GetSrcObject( FbxAnimLayer.ClassId, layer_index ) layer_name = str ( layer.GetName() ).split( ':' )[ -1 ] scene_num_layers = currentTake.GetLayerCount() matchingLayer = None # run through layers and see if there are any missing matches for scene_layer_index in range( scene_num_layers ): scene_layer = currentTake.GetLayer( scene_layer_index ) if scene_layer.Name == layer_name: # TODO: Ask to make sure the user wants to override existing animation on this layer. currentTake.SetCurrentLayer( scene_layer_index ) matchingLayer = layer break # No layer using the name found, so create a new one. if not matchingLayer: currentTake.CreateNewLayer() scene_num_layers = currentTake.GetLayerCount() currentLayer = currentTake.GetLayer( scene_num_layers - 1 ) currentLayer.Name = layer_name currentTake.SetCurrentLayer( scene_num_layers - 1 ) # Iterate over each model in the animation file. # get matching models for each layer fbxGetMatchingAnimationFromLayer( layer, t.fbxScene.GetRootNode() ) takeId += 1 # close take selection UI FBDestroyToolByName("Select Takes") #--------------------------------------------------------------------------------------------------------------------------------------------------------------- # UI #--------------------------------------------------------------------------------------------------------------------------------------------------------------- def takeSelectionButtonCallback(control, event): global SKEL_NODES_LIST SKEL_NODES_LIST = GetSkelNodes() t.takeSelectionUI = FBCreateUniqueTool("Select Takes") PopulateTakeSelectionUI( t.takeSelectionUI ) def sourceListCallback(control, event): #Enable UI in response to user input t.source_char_label.Caption = "Source Selected" t.source_char_label.Enabled = True if t.source_char_label.Enabled and t.dest_char_label.Enabled: # enable > label t.target_label.Caption = ">>" t.target_label.Enabled = True # enable transfer button t.takeSelectionButton.Enabled = True def destListCallback(control, event): #Enable UI in response to user input t.dest_char_label.Caption = "Destination Selected" t.dest_char_label.Enabled = True #determine target namespace from selection t.selected_dest_char_namespace = control.Items[control.ItemIndex].split( ':' )[ 0 ] if t.source_char_label.Enabled and t.dest_char_label.Enabled: t.target_label.Caption = ">>" t.target_label.Enabled = True # enable transfer button t.takeSelectionButton.Enabled = True def loadButtonCallBack(control, event): # get fbx file path from user fbx = chooseFBX() if fbx: t.load_button.Caption = "Loading FBX..." filepath = ( fbx.Path + "\\" + fbx.FileName ) #load fbx into memory through SDK loadFBXIntoMemory( filepath ) characters = fbxGetCharactersFromFBX( t.fbxScene ) #add characters in source fbx to UI for char in characters: t.source_char_list.Items.append( str(char) ) t.load_button.Caption = "Load Complete. Select Source and Destination." t.load_button.Enabled = False else: print "Fbx selection cancelled." def PopulateListWithCharacters( uiList ): for char in RS.Globals.Characters: uiList.Items.append( char.LongName ) def chooseFBX(): # Create the popup and set necessary initial values. lFp = FBFilePopup() lFp.Caption = "Choose FBX to import from" lFp.Style = FBFilePopupStyle.kFBFilePopupOpen # BUG: If we do not set the filter, we will have an exception. lFp.Filter = "*.fbx" # Set the default path. lFp.Path = RS.Config.Project.Path.Art + "\\animation\\ingame\\source\\" # Get the GUI to show. lRes = lFp.Execute() # If we select files, show them, otherwise indicate that the selection was canceled. if lRes: return lFp else: return False def PopulateLayout(t): # ui modifiers for easier element positioning ui_tool_width = 250 ui_gap = 5 ui_list_width = ui_tool_width - ui_gap * 10 ui_list_height = 200 ui_targetlabel_width = 20 ui_label_height = 20 ui_button_height = 30 ui_window_edge = 18 # magic number to add on width of UI edge #init positions ui_offset_width = ui_gap ui_offset_height = ui_gap #---------------------------------------------------------------------------- # Source Character Label #---------------------------------------------------------------------------- t.source_char_label = FBLabel() x = FBAddRegionParam(ui_offset_width,FBAttachType.kFBAttachTop,"status_region") y = FBAddRegionParam(ui_offset_height,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_list_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_label_height,FBAttachType.kFBAttachNone,"") t.AddRegion("sourceLabel_region","sourceLabel_region", x, y, w, h) t.SetControl("sourceLabel_region", t.source_char_label) t.source_char_label.Visible = True t.source_char_label.ReadOnly = False t.source_char_label.Enabled = False t.source_char_label.Justify = FBTextJustify.kFBTextJustifyCenter t.source_char_label.Caption = "Select Source" #---------------------------------------------------------------------------- # Destination Character Label #---------------------------------------------------------------------------- ui_offset_width += ui_list_width + ui_targetlabel_width t.dest_char_label = FBLabel() x = FBAddRegionParam(ui_offset_width,FBAttachType.kFBAttachTop,"status_region") y = FBAddRegionParam(ui_offset_height,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_list_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_label_height,FBAttachType.kFBAttachNone,"") t.AddRegion("destLabel_region","destLabel_region", x, y, w, h) t.SetControl("destLabel_region", t.dest_char_label) t.dest_char_label.Visible = True t.dest_char_label.ReadOnly = False t.dest_char_label.Enabled = False t.dest_char_label.Justify = FBTextJustify.kFBTextJustifyCenter t.dest_char_label.Caption = "Select Destination" #---------------------------------------------------------------------------- # Source Character List #---------------------------------------------------------------------------- ui_offset_width = ui_gap ui_offset_height += ui_label_height + ui_gap x = FBAddRegionParam(ui_offset_width,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_offset_height,FBAttachType.kFBAttachTop,"") w = FBAddRegionParam(ui_list_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_list_height,FBAttachType.kFBAttachNone,"") t.AddRegion("source_char_list","source_char_list", x,y,w,h) t.source_char_list = FBList() t.SetControl("source_char_list", t.source_char_list) t.source_char_list.Style = FBListStyle.kFBVerticalList #add list call back t.source_char_list.OnChange.Add(sourceListCallback) #---------------------------------------------------------------------------- # > Label #---------------------------------------------------------------------------- ui_offset_width += ui_list_width t.target_label = FBLabel() x = FBAddRegionParam(ui_offset_width,FBAttachType.kFBAttachTop,"status_region") y = FBAddRegionParam(ui_offset_height,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_targetlabel_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_list_height,FBAttachType.kFBAttachNone,"") t.AddRegion("targetLabel_region","targetLabel_region", x, y, w, h) t.SetControl("targetLabel_region", t.target_label) t.target_label.Visible = True t.target_label.ReadOnly = False t.target_label.Enabled = False t.target_label.Justify = FBTextJustify.kFBTextJustifyCenter t.target_label.Caption = ">" #---------------------------------------------------------------------------- # Destination Character List #---------------------------------------------------------------------------- ui_offset_width += ui_targetlabel_width x = FBAddRegionParam(ui_offset_width,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_offset_height,FBAttachType.kFBAttachTop,"") w = FBAddRegionParam(ui_list_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_list_height,FBAttachType.kFBAttachNone,"") t.AddRegion("dest_char_list","dest_char_list", x,y,w,h) t.dest_char_list = FBList() t.SetControl("dest_char_list", t.dest_char_list) t.dest_char_list.Style = FBListStyle.kFBVerticalList PopulateListWithCharacters( t.dest_char_list ) t.dest_char_list.OnChange.Add(destListCallback) #---------------------------------------------------------------------------- # FBX load button #---------------------------------------------------------------------------- ui_offset_width = ui_gap ui_offset_height += ui_list_height + ui_gap x = FBAddRegionParam(ui_offset_width,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_offset_height,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam((ui_list_width * 2) + ui_targetlabel_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion("load_button_region","load_button_region", x, y, w, h) t.load_button = FBButton() t.SetControl("load_button_region", t.load_button) t.load_button.Visible = True t.load_button.ReadOnly = False t.load_button.Enabled = True t.load_button.Hint = "" t.load_button.Caption = "Choose Source FBX" t.load_button.State = 0 t.load_button.Style = FBButtonStyle.kFBPushButton t.load_button.Justify = FBTextJustify.kFBTextJustifyCenter t.load_button.Look = FBButtonLook.kFBLookNormal t.load_button.OnClick.Add(loadButtonCallBack) #---------------------------------------------------------------------------- # takeSlection Button #---------------------------------------------------------------------------- ui_offset_height += ui_button_height + ui_gap x = FBAddRegionParam(ui_offset_width,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_offset_height,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam((ui_list_width * 2) + ui_targetlabel_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion("takeSelection_button_region","takeSelection_button_region", x, y, w, h) t.takeSelectionButton = FBButton() t.SetControl("takeSelection_button_region", t.takeSelectionButton) t.takeSelectionButton.Visible = True t.takeSelectionButton.ReadOnly = False t.takeSelectionButton.Enabled = False t.takeSelectionButton.Hint = "" t.takeSelectionButton.Caption = "Select Takes" t.takeSelectionButton.State = 0 t.takeSelectionButton.Style = FBButtonStyle.kFBPushButton t.takeSelectionButton.Justify = FBTextJustify.kFBTextJustifyCenter t.takeSelectionButton.Look = FBButtonLook.kFBLookNormal t.takeSelectionButton.OnClick.Add(takeSelectionButtonCallback) #---------------------------------------------------------------- # show tool #---------------------------------------------------------------- t.StartSizeX = ui_gap*2 + ui_list_width*2 + ui_targetlabel_width + ui_window_edge t.StartSizeY = ui_list_height + ui_button_height * 2 + ui_label_height + ui_gap * 6 + ui_window_edge ShowTool(t) #################################################################### # Take Selection UI Callbacks #################################################################### def AllTakesCallback(control, event): for item in range( len(t.takeSelectionUI.list.Items) ): t.takeSelectionUI.list.Selected( item, True ) def NoTakesCallback(control, event): for item in range( len(t.takeSelectionUI.list.Items) ): t.takeSelectionUI.list.Selected( item, False ) def takeButtonCallback(control, event): print "Transferring Animation" importCharacterAnimation() #################################################################### # Take Selection UI #################################################################### def PopulateTakeSelectionUI( takeSelectionUI ): # ui variables for easier modification ui_tool_width = 500 ui_gap = 5 ui_list_width = ui_tool_width - ui_gap * 10 ui_border_x = 10 ui_border_y = 10 ui_button_width = 50 ui_border_width = ui_tool_width - ui_gap * 7 ui_border_height = 100 ui_row_offset = 10 MAX_LIST_BOX_ENTRIES = 40 ######################################################################## # All Takes Button x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") takeSelectionUI.AddRegion("all_takes_button","all_takes_button", x, y, w, h) takeSelectionUI.alltakesbutton = FBButton() takeSelectionUI.alltakesbutton.Caption = "All" takeSelectionUI.SetControl("all_takes_button", takeSelectionUI.alltakesbutton) #callback takeSelectionUI.alltakesbutton.OnClick.Add( AllTakesCallback ) ######################################################################## # No Takes Button x = FBAddRegionParam(ui_border_width - ui_button_width + ui_gap * 2,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") takeSelectionUI.AddRegion("no_takes_button","no_takes_button", x, y, w, h) takeSelectionUI.nonetakesbutton = FBButton() takeSelectionUI.nonetakesbutton.Caption = "None" takeSelectionUI.SetControl("no_takes_button", takeSelectionUI.nonetakesbutton) #callback takeSelectionUI.nonetakesbutton.OnClick.Add( NoTakesCallback ) ######################################################################## ui_row_offset += 25 #Take List takeSelectionUI.list = FBList() takeSelectionUI.list.Style = FBListStyle.kFBVerticalList takeSelectionUI.list.MultiSelect = True takeSelectionUI.list.ExtendedSelect = True x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") # get take names and calculate size required for UI list at same time num_of_takes = t.fbxScene.GetSrcObjectCount( FbxAnimStack.ClassId ) for take_id in range( num_of_takes ): take = t.fbxScene.GetSrcObject( FbxAnimStack.ClassId, take_id ) take_name = take.GetName() takeSelectionUI.list.Items.append( str(take_name ) ) # select by default takeSelectionUI.list.Selected( take_id, True ) #limit UI size if( len(takeSelectionUI.list.Items) < MAX_LIST_BOX_ENTRIES ): ui_row_offset += 15 w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") takeSelectionUI.AddRegion("takes","takes", x, y, w, h) takeSelectionUI.SetControl("takes", takeSelectionUI.list) ######################################################################## #Go Button ui_row_offset += 30 x = FBAddRegionParam(ui_border_x ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") takeSelectionUI.AddRegion("take_button","take_button", x, y, w, h) takeSelectionUI.takeButton = FBButton() takeSelectionUI.takeButton.Caption = "Transfer Selected" takeSelectionUI.SetControl("take_button", takeSelectionUI.takeButton) #callback takeSelectionUI.takeButton.OnClick.Add( takeButtonCallback ) ######################################################################## # draw tool ui_row_offset += 70 # set tool ui size takeSelectionUI.StartSizeX = ui_tool_width takeSelectionUI.StartSizeY = ui_row_offset ShowTool(takeSelectionUI) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Character Animation Importer") PopulateLayout(t) CreateTool() ‰PNG #################################################################### # Plot from a selection of Takes #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import RS.Globals # Global UI components MAX_LIST_BOX_ENTRIES = 40 COMMONLY_USED_EXTS = ['mover','PH_R_Hand','OH_Pelvis','OH_FacingDirection','OH_UpperFixupDirection','OH_TorsoDirection'] def getUniqueCharacterExtensions(): exts = FBSystem().Scene.CharacterExtensions UI_char_extension_objs = [] for e in exts: components = e.Components for c in components: existing = False for ext in UI_char_extension_objs: if c.Name == ext.Name: existing = True if not existing: UI_char_extension_objs.append( c ) return UI_char_extension_objs def clearSelection(): ''' Clear selection function ''' # Get selected models modelList = FBModelList() FBGetSelectedModels(modelList, None, True) # Deselect models for model in modelList: model.Selected = False def cleanSelectedExtensions( character, take ): index = 0 for item in range( len(t.list.Items) ): if( t.list.IsSelected( item ) ): t.char_ext_objs[ index ].Selected = True FBApplication().CurrentCharacter.KeyingMode = FBCharacterKeyingMode.kFBCharacterKeyingSelection take.ClearAllProperties( True ) index +=1 #################################################################### # UI CallBacks #################################################################### def AllExtsCallback(control, event): for item in range( len(t.list.Items) ): t.list.Selected( item, True ) def NoExtsCallback(control, event): for item in range( len(t.list.Items) ): t.list.Selected( item, False ) def cleanButtonCallBack(control, event): ################################################################################ # check user is sure ################################################################################ user_choice = FBMessageBox( "Message", "Delete all animation on selected character extensions?", "Yes", "No" ) if ( user_choice == 1 ): character_list = [] if t.currentCharacterButton.State == True: character_list.append( FBApplication().CurrentCharacter ) else: for char in RS.Globals.Characters: character_list.append( char ) for character in character_list: ################################################################################ # if current take only ################################################################################ if t.singleTakeModeButton.State: take = FBSystem().CurrentTake cleanSelectedExtensions( character, take ) ################################################################################ # all takes ################################################################################ else: for take in FBSystem().Scene.Takes: FBSystem().CurrentTake = take cleanSelectedExtensions( character, take ) clearSelection() print "Cleaning Complete" #################################################################### # UI #################################################################### def PopulateTool(t): # ui variables for easier modification ui_border_x = 10 ui_border_y = 10 ui_border_width = 250 ui_border_height = 40 ui_column_a = 20 ui_column_b = ui_border_width / 2 ui_row_offset = 10 ui_button_width = 40 ######################################################################## # All Exts Button x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 6 ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("all_exts_button","all_exts_button", x, y, w, h) t.allExtsButton = FBButton() t.allExtsButton.Caption = "All" t.SetControl("all_exts_button", t.allExtsButton) #callback t.allExtsButton.OnClick.Add( AllExtsCallback ) ######################################################################## # No Takes Button x = FBAddRegionParam(ui_border_width + 10 - ui_border_width / 4 ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 4 ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("no_exts_button","no_exts_button", x, y, w, h) t.noExtsButton = FBButton() t.noExtsButton.Caption = "None" t.SetControl("no_exts_button", t.noExtsButton) #callback t.noExtsButton.OnClick.Add( NoExtsCallback ) ######################################################################## ui_row_offset += 25 # Ext List t.list = FBList() t.list.Style = FBListStyle.kFBVerticalList t.list.MultiSelect = True t.list.ExtendedSelect = True x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") t.char_ext_objs = getUniqueCharacterExtensions() # get take names and calculate size required for UI list at same time for obj in t.char_ext_objs: t.list.Items.append( obj.Name ) # select in list unless it's a commonly used item if obj.Name not in COMMONLY_USED_EXTS: t.list.Selected( len( t.list.Items) - 1, True ) #limit UI size if( len(t.list.Items) < MAX_LIST_BOX_ENTRIES ): ui_row_offset += 15 w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") t.AddRegion("exts","exts", x, y, w, h) t.SetControl("exts", t.list) ui_row_offset = ui_row_offset + 10 ######################################################################## ui_row_offset += 40 t.takesRadios = FBButtonGroup() t.singleTakeModeButton = FBButton() t.allTakesModeButton = FBButton() t.takesRadios.Add( t.singleTakeModeButton ) t.takesRadios.Add( t.allTakesModeButton ) #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height,FBAttachType.kFBAttachNone,"") t.AddRegion("takes_border","Takes", x, y, w, h) t.SetBorder("takes_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"single_takes_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height / 2,FBAttachType.kFBAttachNone,"") t.AddRegion("single_takes_region","single_takes_region", x, y, w, h) t.SetControl("single_takes_region", t.singleTakeModeButton) t.singleTakeModeButton.Visible = True t.singleTakeModeButton.ReadOnly = False t.singleTakeModeButton.Enabled = True t.singleTakeModeButton.Hint = "" t.singleTakeModeButton.Caption = "Current" t.singleTakeModeButton.State = 0 t.singleTakeModeButton.Style = FBButtonStyle.kFBRadioButton t.singleTakeModeButton.Justify = FBTextJustify.kFBTextJustifyLeft t.singleTakeModeButton.Look = FBButtonLook.kFBLookNormal x = FBAddRegionParam(ui_column_b,FBAttachType.kFBAttachTop,"all_takes_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height / 2,FBAttachType.kFBAttachNone,"") t.AddRegion("all_takes_region","all_takes_region", x, y, w, h) t.SetControl("all_takes_region", t.allTakesModeButton ) t.allTakesModeButton.Visible = True t.allTakesModeButton.ReadOnly = False t.allTakesModeButton.Enabled = True t.allTakesModeButton.Hint = "" t.allTakesModeButton.Caption = "All" t.allTakesModeButton.State = 1 t.allTakesModeButton.Style = FBButtonStyle.kFBRadioButton t.allTakesModeButton.Justify = FBTextJustify.kFBTextJustifyLeft t.allTakesModeButton.Look = FBButtonLook.kFBLookNormal ######################################################################## # Current Character or All? ######################################################################## ui_row_offset += 45 t.takesRadios = FBButtonGroup() t.currentCharacterButton = FBButton() t.allCharacterButton = FBButton() t.takesRadios.Add( t.currentCharacterButton ) t.takesRadios.Add( t.allCharacterButton ) #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height,FBAttachType.kFBAttachNone,"") t.AddRegion("character_takes_border","Character?", x, y, w, h) t.SetBorder("character_takes_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"current_mode_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height / 2,FBAttachType.kFBAttachNone,"") t.AddRegion("current_mode_region","current_mode_region", x, y, w, h) t.SetControl("current_mode_region", t.currentCharacterButton) t.currentCharacterButton.Visible = True t.currentCharacterButton.ReadOnly = False t.currentCharacterButton.Enabled = True t.currentCharacterButton.Hint = "" t.currentCharacterButton.Caption = "Current" t.currentCharacterButton.State = 1 t.currentCharacterButton.Style = FBButtonStyle.kFBRadioButton t.currentCharacterButton.Justify = FBTextJustify.kFBTextJustifyLeft t.currentCharacterButton.Look = FBButtonLook.kFBLookNormal x = FBAddRegionParam(ui_column_b,FBAttachType.kFBAttachTop,"all_mode_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height / 2,FBAttachType.kFBAttachNone,"") t.AddRegion("all_mode_region","all_mode_region", x, y, w, h) t.SetControl("all_mode_region", t.allCharacterButton ) t.allCharacterButton.Visible = True t.allCharacterButton.ReadOnly = False t.allCharacterButton.Enabled = True t.allCharacterButton.Hint = "" t.allCharacterButton.Caption = "All" t.allCharacterButton.State = 0 t.allCharacterButton.Style = FBButtonStyle.kFBRadioButton t.allCharacterButton.Justify = FBTextJustify.kFBTextJustifyLeft t.allCharacterButton.Look = FBButtonLook.kFBLookNormal ######################################################################## #Clean Extensions Button ui_row_offset += 40 x = FBAddRegionParam(ui_border_x ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("clean_exts_button","clean_exts_button", x, y, w, h) t.cleanButton = FBButton() t.cleanButton.Caption = "Clean Extensions" t.SetControl("clean_exts_button", t.cleanButton) #callback t.cleanButton.OnClick.Add( cleanButtonCallBack ) ######################################################################## # draw tool ui_row_offset += 70 # set tool ui size t.StartSizeX = ui_border_width + 35 t.StartSizeY = ui_row_offset #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Clean Extensions") PopulateTool(t) ShowTool(t) CreateTool() #################################################################### # Allows user to clear character extension components of keys #################################################################### from pyfbsdk import * from pyfbsdk_additions import * # Global UI components clear_button = FBButton() #take control ui comps take_radio_group = FBButtonGroup() take_yes_button = FBButton() take_no_button = FBButton() take_radio_group.Add(take_yes_button) take_radio_group.Add(take_no_button) status = FBLabel() button = [] UI_char_extension_objs = [] selected_character = "" char_rollbone_objs = [] char_3lateral_objs = [] # Global UI components MAX_LIST_BOX_ENTRIES = 40 def ClearAnim( pNode ): # The FCurve property will not be null on a terminal node. # i.e. the 'Lcl Translation' node will not have any animation on it # directly... only the sub-nodes 'X', 'Y' or 'Z' may have animation. if pNode.FCurve: # Ah! there is a FCurve! Let's remove all the keys. pNode.FCurve.EditClear() else: # Then we are dealing with a parent node. Let's look at it # children nodes. for lNode in pNode.Nodes: # Recursively call ourselves to deal with sub-nodes. ClearAnim( lNode ) # Cleanup del( lNode ) def getSpecificCharacterExtension( obj ): global selected_character # get namespace of selected character namespace = ( selected_character ).split(":")[0] if namespace != "": namespace += ":" #print "namepsace is " + str(namespace) exts = FBSystem().Scene.CharacterExtensions for e in exts: components = e.Components for c in components: #print "combined namespace and obj.Name is " + namespace + obj.Name #print "c.LongName is " + c.LongName if namespace + obj.Name == c.LongName: #print "found match! with component " + c.LongName return c def getUniqueCharacterExtensions(): exts = FBSystem().Scene.CharacterExtensions for e in exts: components = e.Components for c in components: existing = False for ext in UI_char_extension_objs: if c.Name == ext.Name: existing = True if not existing: pass # UI_char_extension_objs.append( c ) def getCharacters(): return FBSystem().Scene.Characters def setupPropertyList(tool): global selected_character tool.list.Items.removeAll() tool.prop_list = [] character_list = getCharacters() if len(character_list): for char in character_list: tool.list.Items.append( char.OwnerNamespace.Name ) else: tool.list.Items.append('No characters found!') selected_character = tool.list.Items[0] #################################################################### # Clear Button CallBack #################################################################### def clearButtonCallBack(control, event): system = FBSystem() if take_yes_button.State == 1: currentTake = system.CurrentTake for take in FBSystem().Scene.Takes: system.CurrentTake = take index = 0 for obj in UI_char_extension_objs: if ( button[index].State == True ): ext = getSpecificCharacterExtension( obj ) ClearAnim( ext.AnimationNode ) #animationNode = ext.PropertyList.Find("AnimationNode") #if ( animationNode ): # ClearAnim( animationNode ) index += 1 # restore take system.CurrentTake = currentTake else: index = 0 for obj in UI_char_extension_objs: if ( button[index].State == True ): ext = getSpecificCharacterExtension( obj ) ClearAnim( ext.AnimationNode ) index += 1 print "Selected extensions cleared!" status.Caption = "Extension keys cleared" FBSystem().Scene.Evaluate() #################################################################### # ListCallBack #################################################################### def ListCallback(control, event): global selected_character selected_character = control.Items[control.ItemIndex] #################################################################### # UI #################################################################### def PopulateTool(t): global selected_character # ui variables for easier modification ui_border_x = 10 ui_border_y = 2 ui_border_width = 140 ui_border_height = 100 ui_column_a = 20 ui_column_b = 60 ui_column_c = 100 ui_row_offset = 10 ui_button_width = 120 ui_button_height = 15 #Mover Selection---------------------------------------------- #Drop Down x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(25,FBAttachType.kFBAttachNone,"") t.AddRegion("dropdown","dropdown", x, y, w, h) t.list = FBList() t.SetControl("dropdown", t.list) setupPropertyList(t) t.list.OnChange.Add(ListCallback) #Extensions-------------------------------------------------- #get all scene character extension objects getUniqueCharacterExtensions() ui_row_offset += 40 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( ( 20 * len(UI_char_extension_objs) ) + 15,FBAttachType.kFBAttachNone,"") t.AddRegion("trans_border","Character Extensions", x, y, w, h) t.SetBorder("trans_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons # create a button for each extension obj for i in range( 0, len(UI_char_extension_objs) ): region = "extButton" + str(i) x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop, region ) y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion(region,region, x, y, w, h) button.append( FBButton() ) t.SetControl(region, button[i] ) button[i].Visible = True button[i].ReadOnly = False button[i].Enabled = True button[i].Hint = "" button[i].Caption = UI_char_extension_objs[i].Name # set certain options as off for ease of use if ( "mover" in UI_char_extension_objs[i].Name ) or \ ( "PH_R_Hand" in UI_char_extension_objs[i].Name ) or \ ( "OH_FacingDirection" in UI_char_extension_objs[i].Name ) or \ ( "OH_UpperFixupDirection" in UI_char_extension_objs[i].Name ) or \ ( "PH_L_Hand" in UI_char_extension_objs[i].Name ): button[i].State = 0 else: button[i].State = 1 button[i].Style = FBButtonStyle.kFBCheckbox button[i].Justify = FBTextJustify.kFBTextJustifyLeft button[i].Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 #Takes-------------------------------------------------- #clear all takes? ui_row_offset += 40 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( 60 ,FBAttachType.kFBAttachNone,"") t.AddRegion("takes","Clear All Takes?", x, y, w, h) t.SetBorder("takes",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"take_yes_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("take_yes_button","take_yes_button", x, y, w, h) t.SetControl("take_yes_button", take_yes_button ) take_yes_button.Visible = True take_yes_button.ReadOnly = False take_yes_button.Enabled = True take_yes_button.Hint = "" take_yes_button.Caption = "Yes" take_yes_button.State = 1 take_yes_button.Style = FBButtonStyle.kFBRadioButton take_yes_button.Justify = FBTextJustify.kFBTextJustifyLeft take_yes_button.Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"take_no_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("take_no_button","take_no_button", x, y, w, h) t.SetControl("take_no_button", take_no_button ) take_no_button.Visible = True take_no_button.ReadOnly = False take_no_button.Enabled = True take_no_button.Hint = "" take_no_button.Caption = "No" take_no_button.State = 0 take_no_button.Style = FBButtonStyle.kFBRadioButton take_no_button.Justify = FBTextJustify.kFBTextJustifyLeft take_no_button.Look = FBButtonLook.kFBLookNormal #Clear Button-------------------------------------------------- ui_row_offset = ui_row_offset + 10 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"clear_button_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("clear_button_region","clear_button_region", x, y, w, h) t.SetControl("clear_button_region", clear_button) clear_button.Visible = True clear_button.ReadOnly = False clear_button.Enabled = True clear_button.Hint = "" clear_button.Caption = "Clear Keys on Selected" clear_button.State = 0 clear_button.Style = FBButtonStyle.kFBPushButton clear_button.Justify = FBTextJustify.kFBTextJustifyCenter clear_button.Look = FBButtonLook.kFBLookNormal clear_button.OnClick.Add(clearButtonCallBack) #Status -------------------------------------------------- ui_row_offset = ui_row_offset + 35 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"status_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("status_region","status_region", x, y, w, h) t.SetControl("status_region", status) status.Visible = True status.ReadOnly = False status.Enabled = True status.Caption = "" # set tool ui size t.StartSizeX = 175 t.StartSizeY = ui_row_offset + 60 #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Clean Extensions") PopulateTool(t) ShowTool(t) CreateTool() #################################################################### # Searches for references to clipsets in Loco and Meta files #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import xml.etree.ElementTree as ET import tempfile import os import RS.Config class Clipset(): def __init__(self, name = '', start_line_number = 0, end_line_number = 0, referenced = False): self.name = name self.start_line_number = start_line_number self.end_line_number = end_line_number self.referenced = referenced def Print(self): print "###################" print "clipset name is " + self.name print "clipset starts on line number " + self.start_line_number print "clipset ends on line number " + self.end_line_number return def generateClipsetsSearchArray(filepath, searchSubString): clipsets = [] for subdir, dirs, files in os.walk(filepath): for file in files: filepath = subdir + os.sep + file ############################################################## # only search in meta files ############################################################## if filepath.endswith(".meta"): tree = ET.parse(filepath, parser=LineNumberingParser()) root = tree.getroot() clipsetroot = root.find('ClipSets') attribute_tag = 'key' children = clipsetroot.getchildren() for child in children: if (child.tag == 'Item'): if attribute_tag in child.attrib: # is the search substring contained within the clipset entry? If so add it to the list if searchSubString in child.attrib[ attribute_tag ]: #print "clipset " + child.attrib[ attribute_tag ] + " starts at line number " + str(child._start_line_number) + " and ends at liner number " + str(child._end_line_number) + "\n" # create a clipset class, store the name and which line numbers it is stored at in the file clipset = Clipset(child.attrib[ attribute_tag ], child._start_line_number, child._end_line_number) clipsets.append(clipset) #print "CONTENTS OF CLIPSETS\n" #print "====================\n\n" #for clipset in clipsets: # print clipset.name return clipsets def findClipsetInMotions(parent, clipsets ): children = parent.getchildren() search_tag = 'Name' for child in children: if (child.tag == 'Item'): subelements = child.getchildren() for elem in subelements: if elem.tag == search_tag: # check name isn't left blank if not elem.text == None: # first strip off clip name from motion entry so we just have clipset # typically motion entry contains something like "CLIPSET@MECH_LOCO_M@GENERIC@EMOTION@UNARMED@PANIC@RUN_FROM_CROUCH/START_FWD_45" split_text = elem.text.split("/")[0] # check each entry in clipsets for a match i = 0 for clipset in clipsets: if split_text.upper() == clipset.name.upper(): clipset.referenced = True i = i + 1 return clipsets def findClipsetInLoco( parent, clipsets, debug = False ): children = parent.getchildren() search_tag = 'ClipSet' for child in children: if (child.tag == 'Item'): subelements = child.getchildren() for elem in subelements: if elem.tag == search_tag: # check clipset isn't left blank if not elem.text == None: # check each entry in clipsets for a match i = 0 for clipset in clipsets: if elem.text.upper() == clipset.name: clipset.referenced = True i = i + 1 return clipsets def SearchLoco(locoFilePath, clipsets): for subdir, dirs, files in os.walk(locoFilePath): for file in files: filepath = subdir + os.sep + file ############################################################## # only search in meta files ############################################################## if filepath.endswith(".meta"): tree = ET.parse(filepath, parser=LineNumberingParser()) root = tree.getroot() if not root.tag == 'Motions': root = root.find('Motions') if root is not None: ############################################################## # search in each file ############################################################## clipsets = findClipsetInLoco( root, clipsets) else: #print "Motions Elements not found in file " + filepath pass return clipsets def SearchMotions(motionsFilePath, clipsets): for subdir, dirs, files in os.walk(motionsFilePath): for file in files: filepath = subdir + os.sep + file ############################################################## # only search in motion files ############################################################## if filepath.endswith(".motionxml"): tree = ET.parse(filepath, parser=LineNumberingParser()) root = tree.getroot() if not root.tag == 'Motion': root = root.find('Motion') root = root.find('Clips') if root is not None: ############################################################## # search in each file ############################################################## clipsets = findClipsetInMotions(root, clipsets ) else: print "Clips Elements not found in file " + filepath return clipsets #################################################################### # custom class so we can find line numbers # from: http://stackoverflow.com/questions/6949395/is-there-a-way-to-get-a-line-number-from-an-elementtree-element #################################################################### class LineNumberingParser(ET.XMLParser): def _start_list(self, *args, **kwargs): # Here we assume the default XML parser which is expat # and copy its element position attributes into output Elements element = super(self.__class__, self)._start_list(*args, **kwargs) element._start_line_number = self.parser.CurrentLineNumber element._start_column_number = self.parser.CurrentColumnNumber element._start_byte_index = self.parser.CurrentByteIndex return element def _end(self, *args, **kwargs): element = super(self.__class__, self)._end(*args, **kwargs) element._end_line_number = self.parser.CurrentLineNumber element._end_column_number = self.parser.CurrentColumnNumber element._end_byte_index = self.parser.CurrentByteIndex return element def SearchClipsetsForReferences(clipsets, locoFilePath, motionsFilePath): if len(clipsets) > 0: ############################################################## # SEARCH LOCO FILES FOR CLIPSETS ############################################################## clipsets = SearchLoco(locoFilePath, clipsets) ############################################################## # SEARCH MOTIONS FILES FOR CLIPSETS ############################################################## clipsets = SearchMotions(motionsFilePath, clipsets) return clipsets def PrintResultsToTextFile(clipsets, searchSubString ): output = "\nSearching Loco and Motions files for usage of '" + searchSubString + "' and its variants\n\n" if len(clipsets) > 0: used_clipsets = [ ] unused_clipsets = [ ] for clipset in clipsets: if clipset.referenced == True: used_clipsets.append(clipset) else: unused_clipsets.append(clipset) output += "############################################################################################################################\n" output += "# Clipsets that are referenced in Loco or Motions \n" output += "############################################################################################################################\n\n" for used_clipset in used_clipsets: output += used_clipset.name + "\n" output += "\n############################################################################################################################\n" output += "# Clipsets not referenced in Loco or Motions (Possibly safe to delete, but may be in use outside of these systems) \n" output += "############################################################################################################################\n\n" for unused_clipset in unused_clipsets: output += unused_clipset.name + "\n" else: output += "############################################################################################################################\n" output += "# No Clipsets could be found in selected Clipsets File! Aborting Search. Did you mean to select a different file?\n" output += "############################################################################################################################\n\n" output_file_path = tempfile.gettempdir() + '\\mj_ClipsetSearchAndDestroy_output.txt' with open(output_file_path, 'w') as f: f.write(output) f.closed os.startfile(output_file_path) ############################################################################################################################################################## ############################################################################################################################################################## # UI CallBacks ############################################################################################################################################################## ############################################################################################################################################################## def SearchCallback(control, event): clipsetFilePath = RS.Config.Project.Path.Assets + "\\export\\data\\anim\\clip_sets\\clip_sets_groups\\" locoFilePath = RS.Config.Project.Path.Assets + "\\export\\data\\loco\\types\\human\\" motionsFilePath = RS.Config.Project.Path.Assets + "\\export\\anim\\motions\\@MECH_LOCO_M\\" searchSubString = t.searchEdit.Text clipsets = generateClipsetsSearchArray(clipsetFilePath, searchSubString) clipsets = SearchClipsetsForReferences(clipsets, locoFilePath, motionsFilePath) PrintResultsToTextFile(clipsets, searchSubString) ############################################################################################################################################################## # UI ############################################################################################################################################################## def PopulateTool(t): # ui variables for easier modification MAX_LIST_BOX_ENTRIES = 30 ui_border_x = 10 ui_border_width = 660 ui_border_height = 100 ui_row_offset = 10 ######################################################################## # Border x = FBAddRegionParam(ui_border_x, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_row_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_border_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_border_height - 5, FBAttachType.kFBAttachNone, "") t.AddRegion("border_a", "", x, y, w, h) t.SetBorder("border_a", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ######################################################################## ui_row_offset += 5 # Search Label x = FBAddRegionParam(ui_border_x + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_row_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_border_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("search_region", "search_region", x, y, w, h) t.searchLable = FBLabel() t.SetControl("search_region", t.searchLable) t.searchLable.Justify = FBTextJustify.kFBTextJustifyLeft t.searchLable.Visible = True t.searchLable.ReadOnly = False t.searchLable.Enabled = True t.searchLable.Caption = "Clipset Search Substring" # Search Edit ui_row_offset += 20 x = FBAddRegionParam(ui_border_x + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_row_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_border_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("search_edit", "search_edit", x, y, w, h) t.searchEdit = FBEdit() t.searchEdit.Text = 'CLIPSET@MECH_LOCO_M@GENERIC@EMOTION@UNARMED@NERVOUS' t.SetControl("search_edit", t.searchEdit) ######################################################################## # Search Button ui_row_offset += 30 x = FBAddRegionParam(ui_border_x + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_row_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_border_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(30, FBAttachType.kFBAttachNone, "") t.AddRegion("rename_button", "rename_button", x, y, w, h) t.searchButton = FBButton() t.searchButton.Caption = "Search Usage of Clipsets (and variants) in Loco and Motions files" t.SetControl("rename_button", t.searchButton) # callback t.searchButton.OnClick.Add(SearchCallback) ######################################################################## # draw tool ######################################################################## ui_row_offset += 60 # set tool ui size t.StartSizeX = 700 t.StartSizeY = ui_row_offset + 30 #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Clipset Search Loco and Motions") PopulateTool(t) ShowTool(t) CreateTool() ‰PNG #################################################################### # Clipset utility script # # Initial function is to create a list of unreferenced clip dictionary metadatas #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import xml.etree.ElementTree as ET import tempfile import os import RS.Config def prettyPrint(elem, level=0): i = "\n" + level * "\t" if len( elem ): if not elem.text or not elem.text.strip(): elem.text = i + "\t" if not elem.tail or not elem.tail.strip(): elem.tail = i for elem in elem: prettyPrint(elem, level+1) if not elem.tail or not elem.tail.strip(): elem.tail = i else: if level and (not elem.tail or not elem.tail.strip()): elem.tail = i def chooseClipSet(): # Create the popup and set necessary initial values. lFp = FBFilePopup() lFp.Caption = "Choose ClipSet to import" lFp.Style = FBFilePopupStyle.kFBFilePopupOpen # BUG: If we do not set the filter, we will have an exception. lFp.Filter = "*.meta" # Set the default path. lFp.Path = RS.Config.Project.Path.Assets + "\\export\\data\\anim\\clip_sets\\clip_sets_groups\\" # Get the GUI to show. lRes = lFp.Execute() # If we select files, show them, otherwise indicate that the selection was canceled. if lRes: return lFp else: return False #################################################################### # UI & Callbacks #################################################################### def addConditionButtonCallback(control, event): pass def clipsetButtonCallBack(control, event): loadAndValidateClipSet() def PopulateTool( t ): ########################################################################################## # streaming and memory groups defines - ideally these should be loaded in from a meta file... # but we'll hardcode them for now. ########################################################################################## t.MAX_NUM_OF_CONDITIONS = 6 streaming_policy_list = [ 'SP_STREAMING', 'SP_SINGLEPLAYER_RESIDENT SP_MULTIPLAYER_RESIDENT' ] streaming_policy_list_default = 0 # SP_STREAMING memory_group_list = [ 'MG_AmbientMovement', 'MG_CivilianReactions', 'MG_Climbs','MG_Combat', 'MG_CoreMovement', 'MG_Cover', 'MG_Creature', 'MG_CreatureHorse', 'MG_Dropdowns', 'MG_DoorInteraction','MG_EmotionalMovement', 'MG_Facial', 'MG_Falls','MG_Gesture', 'MG_Getups', 'MG_Jumps','MG_Loot', 'MG_Melee', 'MG_Misc', 'MG_None', 'MG_Parachuting', 'MG_PlayerMovement', 'MG_ScenariosBase', 'MG_ScenariosStreamed', 'MG_Script', 'MG_ShockingEventReactions', 'MG_Swimming', 'MG_VaultingClimbingJumping', 'MG_Vehicle', 'MG_Weapons' ] memory_group_list_default = 13 # MG_Misc streaming_priority_list = [ 'SP_LOW', 'SP_MEDIUM', 'SP_HIGH' ] streaming_priority_list_default = 1 # SP_MEDIUM ########################################################################################## # UI variables ########################################################################################## # ui variables for easier modification ui_border_x = 10 ui_border_y = 10 ui_border_width = 500 ui_border_height = 100 ui_x_offset = 10 ui_y_offset = 10 ui_x_width = 800 ui_button_width = 30 ui_entry_height = 30 ui_condition_gap = 5 ui_condition_width = 500 ui_condition_height = 20 ui_dropdown_width = 300 ui_memory_group_dropdown_width = 150 ui_clipset_button_width = ui_x_offset + ui_condition_gap * 5 + ui_condition_width + ui_dropdown_width + ui_memory_group_dropdown_width * 2 t.dictionaryEdit = [] t.streamingPolicy = [] t.memoryGroup = [] t.streamingPriority = [] for condition_index in range( t.MAX_NUM_OF_CONDITIONS ): ########################################################################################## # Border ########################################################################################## condition_index_str = str( condition_index ) x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_offset + ui_condition_gap * 5 + ui_condition_width + ui_dropdown_width + ui_memory_group_dropdown_width * 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_entry_height,FBAttachType.kFBAttachNone,"") t.AddRegion("condition_border" + condition_index_str, "" , x, y, w, h) t.SetBorder("condition_border" + condition_index_str,FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ########################################################################################## # Dictionary Edit Box ########################################################################################## x = FBAddRegionParam(ui_x_offset + ui_condition_gap,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset + ui_condition_gap,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_condition_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_condition_height,FBAttachType.kFBAttachNone,"") t.AddRegion("dictionary_editbox" + condition_index_str,"dictionary_editbox" + condition_index_str , x, y, w, h) t.dictionaryEdit.append( FBEdit() ) t.dictionaryEdit[condition_index].Id = condition_index t.dictionaryEdit[condition_index].Visible = True t.dictionaryEdit[condition_index].ReadOnly = False t.dictionaryEdit[condition_index].Enabled = True t.dictionaryEdit[condition_index].Text = '' t.SetControl("dictionary_editbox" + condition_index_str, t.dictionaryEdit[condition_index]) #################################################################### # streamingPolicy dropdown #################################################################### x = FBAddRegionParam(ui_x_offset + ui_condition_gap * 2 + ui_condition_width,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset + ui_condition_gap,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_dropdown_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_condition_height,FBAttachType.kFBAttachNone,"") t.AddRegion("streamingpolicy_dropdown" + condition_index_str,"streamingpolicy_dropdown" + condition_index_str , x, y, w, h) t.streamingPolicy.append( FBList() ) t.streamingPolicy[condition_index].Style = FBListStyle.kFBDropDownList for policy in streaming_policy_list: t.streamingPolicy[condition_index].Items.append( policy ) t.SetControl("streamingpolicy_dropdown" + condition_index_str, t.streamingPolicy[condition_index]) #### Default is SP_STREAMING #### t.streamingPolicy[condition_index].ItemIndex = streaming_policy_list_default #################################################################### # memorygroup dropdown #################################################################### x = FBAddRegionParam(ui_x_offset + ui_condition_gap * 3 + ui_condition_width + ui_dropdown_width,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset + ui_condition_gap,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_memory_group_dropdown_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_condition_height,FBAttachType.kFBAttachNone,"") t.AddRegion("memorygroup_dropdown" + condition_index_str,"memorygroup_dropdown" + condition_index_str , x, y, w, h) t.memoryGroup.append( FBList() ) t.memoryGroup[condition_index].Style = FBListStyle.kFBDropDownList for group in memory_group_list: t.memoryGroup[condition_index].Items.append( group ) t.SetControl("memorygroup_dropdown" + condition_index_str, t.memoryGroup[condition_index]) #### Default is #### t.memoryGroup[condition_index].ItemIndex = memory_group_list_default #################################################################### # streaming priority dropdown #################################################################### x = FBAddRegionParam(ui_x_offset + ui_condition_gap * 4 + ui_condition_width + ui_dropdown_width + ui_memory_group_dropdown_width,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset + ui_condition_gap,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_memory_group_dropdown_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_condition_height,FBAttachType.kFBAttachNone,"") t.AddRegion("streamingpriority_dropdown" + condition_index_str,"streamingpriority_dropdown" + condition_index_str , x, y, w, h) t.streamingPriority.append( FBList() ) t.streamingPriority[condition_index].Style = FBListStyle.kFBDropDownList for priority in streaming_priority_list: t.streamingPriority[condition_index].Items.append( priority ) t.SetControl("streamingpriority_dropdown" + condition_index_str, t.streamingPriority[condition_index]) #### Default is SP_MEDIUM #### t.streamingPriority[condition_index].ItemIndex = streaming_priority_list_default #################################################################### # increment UI condition #################################################################### ui_y_offset += ui_entry_height + ui_condition_gap #################################################################### # Choose Clipset and Validate Button #################################################################### ui_y_offset += 5 x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_clipset_button_width ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_entry_height,FBAttachType.kFBAttachNone,"") t.AddRegion("choose_clipset_border", "" , x, y, w, h) t.SetBorder("choose_clipset_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) t.clipset_button = FBButton() t.SetControl("choose_clipset_border", t.clipset_button) t.clipset_button.Visible = True t.clipset_button.ReadOnly = False t.clipset_button.Enabled = True t.clipset_button.Hint = "" t.clipset_button.Caption = "Choose Clipset File and Validate" t.clipset_button.State = 0 t.clipset_button.Style = FBButtonStyle.kFBPushButton t.clipset_button.Justify = FBTextJustify.kFBTextJustifyCenter t.clipset_button.Look = FBButtonLook.kFBLookNormal t.clipset_button.OnClick.Add( clipsetButtonCallBack ) #################################################################### # Enter some defaults (for male loco really) #################################################################### t.dictionaryEdit[0].Text = 'MECH_LOCO_M@CHARACTER@ARTHUR@' t.streamingPolicy[0].ItemIndex = 0 t.memoryGroup[0].ItemIndex = 16 t.streamingPriority[0].ItemIndex = 1 t.dictionaryEdit[1].Text = 'MECH_LOCO_M@CHARACTER@' t.streamingPolicy[1].ItemIndex = 0 t.memoryGroup[1].ItemIndex = 0 t.streamingPriority[1].ItemIndex = 1 t.dictionaryEdit[2].Text = 'MECH_LOCO_M@GENERIC@EMOTION@' t.streamingPolicy[2].ItemIndex = 0 t.memoryGroup[2].ItemIndex = 7 t.streamingPriority[2].ItemIndex = 1 t.dictionaryEdit[3].Text = 'MECH_LOCO_M@TYPE@' t.streamingPolicy[3].ItemIndex = 0 t.memoryGroup[3].ItemIndex = 0 t.streamingPriority[3].ItemIndex = 1 t.dictionaryEdit[ t.MAX_NUM_OF_CONDITIONS - 1 ].Text = 'Default for any clip dictionaries not matching above rules.' t.dictionaryEdit[ t.MAX_NUM_OF_CONDITIONS - 1 ].Enabled = False t.streamingPolicy[t.MAX_NUM_OF_CONDITIONS - 1 ].ItemIndex = 0 t.memoryGroup[t.MAX_NUM_OF_CONDITIONS - 1 ].ItemIndex = 3 t.streamingPriority[t.MAX_NUM_OF_CONDITIONS - 1 ].ItemIndex = 1 #################################################################### # Show UI #################################################################### ui_y_offset += ui_entry_height t.StartSizeX = ui_x_offset + ui_condition_gap * 5 + ui_condition_width + ui_dropdown_width + ui_memory_group_dropdown_width * 2 + 35 t.StartSizeY = ui_y_offset + 45 ShowTool(t) def loadAndValidateClipSet(): result = chooseClipSet() #################################################################### # Open XML file #################################################################### if result: xml_file = ( result.Path + "\\" + result.FileName ) tree = ET.parse( xml_file ) root = tree.getroot() clip_dictionary_names_list = {} clip_dictionary_metadatas_list = {} clipSetsElement = root.find('ClipSets') clipDictionaryMetadatasElement = root.find('ClipDictionaryMetadatas') #################################################################### # Populate existing entries into dictionaries #################################################################### for clip_dictionary in clipSetsElement.iter('DictionaryIds'): for item in clip_dictionary.iter('Item'): # add to list if not already stored - not bothered about value if item.text.upper() not in clip_dictionary_names_list: clip_dictionary_names_list[item.text.upper()] = 0 for clip_dictionary_metadata in clipDictionaryMetadatasElement.iter('Item'): # add to list if not already stored - not bothered about value if clip_dictionary_metadata.attrib['key'].upper() not in clip_dictionary_metadatas_list: clip_dictionary_metadatas_list[ clip_dictionary_metadata.attrib['key'].upper() ] = 0 #################################################################### # Compare clip dictionary list with clip dictionary metadatas # anything missing requires a metadata definition #################################################################### new_clip_dictionaries = ET.SubElement( root, 'These Clip Dictionaries are not referenced in clipDictionaryMetadatas. Paste the following into clipsets file.' ) for clip_dictionary in clip_dictionary_names_list: if clip_dictionary not in clip_dictionary_metadatas_list: # add clipdictionary element new_entry = ET.SubElement( new_clip_dictionaries, 'Item') new_entry.set('key', clip_dictionary) #################################################################### # memoryGroup # uses folder location to choose MG #################################################################### new_sub_entry = ET.SubElement( new_entry, 'MemoryGroup') string_found = False matching_char_length = 0 for condition_index in range( t.MAX_NUM_OF_CONDITIONS - 1): if not t.dictionaryEdit[condition_index].Text == '': if t.dictionaryEdit[condition_index].Text in clip_dictionary: if len( clip_dictionary ) > matching_char_length: new_sub_entry.text = t.memoryGroup[condition_index].Items[ t.memoryGroup[condition_index].ItemIndex ] string_found = True matching_char_length = len( clip_dictionary ) if string_found == False: new_sub_entry.text = t.memoryGroup[ t.MAX_NUM_OF_CONDITIONS - 1 ].Items[ t.memoryGroup[ t.MAX_NUM_OF_CONDITIONS - 1 ].ItemIndex ] prettyPrint( new_clip_dictionaries ) #################################################################### # Compare clip metadata list with clip dictionaries # anything unreferenced requires deleting #################################################################### unused_metadata_dictionaries = ET.SubElement( root, 'These clipDictionaryMetadatas do not reference anything. Remove the following from the clipsets file.' ) unused_metadata = False for clip_dictionary_metadata in clip_dictionary_metadatas_list: if clip_dictionary_metadata not in clip_dictionary_names_list: print "================= UNUSED METADATA FOUND =================" print "name of clipset is " + str( clip_dictionary_metadata ) print "size of dictionary list is " + str( len( clip_dictionary_names_list ) ) for dictionary in clip_dictionary_names_list: print str( dictionary ) # add clipdictionary element new_entry = ET.SubElement( unused_metadata_dictionaries, 'Item') new_entry.set('key', clip_dictionary_metadata) unused_metadata = True prettyPrint( unused_metadata_dictionaries ) #################################################################### # Write new metadatas file #################################################################### output_file_path = tempfile.gettempdir() + '\\mj_ClipSetUtility_output.txt' with open( output_file_path , 'w') as f: output = ET.tostring( new_clip_dictionaries ) f.write( output ) if unused_metadata == True: with open( output_file_path , 'a') as f: output = '-----------------------------------------------------------------------------------------------------------\n' f.write( output ) output = ET.tostring( unused_metadata_dictionaries ) f.write( output ) os.startfile( output_file_path ) print "Saved to " + output_file_path else: print "Error loading clipset." #################################################################### # main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("ClipSet Utility") PopulateTool(t) CreateTool()from pyfbsdk import * from pyfbsdk_additions import * def clearSelection(): ''' Clear selection function ''' # Get selected models modelList = FBModelList () FBGetSelectedModels (modelList, None, True) # Deselect models for model in modelList: model.Selected = False #################################################################### # UI callbacks #################################################################### def goButtonCallBack(control, event): if t.pinned == False: ######################################## # Pin object ######################################## # create null t.null = FBModelNull('constraint_null') # get time span current_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() current_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() #---------------------------------------------------------------------------------- # loop for every frame - get pos/rot of selected object #---------------------------------------------------------------------------------- # lists for storing object pos and rotations trans_array = [] rot_array = [] for frame in range(current_start, current_end + 1): # update slider FBPlayerControl().Goto(FBTime(0,0,0,frame)) FBSystem().Scene.Evaluate() # get selected translation and rotation for object obj_trans = FBVector3d() obj_rot = FBVector3d() t.selected_obj.GetVector (obj_trans, FBModelTransformationType.kModelTranslation, True) t.selected_obj.GetVector (obj_rot, FBModelTransformationType.kModelRotation, True) FBSystem().Scene.Evaluate() # store translation/rotation trans_array.append(obj_trans) rot_array.append(obj_rot) # only select null clearSelection() t.null.Selected = True #---------------------------------------------------------------------------------- # loop for every frame - set pos/rot on null #---------------------------------------------------------------------------------- count = 0 for frame in range(current_start, current_end + 1): # update slider FBPlayerControl().Goto(FBTime(0,0,0,frame)) FBSystem().Scene.Evaluate() # set selected translation and rotation for object t.null.SetVector (trans_array[count], FBModelTransformationType.kModelTranslation, True) t.null.SetVector (rot_array[count], FBModelTransformationType.kModelRotation, True) FBSystem().Scene.Evaluate() # key null FBPlayerControl().Key() FBSystem().Scene.Evaluate() count += 1 #################################################################### # UI definition #################################################################### def PopulateTool(t): # get selected object models = FBModelList() FBGetSelectedModels( models ) if len(models) == 0: FBMessageBox( "Message", "Nothing selected", "OK", None, None ) else: t.selected_obj = models[0] t.selected_obj_name = models[0].Name #Pin button x = FBAddRegionParam(5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(5,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(240,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("go_button_region","go_button_region", x, y, w, h) t.pin_button = FBButton() t.SetControl("go_button_region", t.pin_button) t.pin_button.Visible = True t.pin_button.ReadOnly = False t.pin_button.Enabled = True t.pin_button.Hint = "" t.pin_button.Caption = "Plot & Pin '" + t.selected_obj_name + "'" t.pin_button.State = 0 t.pin_button.Style = FBButtonStyle.kFBPushButton t.pin_button.Justify = FBTextJustify.kFBTextJustifyCenter t.pin_button.Look = FBButtonLook.kFBLookNormal t.pin_button.OnClick.Add(goButtonCallBack) # set pin state t.pinned = False ShowTool(t) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Create Animated Null") t.StartSizeX = 265 t.StartSizeY = 75 PopulateTool(t) CreateTool() ‰PNG from pyfbsdk import * from pyfbsdk_additions import * def clearSelection(): ''' Clear selection function ''' # Get selected models modelList = FBModelList () FBGetSelectedModels (modelList, None, True) # Deselect models for model in modelList: model.Selected = False def getSelectedAuxPivot(): ''' goes through selected objects and returns first selected pivot ''' # Get selected models modelList = FBModelList () FBGetSelectedModels ( modelList ) for model in modelList: if model.Type is FBMarkerType.kFBMarkerTypeIKEffector: return model # clear all models clearSelection() # get current character and control set character = FBApplication().CurrentCharacter ########################################################################################### # left ankle effectors ########################################################################################### ''' # create heel result = character.CreateAuxiliary( FBEffectorId.kFBLeftAnkleEffectorId, True, 0.0, 0.0 ) if result: auxPivot = getSelectedAuxPivot() auxPivot.IKPivot = FBVector3d( 0.42, 9.44, 3.86) auxPivot.Look = FBMarkerLook.kFBMarkerLookLightCross auxPivot.Name = 'LeftAnkleHeelPivot' auxPivot.Selected = False ''' # create ball result = character.CreateAuxiliary( FBEffectorId.kFBLeftAnkleEffectorId, True, 0.0, 0.0 ) if result: auxPivot = getSelectedAuxPivot() auxPivot.IKPivot = FBVector3d( 3.50, 10.53, -15.10) auxPivot.Look = FBMarkerLook.kFBMarkerLookLightCross auxPivot.Name = 'LeftAnkleBallPivot' auxPivot.Selected = False ########################################################################################### # left ball effectors ########################################################################################### ''' # create ball result = character.CreateAuxiliary( FBEffectorId.kFBLeftFootEffectorId, True, 0.0, 0.0 ) if result: auxPivot = getSelectedAuxPivot() auxPivot.IKPivot = FBVector3d( 3.50, 4.11, -1.61) auxPivot.Look = FBMarkerLook.kFBMarkerLookLightCross auxPivot.Color = FBColor(0.0, 255.0, 0.0) auxPivot.Name = 'LeftFootBallPivot' auxPivot.Selected = False # create toe result = character.CreateAuxiliary( FBEffectorId.kFBLeftFootEffectorId, True, 0.0, 0.0 ) if result: auxPivot = getSelectedAuxPivot() auxPivot.IKPivot = FBVector3d( 2.03, -0.52, -11.88) auxPivot.Look = FBMarkerLook.kFBMarkerLookLightCross auxPivot.Color = FBColor(0.0, 255.0, 0.0) auxPivot.Name = 'LeftFootToePivot' auxPivot.Selected = False ''' ########################################################################################### # right ankle effectors ########################################################################################### ''' # create heel result = character.CreateAuxiliary( FBEffectorId.kFBRightAnkleEffectorId, True, 0.0, 0.0 ) if result: auxPivot = getSelectedAuxPivot() auxPivot.IKPivot = FBVector3d( -0.42, 9.44, 3.86) auxPivot.Look = FBMarkerLook.kFBMarkerLookLightCross auxPivot.Name = 'RightAnkleHeelPivot' auxPivot.Selected = False ''' # create ball result = character.CreateAuxiliary( FBEffectorId.kFBRightAnkleEffectorId, True, 0.0, 0.0 ) if result: auxPivot = getSelectedAuxPivot() auxPivot.IKPivot = FBVector3d( -3.65, 10.53, -15.10) auxPivot.Look = FBMarkerLook.kFBMarkerLookLightCross auxPivot.Name = 'RightAnkleBallPivot' auxPivot.Selected = False ########################################################################################### # right ball effectors ########################################################################################### ''' # create ball result = character.CreateAuxiliary( FBEffectorId.kFBRightFootEffectorId, True, 0.0, 0.0 ) if result: auxPivot = getSelectedAuxPivot() auxPivot.IKPivot = FBVector3d( -3.50, 4.11, -1.61) auxPivot.Look = FBMarkerLook.kFBMarkerLookLightCross auxPivot.Color = FBColor(0.0, 255.0, 0.0) auxPivot.Name = 'RightFootBallPivot' auxPivot.Selected = False # create toe result = character.CreateAuxiliary( FBEffectorId.kFBRightFootEffectorId, True, 0.0, 0.0 ) if result: auxPivot = getSelectedAuxPivot() auxPivot.IKPivot = FBVector3d( -2.03, -0.52, -11.88) auxPivot.Look = FBMarkerLook.kFBMarkerLookLightCross auxPivot.Color = FBColor(0.0, 255.0, 0.0) auxPivot.Name = 'RightFootToePivot' auxPivot.Selected = False '''‰PNG #################################################################### # Create a tracking camera for the current character #################################################################### from pyfbsdk import * def createCharacterTrackingCamera(): app = FBApplication() system = FBSystem() # get current character and namespace current_character = app.CurrentCharacter if current_character is not None: current_ns = current_character.OwnerNamespace.Name # create camera camera = FBCamera('Tracking Camera') if current_ns is not None: camera.ProcessObjectNamespace(FBNamespaceAction.kFBConcatNamespace, current_ns ) camera.Visible = True camera.Show = True # create parent null cameraParent = FBModelNull('Tracking Camera Parent') if current_ns is not None: cameraParent.ProcessObjectNamespace(FBNamespaceAction.kFBConcatNamespace, current_ns) camera.Parent = cameraParent # create parent constraint constraintManager = FBConstraintManager() # get index to parent/child constraint constraint_index = None for i in range(0, constraintManager.TypeGetCount()): if constraintManager.TypeGetName(i) == 'Position': constraint_index = i break # create constraint posConstraint = constraintManager.TypeCreateConstraint(constraint_index) posConstraint.Name = 'Tracking Camera Position Constraint' if current_ns is not None: posConstraint.ProcessObjectNamespace(FBNamespaceAction.kFBConcatNamespace, current_ns) moverObject = FBFindModelByLabelName(current_ns + ':mover') if moverObject is not None: # add ref objects for i in range(0, posConstraint.ReferenceGroupGetCount()): if posConstraint.ReferenceGroupGetName(i) == 'Source': posConstraint.ReferenceAdd(i, moverObject ) elif posConstraint.ReferenceGroupGetName(i) == 'Constrained Object': posConstraint.ReferenceAdd(i, cameraParent ) # snap constraint posConstraint.Snap() # switch camera to tracking cam app.SwitchViewerCamera(camera) createCharacterTrackingCamera() ‰PNG from pyfbsdk import * from pyfbsdk_additions import * def DeleteAnimationOnSelectedObjects(): # get selected objects selected_objs = FBModelList() FBGetSelectedModels( selected_objs ) if len( selected_objs ) > 0: user_choice = FBMessageBox( "Message", "Delete all animation on selected objects across all takes?", "Yes", "No" ) if ( user_choice == 1 ): # make sure we're in selection mode not fullbody! Otherwise it will delete all animation on a character even if only one bone is selected FBApplication().CurrentCharacter.KeyingMode = FBCharacterKeyingMode.kFBCharacterKeyingSelection # for each take delete animation for take in FBSystem().Scene.Takes: FBSystem().CurrentTake = take FBSystem().Scene.Evaluate() take.ClearAllProperties( True ) print "All animation deleted on selected objects" else: FBMessageBox( "Message", "No objects selected", "OK" ) DeleteAnimationOnSelectedObjects()‰PNG from pyfbsdk import * from pyfbsdk_additions import * # Clear all auxiliary on given effector def ClearAuxEffectors(pCharacter, pEffectorId): lAuxToClear = [] for lSetId in FBEffectorSetID.values.values(): # Default set should not be deleted. if lSetId == FBEffectorSetID.FBEffectorSetDefault: continue lAuxEffector = pCharacter.GetEffectorModel(pEffectorId,lSetId) if lAuxEffector: lAuxToClear.append(lAuxEffector) else: break # Clear the list for lModel in reversed(lAuxToClear): lModel.FBDelete() user_response = FBMessageBox( "Warning", "Are you sure you want to delete all aux foot pivots?", "Yes", "No" ) if user_response == 1: character = FBApplication().CurrentCharacter ClearAuxEffectors( character, FBEffectorId.kFBLeftAnkleEffectorId ) ClearAuxEffectors( character, FBEffectorId.kFBLeftFootEffectorId ) ClearAuxEffectors( character, FBEffectorId.kFBRightAnkleEffectorId ) ClearAuxEffectors( character, FBEffectorId.kFBRightFootEffectorId ) print "All aux pivots deleted!" else: print "Cancelled!" ‰PNG #################################################################### # searches for bad xml formatting with respect to conditions #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import xml.etree.ElementTree as ET import tempfile import os import RS.Config def checkForBadConditions(filepath, output, parent): children = parent.getchildren() for child in children: if (child.tag == 'Condition') and (parent.tag =='Conditions'): output += ">> " + filepath + " at line number " + str(child._start_line_number) + "\n" #print output # does child contain more elements? if len(child.getchildren()) > 0: output = checkForBadConditions(filepath, output, child) return output #################################################################### # custom class so we can find line numbers # from: http://stackoverflow.com/questions/6949395/is-there-a-way-to-get-a-line-number-from-an-elementtree-element #################################################################### class LineNumberingParser(ET.XMLParser): def _start_list(self, *args, **kwargs): # Here we assume the default XML parser which is expat # and copy its element position attributes into output Elements element = super(self.__class__, self)._start_list(*args, **kwargs) element._start_line_number = self.parser.CurrentLineNumber element._start_column_number = self.parser.CurrentColumnNumber element._start_byte_index = self.parser.CurrentByteIndex return element def _end(self, *args, **kwargs): element = super(self.__class__, self)._end(*args, **kwargs) element._end_line_number = self.parser.CurrentLineNumber element._end_column_number = self.parser.CurrentColumnNumber element._end_byte_index = self.parser.CurrentByteIndex return element #################################################################### # main #################################################################### # loco meta path rootdir = RS.Config.Project.Path.Assets + "\\export\\data\\loco\\" # output string output = "############################################################################################################################\n" output += "# Bad Conditions xml formatting results\n" output += "############################################################################################################################\n\n" ############################################################## # Loop through each nested file in the locoelement meta structure ############################################################## for subdir, dirs, files in os.walk(rootdir): for file in files: filepath = subdir + os.sep + file ############################################################## # only search in meta files ############################################################## if filepath.endswith(".meta"): tree = ET.parse( filepath, parser=LineNumberingParser() ) root = tree.getroot() ############################################################## # search in in each file ############################################################## conditionElement = root.find('Condition') if conditionElement: output = checkForBadConditions(filepath, output, conditionElement) ############################################################## # search in in each file ############################################################## conditionElement = root.find('Motions') if conditionElement: output = checkForBadConditions(filepath, output, conditionElement) ############################################################## # Save out results and open file ############################################################## output_file_path = tempfile.gettempdir() + '\\mj_LocoMetaSearch_output.txt' with open(output_file_path,'w') as f: f.write( output ) f.closed os.startfile( output_file_path ) ‰PNG #################################################################### # searches for bad xml formatting with respect to conditions #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import xml.etree.ElementTree as ET import tempfile import os import RS.Config def isFileAMotionType( root ): if root.tag == 'Item': for attribute_tag in root.attrib: if root.attrib[ attribute_tag ] == 'Loco__MotionType': return True return False def findMissingController(filepath, output, parent ): children = parent.getchildren() isControllerFound = False for child in children: if (child.tag == 'UsesController'): isControllerFound = True if not isControllerFound: output += ">> " + filepath + "\n" return output def findNonMotionLoco(filepath, output, parent, loco_motion_type): children = parent.getchildren() attribute_tag = 'type' search_tag = 'MotionSet' for child in children: if (child.tag == 'Item'): if attribute_tag in child.attrib: if child.attrib[ attribute_tag ] == loco_motion_type: subelements = child.getchildren() isTagFound = False for elem in subelements: if elem.tag == search_tag: isTagFound = True # does Motion contain a MotionSet attribute? if it doesn't it mustn't be on motions yet if not isTagFound: output += ">> " + filepath + " at line number " + str(child._start_line_number) + "\n" return output #################################################################### # custom class so we can find line numbers # from: http://stackoverflow.com/questions/6949395/is-there-a-way-to-get-a-line-number-from-an-elementtree-element #################################################################### class LineNumberingParser(ET.XMLParser): def _start_list(self, *args, **kwargs): # Here we assume the default XML parser which is expat # and copy its element position attributes into output Elements element = super(self.__class__, self)._start_list(*args, **kwargs) element._start_line_number = self.parser.CurrentLineNumber element._start_column_number = self.parser.CurrentColumnNumber element._start_byte_index = self.parser.CurrentByteIndex return element def _end(self, *args, **kwargs): element = super(self.__class__, self)._end(*args, **kwargs) element._end_line_number = self.parser.CurrentLineNumber element._end_column_number = self.parser.CurrentColumnNumber element._end_byte_index = self.parser.CurrentByteIndex return element #################################################################### # main #################################################################### # loco meta path rootdir = RS.Config.Project.Path.Assets + "\\export\\data\\loco\\types\\human\\" # output strings missing_controller_output = "############################################################################################################################\n" missing_controller_output += "# Motiontypes with no specified controller \n" missing_controller_output += "############################################################################################################################\n\n" loop_output = "############################################################################################################################\n" loop_output += "# Loco Loops not on Motions \n" loop_output += "############################################################################################################################\n\n" idle_turn_output = "############################################################################################################################\n" idle_turn_output += "# Loco Idle Turns not on Motions \n" idle_turn_output += "############################################################################################################################\n\n" stop_output = "############################################################################################################################\n" stop_output += "# Loco Stops not on Motions \n" stop_output += "############################################################################################################################\n\n" start_output = "############################################################################################################################\n" start_output += "# Loco Starts not on Motions \n" start_output += "############################################################################################################################\n\n" moving_turn_output = "############################################################################################################################\n" moving_turn_output += "# Loco Moving Turns not on Motions \n" moving_turn_output += "############################################################################################################################\n\n" ############################################################## # Loop through each nested file in the locoelement meta structure ############################################################## for subdir, dirs, files in os.walk(rootdir): for file in files: filepath = subdir + os.sep + file ############################################################## # only search in meta files ############################################################## if filepath.endswith(".meta"): tree = ET.parse( filepath, parser=LineNumberingParser() ) root = tree.getroot() ############################################################## # search in in each file for missing motions ############################################################## if isFileAMotionType( root ): missing_controller_output = findMissingController(filepath, missing_controller_output, root ) ############################################################## # search in in each file for missing motions ############################################################## conditionElement = root.find('Motions') if conditionElement: loop_output = findNonMotionLoco(filepath, loop_output, conditionElement, 'Loco__MotionMoving') idle_turn_output = findNonMotionLoco(filepath, idle_turn_output, conditionElement, 'Loco__MotionIdleTurn') stop_output = findNonMotionLoco(filepath, stop_output, conditionElement, 'Loco__MotionStop') start_output = findNonMotionLoco(filepath, start_output, conditionElement, 'Loco__MotionStart') moving_turn_output = findNonMotionLoco(filepath, moving_turn_output, conditionElement, 'Loco__MotionMovingTurn') ############################################################## # Save out results and open file ############################################################## output = missing_controller_output + "\n" + loop_output + "\n" + idle_turn_output + "\n" + stop_output + "\n" + start_output + "\n" + moving_turn_output output_file_path = tempfile.gettempdir() + '\\mj_LocoMetaSearch_output.txt' with open(output_file_path,'w') as f: f.write( output ) f.closed os.startfile( output_file_path ) #################################################################### # searches for bad xml formatting with respect to conditions #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import xml.etree.ElementTree as ET import tempfile import os import RS.Config def checkForBadIdleSyncs(filepath, output, parent): children = parent.getchildren() loco_motion_type = 'Loco__MotionUpperBodyLoop' attribute_tag = 'type' gait_search_tags = ['MinGait','MaxGait'] gait_search_value = 'LG_IDLE' layer_search_tags = [ 'LayerSync' ] layer_search_value = 'FOOTSYNC' for child in children: if (child.tag == 'Item'): if attribute_tag in child.attrib: if child.attrib[ attribute_tag ] == loco_motion_type: # we have found a Loco__MotionUpperBodyLoop # does it contain a min or max gait with LG_IDLE? subelements = child.getchildren() isGaitFound = False for elem in subelements: for gait_search_tag in gait_search_tags: if elem.tag == gait_search_tag: # does Loco__MotionUpperBodyLoop contain an LG_Idle Gait? if elem.text.upper() == gait_search_value: isGaitFound = True if isGaitFound: # yes does the upperbody contain a layersync? isLayerSyncFound = False isLayerFootSync = False for elem in subelements: for layer_search_tag in layer_search_tags: if elem.tag == layer_search_tag: # yes isLayerSyncFound = True # does it contain Footsync? if elem.text.upper() == layer_search_value: isLayerFootSync = True # if Loco__MotionUpperBodyLoop doesn't specify a layersync OR has a footsync then it's bad! if not isLayerSyncFound or isLayerFootSync: output += ">> " + filepath + " at line number " + str(child._start_line_number) + "\n" return output #################################################################### # custom class so we can find line numbers # from: http://stackoverflow.com/questions/6949395/is-there-a-way-to-get-a-line-number-from-an-elementtree-element #################################################################### class LineNumberingParser(ET.XMLParser): def _start_list(self, *args, **kwargs): # Here we assume the default XML parser which is expat # and copy its element position attributes into output Elements element = super(self.__class__, self)._start_list(*args, **kwargs) element._start_line_number = self.parser.CurrentLineNumber element._start_column_number = self.parser.CurrentColumnNumber element._start_byte_index = self.parser.CurrentByteIndex return element def _end(self, *args, **kwargs): element = super(self.__class__, self)._end(*args, **kwargs) element._end_line_number = self.parser.CurrentLineNumber element._end_column_number = self.parser.CurrentColumnNumber element._end_byte_index = self.parser.CurrentByteIndex return element #################################################################### # main #################################################################### # loco meta path rootdir = RS.Config.Project.Path.Assets + "\\export\\data\\loco\\types\\human\\" # output string output = "############################################################################################################################\n" output += "# LG_IDLE LayerSync results\n" output += "# \n" output += "# These motions contain an upperbody loop which has an LG_Idle gait, and either specifies FootSync OR doesn't specify anything (which will default to FootSync)\n" output += "############################################################################################################################\n\n" ############################################################## # Loop through each nested file in the locoelement meta structure ############################################################## for subdir, dirs, files in os.walk(rootdir): for file in files: filepath = subdir + os.sep + file ############################################################## # only search in meta files ############################################################## if filepath.endswith(".meta"): tree = ET.parse( filepath, parser=LineNumberingParser() ) root = tree.getroot() ############################################################## # search in in each file ############################################################## conditionElement = root.find('Motions') if conditionElement: output = checkForBadIdleSyncs(filepath, output, conditionElement) ############################################################## # Save out results and open file ############################################################## output_file_path = tempfile.gettempdir() + '\\mj_LocoMetaSearch_output.txt' with open(output_file_path,'w') as f: f.write( output ) f.closed os.startfile( output_file_path ) ‰PNG from pyfbsdk import * from pyfbsdk_additions import * from ctypes import * import os import subprocess def launchWithoutConsole(command): CREATE_NO_WINDOW = 0x08000000 subprocess.call(command, creationflags=CREATE_NO_WINDOW) def GetCharacterAsRexMBModel( character ): current_character_root = character.GetModel(FBBodyNodeId.kFBHipsNodeId) rexMBModels = RexMBRagePython.GetModels() for rexMBModel in rexMBModels: if current_character_root.LongName == rexMBModel.GetRootName(): return rexMBModel def GetTakeAsRexMBTake( take_name ): RexMBTakes = RexMBRagePython.GetTakes() for i in range( len( FBSystem().Scene.Takes ) ): if take_name == RexMBTakes[ i ].GetName(): return RexMBTakes[take_count] def CalculateFootPlantFrames(): DEBUG = False ############################################################################################# # Method runs through every frame of current take, stores pos and velocities of foot bones, then works out which frames we have a foot plant # Returns two bool arrays in a tuple representing each frame for left foot and right foot respectively. ############################################################################################# left_model = t.left_foot_container.Model right_model = t.right_foot_container.Model # frame range take_start_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() take_end_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() left_model_pos = [] right_model_pos = [] left_model_vel = [] right_model_vel = [] left_plant = [] right_plant = [] inputPos = FBVector3d() frame_count = 0 ############################################################################################# # Store joint positions per frame ############################################################################################# for frame in range(take_start_frame, take_end_frame + 1): # update frame FBPlayerControl().Goto(FBTime(0, 0, 0, frame)) FBSystem().Scene.Evaluate() # get current foot transformations left_model.GetVector(inputPos, FBModelTransformationType.kModelTranslation, True) left_model_pos.append(FBVector3d(inputPos[0], inputPos[1], inputPos[2])) right_model.GetVector(inputPos, FBModelTransformationType.kModelTranslation, True) right_model_pos.append(FBVector3d(inputPos[0], inputPos[1], inputPos[2])) frame_count += 1 ############################################################################################# # Calc joint velocities pre frame ############################################################################################# for frame in range(frame_count): if frame == 0: # Initialise velocity arrays with zero on first frame # and set plants as False. left_model_vel.append( 0.0 ) right_model_vel.append( 0.0 ) left_plant.append(False) right_plant.append(False) else: left_model_vel.append(( left_model_pos[ frame ] - left_model_pos[ frame - 1 ] ).Length()) if DEBUG: print "left foot vel on frame " + str(frame) + " is " + str( left_model_vel[ frame ] ) right_model_vel.append(( right_model_pos[ frame ] - right_model_pos[ frame - 1 ] ).Length()) if DEBUG: print "right foot vel on frame " + str(frame) + " is " + str(right_model_vel[ frame ]) # magic numbers - probably want to expose in UI planting_velocity_threshold = 0.3 lifting_velocity_threshold = 0.6 left_foot_is_moving = False right_foot_is_moving = False # don't check first frame, we can't tell whether we've planted on frame zero for frame in range(1, frame_count): # check left foot if left_model_vel[frame] < planting_velocity_threshold and left_foot_is_moving: left_plant.append(True) left_foot_is_moving = False else: left_plant.append(False) if left_model_vel[frame] > lifting_velocity_threshold: left_foot_is_moving = True # check right foot if right_model_vel[ frame ] < planting_velocity_threshold and right_foot_is_moving: right_plant.append(True) right_foot_is_moving = False else: right_plant.append(False) if right_model_vel[ frame ] > lifting_velocity_threshold: right_foot_is_moving = True # return two bool arrays, access via tuple return left_plant, right_plant def AddFootTags( clip_path, footPlantFrames, clear_existing = True ): tools_root = os.environ['RS_TOOLSROOT'] # delete any existing foot tracks if clear_existing: launchWithoutConsole(tools_root + '/bin/anim/clipedit.exe -clip ' + clip_path + ' -ops removetag,Foot -out ' + clip_path) ############################################################################################# # left foot ############################################################################################# # [0] tuple entry is left foot count = 0 for frame in footPlantFrames[0]: if frame: phase = float( count ) / float( len(footPlantFrames[0]) ) launchWithoutConsole( tools_root + '/bin/anim/clipedit.exe -clip ' + clip_path + ' -ops addtag,Foot[].Heel=true;addtag,Foot[0].Right=false -out ' + clip_path ) launchWithoutConsole( tools_root + '/bin/anim/clipedit.exe -clip ' + clip_path + ' -ops edittag,Foot[0].startend=' + str( phase ) + ',' + str( phase ) + ' -out ' + clip_path ) count += 1 ############################################################################################# # right foot ############################################################################################# # [1] tuple entry is right foot count = 0 for frame in footPlantFrames[1]: if frame: phase = float( count ) / float( len(footPlantFrames[1]) ) launchWithoutConsole( tools_root + '/bin/anim/clipedit.exe -clip ' + clip_path + ' -ops addtag,Foot[].Heel=true;addtag,Foot[0].Right=true -out ' + clip_path) launchWithoutConsole( tools_root + '/bin/anim/clipedit.exe -clip ' + clip_path + ' -ops edittag,Foot[0].startend=' + str(phase) + ',' + str(phase) + ' -out ' + clip_path) count += 1 def IsFileWritable( path ): return os.access( path, os.W_OK ) def DoesFilePathExist( path ): return os.path.exists(path) def GetExportClipFileName(take_name): # first check for an alternate output name cdll.rexmbrage.GetTakeAltName_Py.restype = c_char_p output_name = cdll.rexmbrage.GetTakeAltName_Py( take_name ) # if no alternate is set then use take name if output_name == '': output_name = take_name # add any prefix/suffixes # current_character = FBApplication().CurrentCharacter # rexMBModel = GetCharacterAsRexMBModel( current_character ) #output_name = rexMBModel.GetAnimPrefix() + output_name + rexMBModel.GetAnimSuffix() return output_name def ReturnExportPathForCurrentTake(): current_take_name = FBSystem().CurrentTake.Name clip_path = '' # only interested in takes if they are marked for export if c_bool(cdll.rexMBRage.GetTakeExportSetting_Py( current_take_name )).value: clip_name = GetExportClipFileName(current_take_name) # if there's an alternate output path grab that, otherwise it will be using the default if c_int(cdll.rexMBRage.GetTakeOutputPathCount_Py( current_take_name )).value > 0: cdll.rexmbrage.GetTakeOutputPath_Py.restype = c_char_p clip_path = cdll.rexmbrage.GetTakeOutputPath_Py( current_take_name, 0 ) else: cdll.rexmbrage.GetExportPath_Py.restype = c_char_p clip_path = cdll.rexmbrage.GetExportPath_Py() if clip_path == '': return clip_path else: # create final name clip_path = clip_path + clip_name + '.clip' return clip_path def ValidatePath( path ): if path != '': if DoesFilePathExist( path ): if IsFileWritable( path): return True else: FBMessageBox("Error", "Clip file is not writable.", "Ok") else: FBMessageBox("Error", path + " does not appear on disk!", "Ok") else: FBMessageBox("Error","Export path has not been set in exporter for current take!", "Ok") return False def AutomateFootSteps(): path = ReturnExportPathForCurrentTake() if ValidatePath( path ): footPlantFrames = CalculateFootPlantFrames() AddFootTags( path, footPlantFrames ) FBMessageBox("Success", "Foot tags added to " + path, "Ok") #################################################################### # UI callbacks #################################################################### def EventContainerDragAndDrop(control, event): if event.State == FBDragAndDropState.kFBDragAndDropDrag: event.Accept() elif event.State == FBDragAndDropState.kFBDragAndDropDrop: for e in event.Components: # only allow certain types to be dropped if type(e) is pyfbsdk.FBModelNull or type(e) is pyfbsdk.FBModelSkeleton or type(e) is pyfbsdk.FBModelMarker or type(e) is pyfbsdk.FBModel: event.Accept() # update UI and store dropped model control.Items.removeAll() control.Items.append(e.LongName) control.Model = e if t.left_foot_container.Model and t.right_foot_container.Model: t.enabled = True t.applyButton.Enabled = True def applyButtonCallBack(control, event): AutomateFootSteps() #################################################################### # UI definition #################################################################### def PopulateTool(t): # UI constants ui_x_offset = 10 ui_y_offset = 5 ui_x_width = 400 ui_button_height = 30 ui_button_width = 40 ui_column_a = 20 ui_column_b = 100 ui_column_c = 100 t.enabled = True model = None ########################################################################################## # Right Foot Effector ########################################################################################## ui_y_offset += 10 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 20, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height + 10, FBAttachType.kFBAttachNone, "") t.AddRegion("right_foot_border", "Right Foot Effector/Joint (Drag to change)", x, y, w, h) t.SetBorder("right_foot_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ui_y_offset += 10 x = FBAddRegionParam(ui_x_offset + 10, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 40, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("right_foot_model", "right_foot_model", x, y, w, h) # set default model character = FBApplication().CurrentCharacter if character: model = FBFindModelByLabelName(character.OwnerNamespace.Name + ':SKEL_R_Toe0') t.right_foot_container = FBVisualContainer() if model: t.right_foot_container.Model = model t.right_foot_container.Items.append(t.right_foot_container.Model.LongName) else: t.right_foot_container.Model = None t.enabled = False t.right_foot_container.OnDragAndDrop.Add(EventContainerDragAndDrop) t.right_foot_container.ItemHeight = 20 t.right_foot_container.ItemWidth = ui_x_width - 45 t.SetControl("right_foot_model", t.right_foot_container) ui_y_offset += ui_button_height + 10 ########################################################################################## # Left Foot effector ########################################################################################## ui_y_offset += 10 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 20, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height + 10, FBAttachType.kFBAttachNone, "") t.AddRegion("left_foot_border", "Left Foot Effector/Joint (Drag to change)", x, y, w, h) t.SetBorder("left_foot_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ui_y_offset += 10 x = FBAddRegionParam(ui_x_offset + 10, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 40, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("left_foot_model", "left_foot_model", x, y, w, h) # set default model character = FBApplication().CurrentCharacter if character: model = FBFindModelByLabelName(character.OwnerNamespace.Name + ':SKEL_L_Toe0') t.left_foot_container = FBVisualContainer() if model: t.left_foot_container.Model = model t.left_foot_container.Items.append(t.left_foot_container.Model.LongName) else: t.right_foot_container.Model = None t.enabled = False t.left_foot_container.OnDragAndDrop.Add(EventContainerDragAndDrop) t.left_foot_container.ItemHeight = 20 t.left_foot_container.ItemWidth = ui_x_width - 45 t.SetControl("left_foot_model", t.left_foot_container) ########################################################################################## # Apply Button ########################################################################################## ui_y_offset += 40 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 20, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(25, FBAttachType.kFBAttachNone, "") t.AddRegion("apply_button", "apply_button", x, y, w, h) t.applyButton = FBButton() t.SetControl("apply_button", t.applyButton) t.applyButton.Visible = True t.applyButton.ReadOnly = False t.applyButton.Enabled = t.enabled t.applyButton.Hint = "" t.applyButton.Caption = "Apply" t.applyButton.State = 0 t.applyButton.Style = FBButtonStyle.kFBPushButton t.applyButton.Justify = FBTextJustify.kFBTextJustifyCenter t.applyButton.Look = FBButtonLook.kFBLookNormal t.applyButton.OnClick.Add(applyButtonCallBack) ui_y_offset += ui_button_height + 30 ########################################################################################## # Calculate Tool Window Size and Draw ########################################################################################## t.StartSizeX = ui_x_width + ui_x_offset * 2 t.StartSizeY = ui_y_offset + 10 ShowTool(t) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("AutomateFootSteps") PopulateTool(t) CreateTool() ‰PNG #################################################################### # Allows user to lock selected character hat controller #################################################################### from pyfbsdk import * # get current character current_character = FBApplication().CurrentCharacter if current_character != None: hat_ctrl = None # get hat ctrl for ext in current_character.CharacterExtensions: if ext.Name == 'Hat_Rig': hat_ctrl = ext.GetExtensionObjectWithLabelName('CTRL_PH_Hat_Loc') break if hat_ctrl is None: print "Hat Ctrl Rig not found for current character!" else: # toggle translation/rotation dofs on or off bTranslationDOF = hat_ctrl.PropertyList.Find("Enable Translation DOF").Data bRotationDOF = hat_ctrl.PropertyList.Find("Enable Rotation DOF").Data hat_ctrl.PropertyList.Find("Enable Translation DOF").Data = not bTranslationDOF hat_ctrl.PropertyList.Find("Enable Rotation DOF").Data = not bRotationDOF hat_ctrl.PropertyList.Find("TranslationMinX").Data = not bTranslationDOF hat_ctrl.PropertyList.Find("TranslationMinY").Data = not bTranslationDOF hat_ctrl.PropertyList.Find("TranslationMinZ").Data = not bTranslationDOF hat_ctrl.PropertyList.Find("TranslationMaxX").Data = not bTranslationDOF hat_ctrl.PropertyList.Find("TranslationMaxY").Data = not bTranslationDOF hat_ctrl.PropertyList.Find("TranslationMaxZ").Data = not bTranslationDOF hat_ctrl.PropertyList.Find("RotationMinX").Data = not bRotationDOF hat_ctrl.PropertyList.Find("RotationMinY").Data = not bRotationDOF hat_ctrl.PropertyList.Find("RotationMinZ").Data = not bRotationDOF hat_ctrl.PropertyList.Find("RotationMaxX").Data = not bRotationDOF hat_ctrl.PropertyList.Find("RotationMaxY").Data = not bRotationDOF hat_ctrl.PropertyList.Find("RotationMaxZ").Data = not bRotationDOF hat_ctrl.PropertyList.Find("TranslationMin").Data = FBVector3d() hat_ctrl.PropertyList.Find("TranslationMax").Data = FBVector3d() hat_ctrl.PropertyList.Find("RotationMin").Data = FBVector3d() hat_ctrl.PropertyList.Find("RotationMax").Data = FBVector3d() #################################################################### # Allows user to lock selected character extension components # from receiving keyframes #################################################################### from pyfbsdk import * from pyfbsdk_additions import * # Global UI components lock_button = FBButton() unlock_button = FBButton() radio_group = FBButtonGroup() roll_yes_button = FBButton() roll_no_button = FBButton() radio_group.Add(roll_yes_button) radio_group.Add(roll_no_button) status = FBLabel() button = [] char_extension_objs = [] char_rollbone_objs = [] def lockAllProperties( model ): #get namespace of selected character in dropdown chosenChar = t.list.Items[t.list.ItemIndex] strList = chosenChar.split(':') namespace = strList[0] modelList = FBComponentList() FBFindObjectsByName( namespace + ":" + model.Name, modelList, True, True ) if modelList: for p in modelList[0].PropertyList: if ( p.AllowsLocking() ): p.SetLocked(True) def unlockAllProperties( model ): #get namespace of selected character in dropdown chosenChar = t.list.Items[t.list.ItemIndex] strList = chosenChar.split(':') namespace = strList[0] modelList = FBComponentList() FBFindObjectsByName( namespace + ":" + model.Name, modelList, True, True ) if modelList: for p in modelList[0].PropertyList: if ( p.AllowsLocking() ): p.SetLocked(False) def getAllSceneCharacterRollBones( model ): components = model.Components for c in components: rs_prop = c.PropertyList.Find("rs_Type") if rs_prop: # only look at bones if rs_prop.Data == "rs_Bone": # and ignore skel bones if "SKEL" not in c.Name: print c.Name char_rollbone_objs.append( c ) #check down heirarchy if hasattr(c, 'Children'): for child in c.Children: getAllSceneCharacterRollBones( child ) def getAllSceneCharacterExtensions(): exts = FBSystem().Scene.CharacterExtensions for e in exts: components = e.Components for c in components: existing = False for ext in char_extension_objs: if c.Name == ext.Name: existing = True if not existing: char_extension_objs.append( c ) def getMoverNodes(): # get list of all models called 'mover' mover_list = FBComponentList() FBFindObjectsByName('mover', mover_list , False, True) FBFindObjectsByName('Mover', mover_list , False, True) FBFindObjectsByName('MOVER', mover_list , False, True) filtered_mover_list = [] chars = FBSystem().Scene.Characters # go through each model called mover and find which ones actually belong to characters # so we get rid of vehicles etc. # put result in new filtered_mover_list for object in mover_list: for char in chars: extensions = char.CharacterExtensions for e in extensions: components = e.Components for c in components: if c.LongName == object.LongName: filtered_mover_list.append(object) return filtered_mover_list def setupPropertyList(tool): tool.list.Items.removeAll() tool.prop_list = [] mover_list = getMoverNodes() if len(mover_list): for mover in mover_list: tool.list.Items.append(mover.LongName) else: tool.list.Items.append('No mover node found!') #################################################################### # Lock Button CallBack #################################################################### def lockButtonCallBack(control, event): index = 0 for obj in char_extension_objs: if ( button[index].State == True ): lockAllProperties( obj ) index += 1 if ( roll_yes_button.State == True ): for rb in char_rollbone_objs: lockAllProperties( rb ) print "Selected properties locked!" status.Caption = "Properties locked" FBSystem().Scene.Evaluate() #################################################################### # Unlock Button CallBack #################################################################### def unlockButtonCallBack(control, event): index = 0 for obj in char_extension_objs: if ( button[index].State == True ): unlockAllProperties( obj ) index += 1 if ( roll_yes_button.State == True ): for rb in char_rollbone_objs: unlockAllProperties( rb ) print "Selected properties unlocked!" status.Caption = "Properties unlocked" FBSystem().Scene.Evaluate() #################################################################### # UI #################################################################### def PopulateTool(t): # ui variables for easier modification ui_border_x = 10 ui_border_y = 10 ui_border_width = 140 ui_border_height = 100 ui_column_a = 20 ui_column_b = 60 ui_column_c = 100 ui_row_offset = 10 ui_button_width = 120 ui_button_height = 15 #Mover Selection---------------------------------------------- #Drop Down x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(25,FBAttachType.kFBAttachNone,"") t.AddRegion("dropdown","dropdown", x, y, w, h) t.list = FBList() t.SetControl("dropdown", t.list) setupPropertyList(t) #Extensions-------------------------------------------------- #get all scene character extension objects getAllSceneCharacterExtensions() ui_row_offset += 40 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( ( 20 * len(char_extension_objs) ) + 15,FBAttachType.kFBAttachNone,"") t.AddRegion("trans_border","Character Extensions", x, y, w, h) t.SetBorder("trans_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons # create a button for each extension obj for i in range( 0, len(char_extension_objs) ): region = "extButton" + str(i) x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop, region ) y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion(region,region, x, y, w, h) button.append( FBButton() ) t.SetControl(region, button[i] ) button[i].Visible = True button[i].ReadOnly = False button[i].Enabled = True button[i].Hint = "" button[i].Caption = char_extension_objs[i].Name # set certain options as off for ease of use if ( "mover" in char_extension_objs[i].Name ) or \ ( "PH_R_Hand" in char_extension_objs[i].Name ) or \ ( "PH_L_Hand" in char_extension_objs[i].Name ): button[i].State = 0 else: button[i].State = 1 button[i].Style = FBButtonStyle.kFBCheckbox button[i].Justify = FBTextJustify.kFBTextJustifyLeft button[i].Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 #Rollbones-------------------------------------------------- #get all scene character rollbone objects chosenChar = t.list.Items[t.list.ItemIndex] obj = FBFindModelByLabelName(chosenChar) if obj: getAllSceneCharacterRollBones( obj ) ui_row_offset += 20 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( 60 ,FBAttachType.kFBAttachNone,"") t.AddRegion("roll_bones","Include Rollbones?", x, y, w, h) t.SetBorder("roll_bones",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"roll_yes_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("roll_yes_button","roll_yes_button", x, y, w, h) t.SetControl("roll_yes_button", roll_yes_button ) roll_yes_button.Visible = True roll_yes_button.ReadOnly = False roll_yes_button.Enabled = True roll_yes_button.Hint = "" roll_yes_button.Caption = "Yes" roll_yes_button.State = 1 roll_yes_button.Style = FBButtonStyle.kFBRadioButton roll_yes_button.Justify = FBTextJustify.kFBTextJustifyLeft roll_yes_button.Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"roll_no_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("roll_no_button","roll_no_button", x, y, w, h) t.SetControl("roll_no_button", roll_no_button ) roll_no_button.Visible = True roll_no_button.ReadOnly = False roll_no_button.Enabled = True roll_no_button.Hint = "" roll_no_button.Caption = "No" roll_no_button.State = 0 roll_no_button.Style = FBButtonStyle.kFBRadioButton roll_no_button.Justify = FBTextJustify.kFBTextJustifyLeft roll_no_button.Look = FBButtonLook.kFBLookNormal #Lock Button-------------------------------------------------- ui_row_offset = ui_row_offset + 40 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"lock_button_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("lock_button_region","lock_button_region", x, y, w, h) t.SetControl("lock_button_region", lock_button) lock_button.Visible = True lock_button.ReadOnly = False lock_button.Enabled = True lock_button.Hint = "" lock_button.Caption = "Lock Selected" lock_button.State = 0 lock_button.Style = FBButtonStyle.kFBPushButton lock_button.Justify = FBTextJustify.kFBTextJustifyCenter lock_button.Look = FBButtonLook.kFBLookNormal lock_button.OnClick.Add(lockButtonCallBack) #Unlock Button-------------------------------------------------- ui_row_offset = ui_row_offset + 35 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"unlock_button_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("unlock_button_region","unlock_button_region", x, y, w, h) t.SetControl("unlock_button_region", unlock_button) unlock_button.Visible = True unlock_button.ReadOnly = False unlock_button.Enabled = True unlock_button.Hint = "" unlock_button.Caption = "Unlock Selected" unlock_button.State = 0 unlock_button.Style = FBButtonStyle.kFBPushButton unlock_button.Justify = FBTextJustify.kFBTextJustifyCenter unlock_button.Look = FBButtonLook.kFBLookNormal unlock_button.OnClick.Add(unlockButtonCallBack) #Status -------------------------------------------------- ui_row_offset = ui_row_offset + 35 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"status_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("status_region","status_region", x, y, w, h) t.SetControl("status_region", status) status.Visible = True status.ReadOnly = False status.Enabled = True status.Caption = "" #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Key Locker") t.StartSizeX = 175 t.StartSizeY = 575 PopulateTool(t) ShowTool(t) CreateTool() #################################################################### # Allows user to lock selected character extension components # from receiving keyframes #################################################################### from pyfbsdk import * from pyfbsdk_additions import * # Global UI components lock_button = FBButton() unlock_button = FBButton() #roll bones ui comps radio_group = FBButtonGroup() roll_yes_button = FBButton() roll_no_button = FBButton() radio_group.Add(roll_yes_button) radio_group.Add(roll_no_button) #face control ui comps fc_radio_group = FBButtonGroup() face_yes_button = FBButton() face_no_button = FBButton() fc_radio_group.Add(face_yes_button) fc_radio_group.Add(face_no_button) status = FBLabel() button = [] char_extension_objs = [] char_rollbone_objs = [] char_3lateral_objs = [] def lockAllProperties( obj ): if obj: for p in obj.PropertyList: if ( p.AllowsLocking() ): p.SetLocked(True) def unlockAllProperties( obj ): if obj: for p in obj.PropertyList: if ( p.AllowsLocking() ): p.SetLocked(False) def getAllSceneCharacterRollBones( model ): chosenChar = t.list.Items[t.list.ItemIndex] namespace = chosenChar.split(':') pattern = namespace[0] + '*' modelList = FBComponentList() FBFindObjectsByName( pattern , modelList ) for c in modelList: rs_prop = c.PropertyList.Find("rs_Type") if rs_prop: if rs_prop.Data == "rs_Bone": # and ignore skel bones if "SKEL" not in c.Name: char_rollbone_objs.append( c ) def getAllSceneFaceControls( model ): chosenChar = t.list.Items[t.list.ItemIndex] namespace = chosenChar.split(':') pattern = namespace[0] + '*' modelList = FBComponentList() FBFindObjectsByName( pattern , modelList ) for c in modelList: rs_prop = c.PropertyList.Find("rs_Type") if rs_prop: # only look at bones if "rs_3Lateral" in rs_prop.Data: char_3lateral_objs.append( c ) def getAllSceneCharacterExtensions(): exts = FBSystem().Scene.CharacterExtensions for e in exts: components = e.Components for c in components: existing = False for ext in char_extension_objs: if c.Name == ext.Name: existing = True if not existing: char_extension_objs.append( c ) def getMoverNodes(): # get list of all models called 'mover' mover_list = FBComponentList() FBFindObjectsByName('mover', mover_list , False, True) FBFindObjectsByName('Mover', mover_list , False, True) FBFindObjectsByName('MOVER', mover_list , False, True) filtered_mover_list = [] chars = FBSystem().Scene.Characters # go through each model called mover and find which ones actually belong to characters # so we get rid of vehicles etc. # put result in new filtered_mover_list for object in mover_list: for char in chars: extensions = char.CharacterExtensions for e in extensions: components = e.Components for c in components: if c.LongName == object.LongName: filtered_mover_list.append(object) return filtered_mover_list def setupPropertyList(tool): tool.list.Items.removeAll() tool.prop_list = [] mover_list = getMoverNodes() if len(mover_list): for mover in mover_list: tool.list.Items.append(mover.LongName) else: tool.list.Items.append('No mover node found!') #################################################################### # Lock Button CallBack #################################################################### def lockButtonCallBack(control, event): index = 0 for obj in char_extension_objs: if ( button[index].State == True ): lockAllProperties( obj ) index += 1 # if ( roll_yes_button.State == True ): # for rb in char_rollbone_objs: # lockAllProperties( rb ) if ( face_yes_button.State == True ): for fc in char_3lateral_objs: lockAllProperties( fc ) print "Selected properties locked!" status.Caption = "Properties locked" FBSystem().Scene.Evaluate() #################################################################### # Unlock Button CallBack #################################################################### def unlockButtonCallBack(control, event): index = 0 for obj in char_extension_objs: if ( button[index].State == True ): unlockAllProperties( obj ) index += 1 if ( roll_yes_button.State == True ): for rb in char_rollbone_objs: unlockAllProperties( rb ) if ( face_yes_button.State == True ): for fc in char_3lateral_objs: lockAllProperties( fc ) print "Selected properties unlocked!" status.Caption = "Properties unlocked" FBSystem().Scene.Evaluate() #################################################################### # UI #################################################################### def PopulateTool(t): # ui variables for easier modification ui_border_x = 10 ui_border_y = 10 ui_border_width = 140 ui_border_height = 100 ui_column_a = 20 ui_column_b = 60 ui_column_c = 100 ui_row_offset = 10 ui_button_width = 120 ui_button_height = 15 #Mover Selection---------------------------------------------- #Drop Down x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(25,FBAttachType.kFBAttachNone,"") t.AddRegion("dropdown","dropdown", x, y, w, h) t.list = FBList() t.SetControl("dropdown", t.list) setupPropertyList(t) #Extensions-------------------------------------------------- #get all scene character extension objects getAllSceneCharacterExtensions() ui_row_offset += 40 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( ( 20 * len(char_extension_objs) ) + 15,FBAttachType.kFBAttachNone,"") t.AddRegion("trans_border","Character Extensions", x, y, w, h) t.SetBorder("trans_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons # create a button for each extension obj for i in range( 0, len(char_extension_objs) ): region = "extButton" + str(i) x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop, region ) y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion(region,region, x, y, w, h) button.append( FBButton() ) t.SetControl(region, button[i] ) button[i].Visible = True button[i].ReadOnly = False button[i].Enabled = True button[i].Hint = "" button[i].Caption = char_extension_objs[i].Name # set certain options as off for ease of use if ( "mover" in char_extension_objs[i].Name ) or \ ( "PH_R_Hand" in char_extension_objs[i].Name ) or \ ( "PH_L_Hand" in char_extension_objs[i].Name ): button[i].State = 0 else: button[i].State = 1 button[i].Style = FBButtonStyle.kFBCheckbox button[i].Justify = FBTextJustify.kFBTextJustifyLeft button[i].Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 #Rollbones-------------------------------------------------- #get all scene character rollbone objects chosenChar = t.list.Items[t.list.ItemIndex] obj = FBFindModelByLabelName(chosenChar) if obj: getAllSceneCharacterRollBones( obj ) ui_row_offset += 20 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( 60 ,FBAttachType.kFBAttachNone,"") t.AddRegion("roll_bones","Include Rollbones?", x, y, w, h) t.SetBorder("roll_bones",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"roll_yes_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("roll_yes_button","roll_yes_button", x, y, w, h) t.SetControl("roll_yes_button", roll_yes_button ) roll_yes_button.Visible = True roll_yes_button.ReadOnly = False roll_yes_button.Enabled = False roll_yes_button.Hint = "" roll_yes_button.Caption = "Yes" roll_yes_button.State = 0 roll_yes_button.Style = FBButtonStyle.kFBRadioButton roll_yes_button.Justify = FBTextJustify.kFBTextJustifyLeft roll_yes_button.Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"roll_no_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("roll_no_button","roll_no_button", x, y, w, h) t.SetControl("roll_no_button", roll_no_button ) roll_no_button.Visible = True roll_no_button.ReadOnly = False roll_no_button.Enabled = False roll_no_button.Hint = "" roll_no_button.Caption = "No" roll_no_button.State = 1 roll_no_button.Style = FBButtonStyle.kFBRadioButton roll_no_button.Justify = FBTextJustify.kFBTextJustifyLeft roll_no_button.Look = FBButtonLook.kFBLookNormal #3Lateral-------------------------------------------------- #get all scene character face controller objects chosenChar = t.list.Items[t.list.ItemIndex] namespace = chosenChar.split(":") obj = FBFindModelByLabelName( namespace[0] + ':faceControls_OFF' ) if(obj): getAllSceneFaceControls( obj ) ui_row_offset += 40 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( 60 ,FBAttachType.kFBAttachNone,"") t.AddRegion("face_controls","Include Face Controls?", x, y, w, h) t.SetBorder("face_controls",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"face_yes_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("face_yes_button","face_yes_button", x, y, w, h) t.SetControl("face_yes_button", face_yes_button ) face_yes_button.Visible = True face_yes_button.ReadOnly = False face_yes_button.Enabled = True face_yes_button.Hint = "" face_yes_button.Caption = "Yes" face_yes_button.State = 1 face_yes_button.Style = FBButtonStyle.kFBRadioButton face_yes_button.Justify = FBTextJustify.kFBTextJustifyLeft face_yes_button.Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"face_no_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("face_no_button","face_no_button", x, y, w, h) t.SetControl("face_no_button", face_no_button ) face_no_button.Visible = True face_no_button.ReadOnly = False face_no_button.Enabled = True face_no_button.Hint = "" face_no_button.Caption = "No" face_no_button.State = 0 face_no_button.Style = FBButtonStyle.kFBRadioButton face_no_button.Justify = FBTextJustify.kFBTextJustifyLeft face_no_button.Look = FBButtonLook.kFBLookNormal #Lock Button-------------------------------------------------- ui_row_offset = ui_row_offset + 40 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"lock_button_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("lock_button_region","lock_button_region", x, y, w, h) t.SetControl("lock_button_region", lock_button) lock_button.Visible = True lock_button.ReadOnly = False lock_button.Enabled = True lock_button.Hint = "" lock_button.Caption = "Lock Selected" lock_button.State = 0 lock_button.Style = FBButtonStyle.kFBPushButton lock_button.Justify = FBTextJustify.kFBTextJustifyCenter lock_button.Look = FBButtonLook.kFBLookNormal lock_button.OnClick.Add(lockButtonCallBack) #Unlock Button-------------------------------------------------- ui_row_offset = ui_row_offset + 35 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"unlock_button_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("unlock_button_region","unlock_button_region", x, y, w, h) t.SetControl("unlock_button_region", unlock_button) unlock_button.Visible = True unlock_button.ReadOnly = False unlock_button.Enabled = True unlock_button.Hint = "" unlock_button.Caption = "Unlock Selected" unlock_button.State = 0 unlock_button.Style = FBButtonStyle.kFBPushButton unlock_button.Justify = FBTextJustify.kFBTextJustifyCenter unlock_button.Look = FBButtonLook.kFBLookNormal unlock_button.OnClick.Add(unlockButtonCallBack) #Status -------------------------------------------------- ui_row_offset = ui_row_offset + 35 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"status_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("status_region","status_region", x, y, w, h) t.SetControl("status_region", status) status.Visible = True status.ReadOnly = False status.Enabled = True status.Caption = "" #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Key Locker") t.StartSizeX = 175 t.StartSizeY = 610 PopulateTool(t) ShowTool(t) CreateTool() #################################################################### # Loco Meta Search utility script # # Allows user to search through the loco meta files and search child elements of certain element types #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import xml.etree.ElementTree as ET import tempfile import os import RS.Config #################################################################### # custom class so we can find line numbers # from: http://stackoverflow.com/questions/6949395/is-there-a-way-to-get-a-line-number-from-an-elementtree-element #################################################################### class LineNumberingParser(ET.XMLParser): def _start_list(self, *args, **kwargs): # Here we assume the default XML parser which is expat # and copy its element position attributes into output Elements element = super(self.__class__, self)._start_list(*args, **kwargs) element._start_line_number = self.parser.CurrentLineNumber element._start_column_number = self.parser.CurrentColumnNumber element._start_byte_index = self.parser.CurrentByteIndex return element def _end(self, *args, **kwargs): element = super(self.__class__, self)._end(*args, **kwargs) element._end_line_number = self.parser.CurrentLineNumber element._end_column_number = self.parser.CurrentColumnNumber element._end_byte_index = self.parser.CurrentByteIndex return element #################################################################### # main #################################################################### # outer scope to search search_scope_tag = 'Motions' # parent search attribute_tag = 'type' attribute_text = 'Loco__elementUpperBodyWeaponAiming' # child search child_tag = 'MoveNetwork' child_text = 'TaskelementHumanLocoUpperBodyMovingAndFacing_UserDefined' # loco meta path rootdir = RS.Config.Project.Path.Assets + "\\export\\data\\loco\\" # output string output = "############################################################################################################################\n" output += "# Search Results\n" output += "############################################################################################################################\n\n" ############################################################## # Loop through each nested file in the locoelement meta structure ############################################################## for subdir, dirs, files in os.walk(rootdir): for file in files: #print os.path.join(subdir, file) filepath = subdir + os.sep + file ############################################################## # only search in meta files ############################################################## if filepath.endswith(".meta"): tree = ET.parse( filepath, parser=LineNumberingParser() ) root = tree.getroot() ############################################################## # only search in the searchResults section of each meta file ############################################################## searchScopesElement = root.find( search_scope_tag ) if searchScopesElement: searchResults = searchScopesElement.getchildren() for element in searchResults: if element.tag == 'Item': ############################################################## # find items which match 'attribute_tag' and 'attribute_text' ############################################################## if attribute_tag in element.attrib: if element.attrib[ attribute_tag ] == attribute_text: ############################################################## # find child elements which match 'child_tag' and 'child_text' ############################################################## for element_child in element.getchildren(): if element_child.tag == child_tag: if element_child.text == child_text: # add filename (maybe line number) to output file output += ">> " + filepath + " at line number " + str( element_child._start_line_number ) + "\n" ############################################################## # Save out results and open file ############################################################## output_file_path = tempfile.gettempdir() + '\\mj_LocoMetaSearch_output.txt' with open(output_file_path,'w') as f: f.write( output ) f.closed os.startfile( output_file_path )‰PNG ###################################################################################################### # Mask Locker # Sets and prevents mask dofs from moving from their bind poses ###################################################################################################### from pyfbsdk import * import csv def getNodesByName( node_name ): # find every node with give node name object_list = FBComponentList() FBFindObjectsByName( node_name, object_list, False ) #if no nodes are found we error if len(object_list) == 0: FBMessageBox("Error!", name_name + " mask dof not found in scene!", "OK", None, None) else: return object_list def lockMaskDofs(): ###################################################################################################### # Bind Poses # Name, Local Translation, Local Rotation ###################################################################################################### mask_dofs = [ 'MH_Mask_R_Low', -0.047735, -0.069731, 0.153070, -0.000775, -0.000790, 0.000033, 'MH_Mask_R_Mid', -0.061445, -0.013301, 0.167160, -0.000767, -0.000800, 0.000025, 'MH_Mask_LowRoot', 0.000000, 0.000000, 0.000000, 17.339338, -4.168191, -1.793406, 'MH_Mask_R_Upp', -0.059794, 0.052800, 0.164775, -0.000901, -6.620632, 0.003551, 'MH_Mask_C_Low', -0.000001, -0.101321, 0.196037, -0.242461, -1.506590, -7.084754, 'MH_Mask_C_Mid', 0.057864, -0.013299, 0.167161, -0.000767, -0.000800, 0.000025, 'MH_Mask_L_Low', 0.046939, -0.069730, 0.153071, -0.000775, -0.000790, 0.000033, 'MH_Mask_L_Mid', 0.057864, -0.013299, 0.167161, -0.000767, -0.000800, 0.000025, 'MH_Mask_C_Upp', -0.000003, 0.064241, 0.212310, -0.009491, -0.002771, 0.002094, 'MH_Mask_L_Upp', 0.057261, 0.052801, 0.164777, -0.000776, -0.000803, 0.000099, 'MH_MaskRoot', -0.038081, -0.088986, 0.000692, 179.774673, -89.566566, 90.226219, 'MH_Mask_MidRoot', 0.000000, 0.000000, 0.000000, 31.861649, -5.225916, -3.338191, 'MH_Mask_UpRoot', 0.000000, 0.000000, 0.000000, 45.831963, -4.224141, -4.543106 ] num_of_columns = 7 num_of_dofs = len( mask_dofs ) / num_of_columns for index in range( 0, len( mask_dofs ), num_of_columns ): # get objects from name(since there may be more than one) node_list = getNodesByName( mask_dofs[index] ) # for each node set it to the desired translation/rotation and lock for node in node_list: # set min/max translation limits node.PropertyList.Find("TranslationMinX").Data = True node.PropertyList.Find("TranslationMinY").Data = True node.PropertyList.Find("TranslationMinZ").Data = True node.PropertyList.Find("TranslationMaxX").Data = True node.PropertyList.Find("TranslationMaxY").Data = True node.PropertyList.Find("TranslationMaxZ").Data = True node.PropertyList.Find("RotationMinX").Data = True node.PropertyList.Find("RotationMinY").Data = True node.PropertyList.Find("RotationMinZ").Data = True node.PropertyList.Find("RotationMaxX").Data = True node.PropertyList.Find("RotationMaxY").Data = True node.PropertyList.Find("RotationMaxZ").Data = True node.PropertyList.Find("TranslationMin").Data = FBVector3d( float(mask_dofs[index + 1]), float(mask_dofs[index + 2]), float(mask_dofs[index + 3]) ) node.PropertyList.Find("TranslationMax").Data = FBVector3d( float(mask_dofs[index + 1]), float(mask_dofs[index + 2]), float(mask_dofs[index + 3]) ) node.PropertyList.Find("RotationMin").Data = FBVector3d( float(mask_dofs[index + 4]), float(mask_dofs[index + 5]), float(mask_dofs[index + 6]) ) node.PropertyList.Find("RotationMax").Data = FBVector3d( float(mask_dofs[index + 4]), float(mask_dofs[index + 5]), float(mask_dofs[index + 6]) ) print "All mask dofs locked." lockMaskDofs()‰PNG from pyfbsdk import * # Merge all layers down to base FBSystem().CurrentTake.MergeLayers( FBAnimationLayerMergeOptions.kFBAnimLayerMerge_AllLayers_CompleteScene, False, FBMergeLayerMode.kFBMergeLayerModeAutomatic, False ) # make sure keys are set on the base layer FBSystem().CurrentTake.SetCurrentLayer(0) print "All layers merged!"‰PNG #################################################################### # Scales animation curves around the first frame on selected objects # Allows the user to dampen or exaggerate motion #################################################################### from pyfbsdk import * from pyfbsdk_additions import * def applyScaleToNodeKeys( node, scale ): fCurve = node.FCurve num_of_keys = len( fCurve.Keys ) # get pivot value pivot_value = fCurve.Keys[0].Value for key_index in range( num_of_keys ): difference_from_pivot = fCurve.Keys[key_index].Value - pivot_value scaled_difference = difference_from_pivot * scale fCurve.Keys[key_index].Value = pivot_value + scaled_difference #################################################################### # UI callbacks #################################################################### def applyButtonCallback(control, event): if t.isCached == False: t.transXCache = [] t.transYCache = [] t.transZCache = [] t.rotXCache = [] t.rotYCache = [] t.rotZCache = [] index = 0 for obj in t.selected_objects: # get animation nodes transX = obj.AnimationNode.Nodes[0].Nodes[0] transY = obj.AnimationNode.Nodes[0].Nodes[1] transZ = obj.AnimationNode.Nodes[0].Nodes[2] rotX = obj.AnimationNode.Nodes[1].Nodes[0] rotY = obj.AnimationNode.Nodes[1].Nodes[1] rotZ = obj.AnimationNode.Nodes[1].Nodes[2] # cache fcurves so we can reset if desired if t.isCached == False: t.transXCache.append(FBFCurve()) t.transYCache.append(FBFCurve()) t.transZCache.append(FBFCurve()) t.rotXCache.append(FBFCurve()) t.rotYCache.append(FBFCurve()) t.rotZCache.append(FBFCurve()) t.transXCache[index].KeyReplaceBy(transX.FCurve) t.transYCache[index].KeyReplaceBy(transY.FCurve) t.transZCache[index].KeyReplaceBy(transZ.FCurve) t.rotXCache[index].KeyReplaceBy(rotX.FCurve) t.rotYCache[index].KeyReplaceBy(rotY.FCurve) t.rotZCache[index].KeyReplaceBy(rotZ.FCurve) # scale applyScaleToNodeKeys(transX, t.translation_scale.Value) applyScaleToNodeKeys(transY, t.translation_scale.Value) applyScaleToNodeKeys(transZ, t.translation_scale.Value) applyScaleToNodeKeys(rotX, t.rotation_scale.Value) applyScaleToNodeKeys(rotY, t.rotation_scale.Value) applyScaleToNodeKeys(rotZ, t.rotation_scale.Value) index += 1 # enable reset t.isCached = True t.reset_button.Enabled = True FBSystem().Scene.Evaluate() def resetButtonCallback(control, event): index = 0 for obj in t.selected_objects: # get animation nodes transX = obj.AnimationNode.Nodes[0].Nodes[0] transY = obj.AnimationNode.Nodes[0].Nodes[1] transZ = obj.AnimationNode.Nodes[0].Nodes[2] rotX = obj.AnimationNode.Nodes[1].Nodes[0] rotY = obj.AnimationNode.Nodes[1].Nodes[1] rotZ = obj.AnimationNode.Nodes[1].Nodes[2] transX.FCurve.KeyReplaceBy(t.transXCache[index]) transY.FCurve.KeyReplaceBy(t.transYCache[index]) transZ.FCurve.KeyReplaceBy(t.transZCache[index]) rotX.FCurve.KeyReplaceBy(t.rotXCache[index]) rotY.FCurve.KeyReplaceBy(t.rotYCache[index]) rotZ.FCurve.KeyReplaceBy(t.rotZCache[index]) index += 1 t.translation_scale.Value = 1.0 t.rotation_scale.Value = 1.0 print "Reset FCurves" #FBSystem().Scene.Evaluate() #################################################################### # UI definition #################################################################### def PopulateTool(t): # ui defines ui_tool_width = 200 ui_tool_height_offset = 15 ui_width_gap = 10 ui_border_height = 50 ui_edit_box_height = 30 ui_apply_button_height = 30 ui_reset_button_height = 30 # Translation Border x = FBAddRegionParam(ui_width_gap, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_tool_height_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_tool_width - ui_width_gap * 4, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_border_height, FBAttachType.kFBAttachNone, "") t.AddRegion("trans_border", "Translation Scale", x, y, w, h) t.SetBorder("trans_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) # Translation Edit Box ui_tool_height_offset += 15 x = FBAddRegionParam(ui_width_gap * 2, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_tool_height_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_tool_width - ui_width_gap * 6, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_edit_box_height, FBAttachType.kFBAttachNone, "") t.AddRegion("translation_scale", "translation_scale", x, y, w, h) t.translation_scale = FBEditNumber() t.SetControl("translation_scale", t.translation_scale) t.translation_scale.Visible = True t.translation_scale.ReadOnly = False t.translation_scale.Enabled = True t.translation_scale.Value = 1.0 t.translation_scale.Hint = "multiplier to increase or decrease translation curves" t.translation_scale.SmallStep = 0.1 t.translation_scale.Min = 0.0 t.translation_scale.Max = 10.0 # Rotation Border ui_tool_height_offset += ui_edit_box_height + 10 x = FBAddRegionParam(ui_width_gap, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_tool_height_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_tool_width - ui_width_gap * 4, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_border_height, FBAttachType.kFBAttachNone, "") t.AddRegion("rot_border", "Rotation Scale", x, y, w, h) t.SetBorder("rot_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) # Rotation Edit Box ui_tool_height_offset += 15 x = FBAddRegionParam(ui_width_gap * 2, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_tool_height_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_tool_width - ui_width_gap * 6, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_edit_box_height, FBAttachType.kFBAttachNone, "") t.AddRegion("rotation_scale", "rotation_scale", x, y, w, h) t.rotation_scale = FBEditNumber() t.SetControl("rotation_scale", t.rotation_scale) t.rotation_scale.Visible = True t.rotation_scale.ReadOnly = False t.rotation_scale.Enabled = True t.rotation_scale.Value = 1.0 t.rotation_scale.Hint = "multiplier to increase or decrease rotation curves" t.rotation_scale.SmallStep = 0.1 t.rotation_scale.Min = 0.0 t.rotation_scale.Max = 10.0 # Apply Button ui_tool_height_offset += ui_edit_box_height + 15 x = FBAddRegionParam(ui_width_gap, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_tool_height_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_tool_width - ui_width_gap * 4, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_apply_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("apply_button", "apply_button", x, y, w, h) t.apply_button = FBButton() t.SetControl("apply_button", t.apply_button) t.apply_button.Visible = True t.apply_button.ReadOnly = False t.apply_button.Enabled = True t.apply_button.Hint = "Scale selected object's fcurves by % values above. First frame will be used as pivot point." t.apply_button.Caption = "Apply" t.apply_button.State = 0 t.apply_button.Style = FBButtonStyle.kFBPushButton t.apply_button.Justify = FBTextJustify.kFBTextJustifyCenter t.apply_button.Look = FBButtonLook.kFBLookNormal t.apply_button.OnClick.Add(applyButtonCallback) # Reset Button ui_tool_height_offset += ui_apply_button_height + 5 x = FBAddRegionParam(ui_width_gap, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_tool_height_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_tool_width - ui_width_gap * 4, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_reset_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("reset_button", "reset_button", x, y, w, h) t.reset_button = FBButton() t.SetControl("reset_button", t.reset_button) t.reset_button.Visible = True t.reset_button.ReadOnly = False t.reset_button.Enabled = False t.reset_button.Hint = "Reset selected objects back to their starting animation." t.reset_button.Caption = "Reset" t.reset_button.State = 0 t.reset_button.Style = FBButtonStyle.kFBPushButton t.reset_button.Justify = FBTextJustify.kFBTextJustifyCenter t.reset_button.Look = FBButtonLook.kFBLookNormal t.reset_button.OnClick.Add(resetButtonCallback) # ---------------------------------------------------------------- # show tool # ---------------------------------------------------------------- t.StartSizeX = ui_tool_width t.StartSizeY = ui_tool_height_offset + 85 t.isCached = False ShowTool(t) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Motion Scale") t.selected_objects = FBModelList() FBGetSelectedModels(t.selected_objects) if len(t.selected_objects) == 0: FBMessageBox("Message", "Nothing selected", "OK", None, None) else: PopulateTool(t) CreateTool() ‰PNG from pyfbsdk import * from pyfbsdk_additions import * from RS.Utils.ContextManagers import SceneExpressionsDisabled import xml.etree.ElementTree as ET import math import csv import tempfile import os.path def plotToSkeleton( character ): character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton, plotOptions() ) def calcAV(AvVel, currentDropDownIndex): multiplier = float(t.angularVelMultiplier[currentDropDownIndex]) y = float(t.y_addition[currentDropDownIndex]) if t.large_turn_button.State == 1: return (multiplier * AvVel + y)/2 else: return (multiplier * AvVel + y) def FlattenKeys(fcurve): for key in fcurve.Keys: key.TangentMode = FBTangentMode.kFBTangentModeClampProgressive key.LeftTangentWeight = 0.3333; key.RightTangentWeight = 0.3333; key.LeftDerivative = 0.0; key.RightDerivative = 0.0; def parseMotionWarpXML(t): t.motionWarpPath = r"X:\wildwest\script\motionbuildersandbox\Mike\2014\MotionWarpData.xml" print t.motionWarpPath try: t.tree = ET.ElementTree(file=t.motionWarpPath) t.root = t.tree.getroot() except: t.XMLRead = False return t.XMLRead t.angularVelListNames = [] t.angularVelMultiplier = [] t.y_addition = [] t.minRadius = [] for child in t.root: for x in child.iter("type"): getRadius = child.find('minRadius').text t.minRadius.append(getRadius) getMultiplier = child.find('multiplier').text t.angularVelMultiplier.append(getMultiplier) getOffset = child.find('offset').text t.y_addition.append(getOffset) getName = x.get("name") t.angularVelListNames.append(getName) t.XMLRead = True return t.XMLRead def setupTurnParameterList(tool): tool.dropDownList.Items.removeAll() for x in t.angularVelListNames: tool.dropDownList.Items.append(x) def setupAdvancedSettingsList(tool): tool.advanced_settings_dropdown.Items.removeAll() tool.prop_list = [] tool.advanced_settings_dropdown.Items.append("Default (XZ)") tool.advanced_settings_dropdown.Items.append("3D (XYZ)") def setupPlotModeList(tool): tool.plot_mode_dropdown.Items.removeAll() tool.plot_mode_dropdown.Items.append("Plot Character") tool.plot_mode_dropdown.Items.append("Plot Selected Nodes") def ToggleDevices(): for device in FBSystem().Scene.Devices: device.Online = not device.Online def clamp(n, minn, maxn): return max(min(maxn, n), minn) def clean_cos(angle): angle = min(1, max(angle, -1)) if (round(angle, 4) == 1.0): angle = 0.0 return angle def DoesSaveFileExist(): csvpath = tempfile.gettempdir() + '\\motionwarp_save_file.txt' return os.path.isfile(csvpath) def FindPropertyAnimationNode(property_name, model): index = 0 # handle compound attributes if '/' in property_name: split_property_name = property_name.split('/') if split_property_name[1] == 'X': index = 0 if split_property_name[1] == 'Y': index = 1 if split_property_name[1] == 'Z': index = 2 property = model.PropertyList.Find(split_property_name[0]) if property: animationNode = property.GetAnimationNode().Nodes[index] return animationNode else: print "no property found for " + model.Name # simple non-compound property type else: property = model.PropertyList.Find(property_name) if property: animationNode = property.GetAnimationNode() return animationNode else: print "no property found for " + model.Name def SerializeCurve(model_name, property_name, offset_angle, fcurve): ''' Returns a list of dictionaries representing each of the keys in the given FCurve. ''' keyDataList = [] num_of_keys = len(fcurve.Keys) for i in range(num_of_keys): key = fcurve.Keys[i] keyData = { 'model_name': model_name, 'property_name': property_name, 'time': key.Time.Get(), 'value': key.Value, 'interpolation': int(key.Interpolation), 'tangent-mode': int(key.TangentMode), 'constant-mode': int(key.TangentConstantMode), 'left-derivative': key.LeftDerivative, 'right-derivative': key.RightDerivative, 'left-weight': key.LeftTangentWeight, 'right-weight': key.RightTangentWeight, 'offset_angle': offset_angle } keyDataList.append(keyData) return keyDataList def storeZonalValues(): csvdata = [] csvpath = tempfile.gettempdir() + '\\motionwarp_save_file.txt' properties = ['Yaw', 'Translation Offset'] offset_angle = t.angle_a.Value for i in range(0, t.numOfObjects): for j in range(0, len(properties)): property = t.pathConstraint[i].PropertyList.Find(properties[j]) if property: animationNode = property.GetAnimationNode() if animationNode: if len(animationNode.Nodes) > 0: for node in animationNode.Nodes: fcurve = node.FCurve # create a list per key keyDataList = SerializeCurve(t.selected_obj[i].Name, properties[j] + '/' + node.Name, offset_angle, fcurve) for key in keyDataList: csvdata.append(key) else: fcurve = animationNode.FCurve # create a list per key keyDataList = SerializeCurve(t.selected_obj[i].Name, properties[j], offset_angle, fcurve) for key in keyDataList: csvdata.append(key) # save out csv file with open(csvpath, 'w') as csvfile: fieldnames = ['model_name', 'property_name', 'time', 'value', 'interpolation', 'tangent-mode', 'constant-mode', 'left-derivative', 'right-derivative', 'left-weight', 'right-weight', 'offset_angle'] writer = csv.DictWriter(csvfile, fieldnames=fieldnames) writer.writeheader() for element in csvdata: writer.writerow(element) def restoreZonalValues(): # reset min and max ranges for slider_index in range(len(t.zonal.startSlider)): t.zonal.startSlider[slider_index].range_min = 1000000000 t.zonal.endSlider[slider_index].range_max = -1000000000 csvdata = [] csvpath = tempfile.gettempdir() + '\\motionwarp_save_file.txt' model_dictionary = CreateDictionaryFromModels(t.selected_obj) properties = ['Yaw', 'Translation Offset/X', 'Translation Offset/Y', 'Translation Offset/Z'] # load from tmp file with open(csvpath) as csvfile: reader = csv.DictReader(csvfile) for row in reader: csvdata.append(row) new_fcurve_cache = [] curr_fcurve_cache = [] for row in csvdata: # only check against current selected models if row['model_name'] in model_dictionary: index = GetConstraintIndexFromModelName(row['model_name']) animationNode = FindPropertyAnimationNode(row['property_name'], t.pathConstraint[index]) if animationNode: fcurve = animationNode.FCurve if fcurve: propertykey = str(t.pathConstraint[index].Name + row['property_name']) # is this a new curve? if not IsCurveAlreadyCached(new_fcurve_cache, index, propertykey): # create new tmp FCurve new_fcurve_cache.append(FBFCurve()) new_fcurve_cache[-1].propertyname = row['property_name'] new_fcurve_cache[-1].propertykey = propertykey new_fcurve_cache[-1].index = index # load anim data DeserializeCurve(new_fcurve_cache[-1], row) # cache old fcurve pointer too curr_fcurve_cache.append(fcurve) else: fcurve_from_cache = GetCurveFromCache(new_fcurve_cache, index, propertykey) # load anim data DeserializeCurve(fcurve_from_cache, row) else: print "no animation node found!!!!" for i in range(len(new_fcurve_cache)): ScaleCurveValues(new_fcurve_cache[i], curr_fcurve_cache[i]) ################################################## # check range extents of frame time # used to update UI slider positions ################################################## min_frame = curr_fcurve_cache[i].Keys[0].Time.GetFrame() max_frame = curr_fcurve_cache[i].Keys[-1].Time.GetFrame() if min_frame < t.zonal.startSlider[new_fcurve_cache[i].index].range_min: t.zonal.startSlider[new_fcurve_cache[i].index].range_min = min_frame if max_frame > t.zonal.endSlider[new_fcurve_cache[i].index].range_max: t.zonal.endSlider[new_fcurve_cache[i].index].range_max = max_frame def IsCurveAlreadyCached(new_fcurve_cache, index, propertykey): for fcurve in new_fcurve_cache: if fcurve.index == index: if fcurve.propertykey == propertykey: return True return False def GetCurveFromCache(new_fcurve_cache, index, propertykey): for fcurve in new_fcurve_cache: if fcurve.index == index: if fcurve.propertykey == propertykey: return fcurve print "Warning! Curve was not found in cache when it should have been!" def CreateDictionaryFromModels(models): model_dictionary = {} for model in models: model_dictionary[model.Name] = model return model_dictionary def GetConstraintIndexFromModelName(model_name): index = 0 for constraint in t.pathConstraint: if constraint.ModelName == model_name: return index else: index += 1 def TangentWeightIsDefault(tangentWeight): ''' Returns whether the given tangent weight is equal to the default value of 1/3, taking floating-point precision into account. ''' return tangentWeight > 0.3333 and tangentWeight < 0.3334 def DeserializeCurve(fcurve, row): ''' Populates the given FCurve based on keyframe data listed in serialized form. Expects key data to be ordered by time. ''' keyIndex = fcurve.KeyAdd(FBTime(int(row['time'])), float(row['value'])) key = fcurve.Keys[keyIndex] key.Interpolation = FBInterpolation.values[float(row['interpolation'])] key.TangentMode = FBTangentMode.values[float(row['tangent-mode'])] if key.TangentMode == FBTangentMode.kFBTangentModeTCB: key.TangentMode = FBTangentMode.kFBTangentModeBreak key.TangentConstantMode = FBTangentConstantMode.values[float(row['constant-mode'])] key.LeftDerivative = float(row['left-derivative']) key.RightDerivative = float(row['right-derivative']) if not TangentWeightIsDefault(float(row['left-weight'])): key.LeftTangentWeight = float(row['left-weight']) if not TangentWeightIsDefault(float(row['right-weight'])): key.RightTangentWeight = float(row['right-weight']) def ScaleCurveValues(new_fcurve, curr_fcurve): # scale new curve to match extents of old curve new_curve_min = GetMinKeyValue(new_fcurve) new_curve_max = GetMaxKeyValue(new_fcurve) new_curve_min_time = new_fcurve.Keys[0].Time.GetFrame() new_curve_max_time = new_fcurve.Keys[-1].Time.GetFrame() new_curve_dir = GetDirectionOfFCurve(new_fcurve) new_curve_range = abs(new_curve_min - new_curve_max) new_curve_range_time = abs(new_curve_max_time - new_curve_min_time) old_curve_min = GetMinKeyValue(curr_fcurve) old_curve_max = GetMaxKeyValue(curr_fcurve) old_curve_min_time = curr_fcurve.Keys[0].Time.GetFrame() old_curve_max_time = curr_fcurve.Keys[-1].Time.GetFrame() old_curve_dir = GetDirectionOfFCurve(curr_fcurve) old_curve_range = abs(old_curve_min - old_curve_max) old_curve_range_time = abs(old_curve_max_time - old_curve_min_time) # if there is no value range then just scale time if abs(new_curve_range) < 0.01: scale_ratio = float(new_curve_range_time) / old_curve_range_time offset = int(new_curve_min_time - old_curve_min_time) ScaleFCurveTime(curr_fcurve, FBTime(0, 0, 0, old_curve_min_time), scale_ratio, offset) return else: # then scale values of new fcurve to match existing curve value extents for key in new_fcurve.Keys: normalised_value = 1.0 if new_curve_dir * old_curve_dir > 0: normalised_value = abs(key.Value - new_curve_min) / new_curve_range else: normalised_value = abs(key.Value - new_curve_max) / new_curve_range key.Value = old_curve_min + (normalised_value * old_curve_range) # copy fcurve curr_fcurve.EditClear() curr_fcurve.KeyReplaceBy(new_fcurve) def GetMinKeyValue(fcurve): min = 1000000000.0 for key in fcurve.Keys: if key.Value < min: min = key.Value return min def GetMaxKeyValue(fcurve): max = -1000000000.0 for key in fcurve.Keys: if key.Value > max: max = key.Value return max def GetDirectionOfFCurve(fcurve): if fcurve.Keys[0].Value <= fcurve.Keys[-1].Value: return 1 else: return -1 ''' def GetAbsMaxValueOfFCurve( fcurve, ref sign ): abs_max_value = 0.0 for key in fcurve.Keys: abs_value = abs( key.Value ) if abs_value > abs_max_value: abs_max_value = key.Value return abs_max_value ''' def closeZonalHelperWindow(): FBDestroyToolByName("Zonal Key Helper") def clearSelection(): ''' Clear selection function ''' # Get selected models modelList = FBModelList() FBGetSelectedModels(modelList, None, True) # Deselect models for model in modelList: model.Selected = False # deselect constraints and unfocus any custom properties for constraint in t.pathConstraint: yaw_prop = constraint.PropertyList.Find("Yaw") if yaw_prop: # select custom property yaw_prop.SetFocus(False) translation_prop = constraint.PropertyList.Find("Translation Offset") if translation_prop: # select custom property translation_prop.SetFocus(False) constraint.Selected = False def cleanupAndReset(): '''delete all constraints/paths and reset arrays to starting state''' for j in range(0, t.numOfObjects): # delete constraints t.constraint[j].FBDelete() t.pathConstraint[j].FBDelete() # delete nulls t.null[j].FBDelete() t.pathNull[j].FBDelete() # delete path t.path[j].FBDelete() # reset arrays t.null = [] t.midNull = [] t.pathNull = [] t.path = [] t.pathConstraint = [] t.midNullConstraint = [] t.constraint = [] # delete keycontrols and reset t.keyGroup.RemoveAllProperties() t.keyGroup.FBDelete() print 'constraints deleted' # update UI if t.numOfObjects == 1: t.pin_button.Caption = "Apply MotionWarp to '" + t.selected_obj_name[0] + "'" else: t.pin_button.Caption = "Apply MotionWarp to " + str(t.numOfObjects) + " objects" t.delete_button.Enabled = False t.slider.Enabled = True t.status.Enabled = True t.pinned = False t.zonalKeyHelper.Enabled = False t.circle_button.Enabled = True t.spline_button.Enabled = True t.zonal_start_button.Enabled = True t.zonal_stop_button.Enabled = True #################################################################### # UI callbacks #################################################################### def sliderUpdated(control, event): t.status.Caption = str(int(control.Value * 10) + 2) def modeButtonsCallback(control, event): if t.circle_button.State: t.radius.Enabled = True t.radius.ReadOnly = False t.counter_button.Enabled = True t.clockwise_button.Enabled = True t.slider.ReadOnly = True t.slider.Enabled = False t.status.Enabled = False t.status.ReadOnly = True t.angle_a.Enabled = False t.angle_a.ReadOnly = True t.angle_b.Enabled = False t.angle_b.ReadOnly = True t.zonalKeyHelper.Enabled = False t.zonalKeyHelper.ReadOnly = True t.radius_mode_button.Enabled = True t.radius_mode_button.ReadOnly = False t.manual_AV_mode_button.Enabled = True t.manual_AV_mode_button.ReadOnly = False t.auto_AV_mode_button.Enabled = True t.auto_AV_mode_button.ReadOnly = False t.advanced_settings_dropdown.Enabled = False t.advanced_settings_dropdown.ReadOnly = True t.advanced_settings_dropdown.ItemIndex = 0 if t.radius_mode_button.State == 1: t.radius.Enabled = True t.radius.ReadOnly = False t.dropDownList.Enabled = False t.dropDownList.ReadOnly = True t.large_turn_button.Enabled = False t.small_turn_button.Enabled = False elif t.manual_AV_mode_button.State == 1: t.radius.Enabled = True t.radius.ReadOnly = False t.dropDownList.Enabled = False t.dropDownList.ReadOnly = True t.large_turn_button.Enabled = False t.small_turn_button.Enabled = False else: t.radius.Enabled = False t.radius.ReadOnly = True t.dropDownList.Enabled = True t.dropDownList.ReadOnly = False t.large_turn_button.Enabled = True t.small_turn_button.Enabled = True if t.spline_button.State: t.radius.Enabled = False t.radius.ReadOnly = True t.counter_button.Enabled = False t.clockwise_button.Enabled = False t.large_turn_button.Enabled = False t.small_turn_button.Enabled = False t.slider.ReadOnly = False t.slider.Enabled = True t.status.Enabled = True t.status.ReadOnly = False t.angle_a.Enabled = False t.angle_a.ReadOnly = True t.angle_b.Enabled = False t.angle_b.ReadOnly = True t.zonalKeyHelper.Enabled = False t.zonalKeyHelper.ReadOnly = True t.radius_mode_button.Enabled = False t.radius_mode_button.ReadOnly = True t.manual_AV_mode_button.Enabled = False t.manual_AV_mode_button.ReadOnly = True t.auto_AV_mode_button.Enabled = False t.auto_AV_mode_button.ReadOnly = True t.dropDownList.Enabled = False t.dropDownList.ReadOnly = True t.advanced_settings_dropdown.Enabled = True t.advanced_settings_dropdown.ReadOnly = False if t.zonal_start_button.State or t.zonal_stop_button.State: t.radius.Enabled = False t.radius.ReadOnly = True t.counter_button.Enabled = False t.clockwise_button.Enabled = False t.large_turn_button.Enabled = False t.small_turn_button.Enabled = False t.slider.ReadOnly = True t.slider.Enabled = False t.status.Enabled = False t.status.ReadOnly = True t.angle_a.Enabled = True t.angle_a.ReadOnly = False t.angle_b.Enabled = False t.angle_b.ReadOnly = True t.zonalKeyHelper.Enabled = False t.zonalKeyHelper.ReadOnly = True t.radius_mode_button.Enabled = False t.radius_mode_button.ReadOnly = True t.manual_AV_mode_button.Enabled = False t.manual_AV_mode_button.ReadOnly = True t.auto_AV_mode_button.Enabled = False t.auto_AV_mode_button.ReadOnly = True t.dropDownList.Enabled = False t.dropDownList.ReadOnly = True t.advanced_settings_dropdown.Enabled = True t.advanced_settings_dropdown.ReadOnly = False if t.zonal_advanced_button.State: t.radius.Enabled = False t.radius.ReadOnly = True t.counter_button.Enabled = False t.clockwise_button.Enabled = False t.large_turn_button.Enabled = False t.small_turn_button.Enabled = False t.slider.ReadOnly = True t.slider.Enabled = False t.status.Enabled = False t.status.ReadOnly = True t.angle_a.Enabled = True t.angle_a.ReadOnly = False t.angle_b.Enabled = True t.angle_b.ReadOnly = False t.zonalKeyHelper.Enabled = False t.zonalKeyHelper.ReadOnly = True t.radius_mode_button.Enabled = False t.radius_mode_button.ReadOnly = True t.manual_AV_mode_button.Enabled = False t.manual_AV_mode_button.ReadOnly = True t.auto_AV_mode_button.Enabled = False t.auto_AV_mode_button.ReadOnly = True t.dropDownList.Enabled = False t.dropDownList.ReadOnly = True t.advanced_settings_dropdown.Enabled = True t.advanced_settings_dropdown.ReadOnly = False return def radiusModeButtonsCallback(control, event): if t.radius_mode_button.State: t.radius.Hint = "Radius of circle in Metres" else: t.radius.Hint = "Inaccessable while not in Radius mode" # Set Auto Angular Velocity Buttons to correct states if t.auto_AV_mode_button.State: t.radius.Enabled = False t.radius.ReadOnly = True t.radius_mode_button.Enabled = True t.radius_mode_button.ReadOnly = False t.manual_AV_mode_button.Enabled = True t.manual_AV_mode_button.ReadOnly = False t.auto_AV_mode_button.Enabled = True t.auto_AV_mode_button.ReadOnly = False t.dropDownList.Enabled = True t.dropDownList.ReadOnly = False t.large_turn_button.Enabled = True t.small_turn_button.Enabled = True # Set Manual Angular Velocity Buttons to correct states elif t.manual_AV_mode_button.State: t.large_turn_button.Enabled = False t.small_turn_button.Enabled = False t.radius.Enabled = True t.radius.ReadOnly = False t.radius_mode_button.Enabled = True t.radius_mode_button.ReadOnly = False t.manual_AV_mode_button.Enabled = True t.manual_AV_mode_button.ReadOnly = False t.auto_AV_mode_button.Enabled = True t.auto_AV_mode_button.ReadOnly = False t.dropDownList.Enabled = False t.dropDownList.ReadOnly = True # Set Manual Radius Buttons to correct states elif t.radius_mode_button.State: t.large_turn_button.Enabled = False t.small_turn_button.Enabled = False t.radius.Enabled = True t.radius.ReadOnly = False t.radius_mode_button.Enabled = True t.radius_mode_button.ReadOnly = False t.manual_AV_mode_button.Enabled = True t.manual_AV_mode_button.ReadOnly = False t.auto_AV_mode_button.Enabled = True t.auto_AV_mode_button.ReadOnly = False t.dropDownList.Enabled = False t.dropDownList.ReadOnly = True def deleteButtonCallBack(control, event): if t.zonal_start_button.State or t.zonal_stop_button.State: if hasattr(t, 'zonal'): if not t.zonal.TypeInfo == 203: closeZonalHelperWindow() cleanupAndReset() def zonalKeyHelperCallBack(control, event): t.zonal = FBCreateUniqueTool("Zonal Key Helper") PopulateZonalUI(t.zonal) def goButtonCallBack(control, event): # Disable all expressions for the duration of this event with SceneExpressionsDisabled(): # get time span t.take_start_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() t.take_end_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() t.current_frame = FBSystem().LocalTime.GetFrame() # HORRENDOUS HACK ALERT # this fixes an issue with turns. Need to figure out why this fixes it when time permits :( t.take_end_frame += 1 # update slider to startframe FBPlayerControl().Goto(FBTime(0, 0, 0, t.take_start_frame)) FBSystem().Scene.Evaluate() for i in range(0, t.numOfObjects): ######################################## # Initialise ######################################## if t.pinned == False: clearSelection() # create null t.null.append(FBModelNull('constraint_null for ' + t.selected_obj_name[i])) # create null t.midNull.append(FBModelNull('mid_null')) # create path null t.pathNull.append(FBModelNull('path_null for ' + t.selected_obj_name[i])) # create path curve t.path.append(FBModelPath3D('MotionWarp 3d Curve for ' + t.selected_obj_name[i])) # if it's the mover then make the 3D curve a different colour since we don't want to edit this one manually in most cases if (t.selected_obj_name[i] == 'mover'): # get curve color property color_property = t.path[-1].PropertyList.Find('Curve Color') if color_property: color_property.Data = FBColor(0.0, 0.0, 1.0) t.path[i].Show = True ##################################################################################### # set keying group # need to plot translation/rotation ##################################################################################### ltran = t.null[i].PropertyList.Find('Translation (Lcl)') lrot = t.null[i].PropertyList.Find('Rotation (Lcl)') t.keyGroup = FBKeyingGroup("MotionWarpKeyGroup", FBKeyingGroupType.kFBKeyingGroupGlobal) # Add Properties to Custom Key Group t.keyGroup.AddProperty(ltran) t.keyGroup.AddProperty(lrot) # # SetActive, activate the keying group. t.keyGroup.SetActive(True) # # SetEnabled, will make available the keying group in keying group list of the key control UI. t.keyGroup.SetEnabled(True) # make sure system in on current layer FBSystem().CurrentTake.SetCurrentLayer(0) if t.pinned == False: # turn off expressions for optimal performance # ToggleDevices() # lists for storing object pos and rotations trans_array = [] trans_warp_weighting = [] path_start_position = [] rot_array = [] count = 0 # ---------------------------------------------------------------------------------- # Get Mover position of current character # This will be used as a reference point for offsets # if no Mover is found use initialised vector instead # ---------------------------------------------------------------------------------- SEARCH_STRING = 'mover' mover_start_position = FBVector3d() mover_end_position = FBVector3d() invMoverMx = FBMatrix() moverWorldMx = FBMatrix() mover_world_scaleV3 = FBSVector() t.moverObject = '' character = FBApplication().CurrentCharacter if character is not None: t.moverObject = FBFindModelByLabelName(character.OwnerNamespace.Name + ':' + SEARCH_STRING) if t.moverObject is None: print "WARNING! " + SEARCH_STRING + " not Found!" # ---------------------------------------------------------------------------------- # loop for every frame - get pos/rot of selected object # ---------------------------------------------------------------------------------- for frame in range(t.take_start_frame, t.take_end_frame + 1): # update slider FBPlayerControl().Goto(FBTime(0, 0, 0, frame)) FBSystem().Scene.Evaluate() # get selected translation and rotation for object for i in range(0, t.numOfObjects): obj_trans = FBVector3d() obj_rot = FBVector3d() t.selected_obj[i].GetVector(obj_trans, FBModelTransformationType.kModelTranslation, True) t.selected_obj[i].GetVector(obj_rot, FBModelTransformationType.kModelRotation, True) ############################################################################################################################## # if we're on the last object also grab mover inv matrix and position ############################################################################################################################## if (i == t.numOfObjects - 1): if (frame == t.take_start_frame): # get start position t.moverObject.GetVector(mover_start_position, FBModelTransformationType.kModelTranslation, True) # get world and inverse matrices for Mover t.moverObject.GetMatrix(invMoverMx, FBModelTransformationType.kModelInverse_Transformation, True) t.moverObject.GetMatrix(moverWorldMx, FBModelTransformationType.kModelTransformation, True) FBMatrixToScaling(mover_world_scaleV3, moverWorldMx) if (frame == t.take_end_frame): t.moverObject.GetVector(mover_end_position, FBModelTransformationType.kModelTranslation, True) current_frame_mover_pos = FBVector3d() current_frame_invMoverMx = FBMatrix() t.moverObject.GetMatrix(current_frame_invMoverMx, FBModelTransformationType.kModelInverse_Transformation, True) t.moverObject.GetVector(current_frame_mover_pos, FBModelTransformationType.kModelTranslation, True) ############################################################################################################################## # store translation/rotation trans_array.append(obj_trans) rot_array.append(obj_rot) t.midNull[i].Selected = True if (frame == t.take_start_frame): # store path position start path_start_position.append(obj_trans) count += 1 # ---------------------------------------------------------------------------------- # calculate path lengths # ---------------------------------------------------------------------------------- path_length = [] frame_range = t.take_end_frame - t.take_start_frame for i in range(0, t.numOfObjects): if t.advanced_settings_dropdown.ItemIndex == 1: path_length.append( FBVector3d((trans_array[(frame_range - 1) * t.numOfObjects + i][0] - trans_array[i][0]), (trans_array[(frame_range - 1) * t.numOfObjects + i][1] - trans_array[i][1]), (trans_array[(frame_range - 1) * t.numOfObjects + i][2] - trans_array[i][2]) ).Length() ) else: path_length.append( FBVector3d((trans_array[(frame_range - 1) * t.numOfObjects + i][0] - trans_array[i][0]), 0, (trans_array[(frame_range - 1) * t.numOfObjects + i][2] - trans_array[i][2]) ).Length() ) # ---------------------------------------------------------------------------------- # loop for every frame - set pos/rot on null # ---------------------------------------------------------------------------------- count = 0 if t.circle_button.State: curve_steps = 2 else: curve_steps = int(t.slider.Value * 10) + 2 frame_step = float(frame_range / (curve_steps - 1.0)) frame_step_total = t.take_start_frame + frame_step frame = t.take_start_frame while frame <= t.take_end_frame: # update slider FBPlayerControl().Goto(FBTime(0, 0, 0, frame)) FBSystem().Scene.Evaluate() for i in range(0, t.numOfObjects): # set up path curve on first frame if (frame == t.take_start_frame): # path starts at each objects own starting frame pathStart = FBVector4d(trans_array[count * t.numOfObjects + i][0], trans_array[count * t.numOfObjects + i][1], trans_array[count * t.numOfObjects + i][2], 0) t.path[i].PathKeyStartAdd(pathStart) # set pivot to be same as mover as default pivot = t.path[i].PropertyList.Find('Scaling Pivot') if pivot: pivot.Data = mover_start_position pivot = t.path[i].PropertyList.Find('Rotation Pivot') if pivot: pivot.Data = mover_start_position # remove next three indices as they are created automatically and are unwanted # each time we remove index1 the curve indices are recalculated so we need to do this three times # looks odd! t.path[i].PathKeyRemove(1) t.path[i].PathKeyRemove(1) t.path[i].PathKeyRemove(1) trans_warp_weighting.append(0.0) else: # only if we're on a 'frame step' keyframe do we want to create a spline point (or end frame) if (frame >= frame_step_total or frame == t.take_end_frame): # Add path key. Fix the height to be the same as the starting height if t.advanced_settings_dropdown.ItemIndex == 1: t.path[i].PathKeyEndAdd( FBVector4d(trans_array[count * t.numOfObjects + i][0], trans_array[count * t.numOfObjects + i][1], trans_array[count * t.numOfObjects + i][2], 0)) else: t.path[i].PathKeyEndAdd( FBVector4d(trans_array[count * t.numOfObjects + i][0], trans_array[i][1], trans_array[count * t.numOfObjects + i][2], 0)) # Fix for bug. if the end frame is to close to the penultimate keyframe the tangent stretches out the curve. # Reduce tangent length on penultimate key to zero if (frame == t.take_end_frame and t.circle_button.State): # linear tangent for penultimate key keyIndex = t.path[i].PathKeyGetCount() - 2 keyPosition = t.path[i].PathKeyGet(keyIndex) # set tangent to be the same position as the keyframe (creating a linear section between this and the last path key) t.path[i].PathKeySetLeftTangent(keyIndex, keyPosition, True) # if we're at the end of the object list *and* we're on a frame step then increment frame step total if (i == t.numOfObjects - 1): frame_step_total += frame_step # trans warp weighting is an estimated percentage of the overall motionwarp path moved in this frame # this will allow the motion warp path to more accurately follow the selected object # calc % distance travelled this frame of overall path length (very approximate) if t.advanced_settings_dropdown.ItemIndex == 1: current_length = FBVector3d( (trans_array[count * t.numOfObjects + i][0] - trans_array[i][0]), (trans_array[count * t.numOfObjects + i][1] - trans_array[i][1]), (trans_array[count * t.numOfObjects + i][2] - trans_array[i][2]) ).Length() else: current_length = FBVector3d( (trans_array[count * t.numOfObjects + i][0] - trans_array[i][0]), 0, (trans_array[count * t.numOfObjects + i][2] - trans_array[i][2]) ).Length() if not path_length[i] == 0.0: trans_warp_weighting.append((current_length / path_length[i]) * 100.0) # if path length is zero use the frame progression to update the weighting percentage else: trans_warp_weighting.append((frame / frame_range) * 100.0) # set selected translation and rotation for object t.midNull[i].SetVector(trans_array[count * t.numOfObjects + i], FBModelTransformationType.kModelTranslation, True) t.midNull[i].SetVector(rot_array[count * t.numOfObjects + i], FBModelTransformationType.kModelRotation, True) FBSystem().Scene.Evaluate() # key null FBPlayerControl().Key() if (frame == t.take_end_frame): break # increment frame and count frame += 1 count += 1 # clamp frame range to end frame if (frame > t.take_end_frame): frame = t.take_end_frame count = frame_range for j in range(0, t.numOfObjects): ################################################################################### # constrain path_null to path ################################################################################### lMgr = FBConstraintManager() # get index to parent/child constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Path': lIndex = i break # create constraint path_constraint = lMgr.TypeCreateConstraint(lIndex) path_constraint.Name = 'MotionWarp Path Constraint for ' + t.selected_obj_name[j] # add ref objects for i in range(0, path_constraint.ReferenceGroupGetCount()): if path_constraint.ReferenceGroupGetName(i) == 'Path Source': path_constraint.ReferenceAdd(i, t.path[j]) elif path_constraint.ReferenceGroupGetName(i) == 'Constrained Object': path_constraint.ReferenceAdd(i, t.pathNull[j]) # snap/activate constraint path_constraint.Active = True # store ref to constraint objects t.pathConstraint.append(path_constraint) # store ref to the model related to the constraint t.pathConstraint[len(t.pathConstraint) - 1].ModelName = t.selected_obj_name[j] if t.circle_button.State: path_constraint.PropertyList.Find("Follow Path").Data = False else: path_constraint.PropertyList.Find("Follow Path").Data = True # path_constraint.PropertyList.Find("Follow Path").Data = True path_constraint.PropertyList.Find("Up Vector").Data = 2 path_constraint.PropertyList.Find("Front Vector").Data = 0 ############################### # alter path warp to match object % ############################### warpProperty = path_constraint.PropertyList.Find("Warp") warpAn = warpProperty.GetAnimationNode() warpFc = warpAn.FCurve count = 0 for key in range(t.take_start_frame, t.take_end_frame): warpFc.KeyAdd(FBTime(0, 0, 0, key), trans_warp_weighting[count * t.numOfObjects + j]) count += 1 ################################################################################### # constrain constraint_null to midd_null ################################################################################### lMgr = FBConstraintManager() # get index to parent/child constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Parent/Child': lIndex = i break # create constraint lPosConst = lMgr.TypeCreateConstraint(lIndex) # add ref objects for i in range(0, lPosConst.ReferenceGroupGetCount()): if lPosConst.ReferenceGroupGetName(i) == 'Source (Parent)': lPosConst.ReferenceAdd(i, t.midNull[j]) elif lPosConst.ReferenceGroupGetName(i) == 'Constrained object (Child)': lPosConst.ReferenceAdd(i, t.null[j]) # activate constraint lPosConst.Active = True # store ref to constraint objects t.midNullConstraint.append(lPosConst) ################################################################################### # parent constraint null ################################################################################### # this is where the magic happens :) # we've contrained the null to the midnull so it can't change but we change the heirarchy and plot # so that the offset per frame now becomes a local transformation and we keep the existing world space transformation # through this counter animation t.null[j].Parent = t.pathNull[j] ################################################################################### # plot and remove mid null ################################################################################### clearSelection() for i in range(0, t.numOfObjects): t.null[i].Selected = True # ToggleDevices() FBSystem().CurrentTake.PlotTakeOnSelected(plotOptions()) # ToggleDevices() FBSystem().Scene.Evaluate() for i in range(0, t.numOfObjects): # delete mid null t.midNull[i].FBDelete() # delete constraint t.midNullConstraint[i].FBDelete() ################################################################################### # finally constrain selected object to constraint null ################################################################################### for j in range(0, t.numOfObjects): lMgr = FBConstraintManager() # get index to parent/child constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Parent/Child': lIndex = i break # create constraint lPosConst = lMgr.TypeCreateConstraint(lIndex) lPosConst.Name = ' Parent/Child Constraint to MotionWarp Path for ' + t.selected_obj_name[j] # add ref objects for i in range(0, lPosConst.ReferenceGroupGetCount()): if lPosConst.ReferenceGroupGetName(i) == 'Source (Parent)': lPosConst.ReferenceAdd(i, t.null[j]) elif lPosConst.ReferenceGroupGetName(i) == 'Constrained object (Child)': lPosConst.ReferenceAdd(i, t.selected_obj[j]) # snap/activate constraint lPosConst.Snap() # store ref to constraint objects t.constraint.append(lPosConst) ################################################################################### # Circle Mode # # if mode selected move spline points around into circle ################################################################################### if t.circle_button.State: ################################################################################### # turn on path constraint and set yaw offset to angle between world X axis (1,0,0) and Mover Y axis direction ################################################################################### for path_constraint in t.pathConstraint: path_constraint.PropertyList.Find("Follow Path").Data = True # calc angle world_x_axis_in_mover_space = FBVector4d() FBVectorMatrixMult(world_x_axis_in_mover_space, invMoverMx, FBVector4d(mover_start_position[0], mover_start_position[1], mover_start_position[2], 0.0) + FBVector4d(100.0, 0.0, 0.0, 0.0)) dot = world_x_axis_in_mover_space.DotProduct(FBVector4d(0.0, 1.0, 0.0, 0.0)) # catch for zero angle (1.0) since it causes math.acos to error # requires dot rounded since very tiny floats were also slipping through comparison check # but still causing math.acos to bomb out dot = clean_cos(dot) angle_between_x_axes = math.acos(dot) * 180.0 / math.pi if world_x_axis_in_mover_space[0] >= 0.0: path_constraint.PropertyList.Find("Yaw").Data = -angle_between_x_axes else: path_constraint.PropertyList.Find("Yaw").Data = angle_between_x_axes worldCoords = FBVector4d() objectPositionInMoverSpace = FBVector4d() ModelMx = FBMatrix() TransformedModelMx = FBMatrix() # unit to metre conversion in MB scale = 100.0 ################################################################################################ # cache user direction choice for circle - (note: flip value) ################################################################################################ if t.clockwise_button.State == 1: direction = 1 else: direction = -1 for j in range(0, t.numOfObjects): ################################################################################################ # calcluate radius # either direct from UI if radius mode is selected, or calculated from the angular velocity ################################################################################################ if t.radius_mode_button.State == 1: radius = t.radius.Value elif t.auto_AV_mode_button.State == 1: average_velocity_ms = (path_length[j] / scale) / (frame_range / 30.0) print "average_velocity_ms is " + str(average_velocity_ms) # calculate the desired angular velocity of from overall average speed currentDropDownSelection = t.dropDownList.ItemIndex calculated_angular_velocity = calcAV(average_velocity_ms, currentDropDownSelection) print "calculated angular velocity is " + str(calculated_angular_velocity) radius = average_velocity_ms / calculated_angular_velocity selectedMininumRadius = float(t.minRadius[currentDropDownSelection]) if t.large_turn_button.State == 1: # clamp value of radius if it's too tight if radius < (selectedMininumRadius * 2): radius = (selectedMininumRadius * 2) else: if radius < selectedMininumRadius: radius = selectedMininumRadius print "calculated radius is " + str(radius) else: average_velocity_ms = (path_length[j] / scale) / (frame_range / 30.0) print "average_velocity_ms is " + str(average_velocity_ms) radius = average_velocity_ms / t.radius.Value print "calculated radius is " + str(radius) prescaled_radius = radius # centre point is the circle origin with respect to the mover ( of radius) FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(radius * direction, 0.0, 0.0, 0.0)) centrePoint = FBVector3d(worldCoords[0], worldCoords[1], worldCoords[2]) print "########################################" print "OBJECT >> " + t.selected_obj_name[j] print "########################################" # make sure we have at least five points... while (t.path[j].PathKeyGetCount() < 5): t.path[j].PathKeyEndAdd(FBVector4d()) # remove any extraneous points over 5... while (t.path[j].PathKeyGetCount() > 5): t.path[j].PathKeyRemove(t.path[j].PathKeyGetCount() - 1) # create four point circle points and edit tangents circle_height = t.path[j].PathKeyGet(0)[1] # convert selected object to Mover coord space FBVectorMatrixMult(objectPositionInMoverSpace, invMoverMx, FBVector4d(path_start_position[j][0], path_start_position[j][1], path_start_position[j][2], 0.0)) # copy to a FBVector3d type for ease of use objectOffsetFromMover = FBVector3d(objectPositionInMoverSpace[0], objectPositionInMoverSpace[1], objectPositionInMoverSpace[2]) radius = (FBVector3d(path_start_position[j][0], 0.0, path_start_position[j][2]) - FBVector3d( centrePoint[0], 0.0, centrePoint[2])).Length() / scale # magic number/constant below is (4/3)*tan(math.pi/8) based on four control points # see http://stackoverflow.com/questions/1734745/how-to-create-circle-with-b%C3%A9zier-curves tangent_length = 0.552284749831 * radius ############################################################################################ # calculate circle angle offset ############################################################################################ v2 = path_start_position[j] - centrePoint v2[1] = 0.0 v1 = mover_start_position - centrePoint v1[1] = 0.0 v1.Normalize() v2.Normalize() dot = v1.DotProduct(v2) # catch for zero angle (1.0) since it causes math.acos to error # requires dot rounded since very tiny floats were also slipping through comparison check # but still causing math.acos to bomb out if (round(dot, 4) == 1.0): circleAngleOffset = 0.0 else: circleAngleOffset = math.acos(dot) # if object position is in front or behind mover then flip angle depending on circle direction if (objectOffsetFromMover[1] > 0.0 and direction == 1) or ( objectOffsetFromMover[1] < 0.0 and direction == -1): circleAngleOffset *= -1 ############################################################################################ # create circle with all points rotated by circleAngleOffset ############################################################################################ ################################### # Circle Point 0 ################################### circlePoint = FBVector3d(0.0, 0.0, 0.0) rotatedCirclePoint = RotateVectorRadians(circlePoint, circleAngleOffset) # add any offset between object and mover rotatedCirclePoint += objectOffsetFromMover # convert to WS & set FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedCirclePoint[0], rotatedCirclePoint[1], rotatedCirclePoint[2], 1.0)) # cache circle point position for tangent use transformedCirclePoint = FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0) t.path[j].PathKeySet(0, transformedCirclePoint) # rotate tangent tangent = FBVector3d(0.0, tangent_length, 0.0) rotatedTangent = RotateVectorRadians(tangent, circleAngleOffset) # convert to WS FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedTangent[0], rotatedTangent[1], rotatedTangent[2], 0.0)) # create relative vector from mover to tangent end, and add to circle point worldCoords = FBVector4d((worldCoords[0] - mover_start_position[0]) + transformedCirclePoint[0], (worldCoords[1] - mover_start_position[1]) + transformedCirclePoint[1], (worldCoords[2] - mover_start_position[2]) + transformedCirclePoint[2], 0.0) # have to set both tangent sides on the first point for some strange reason only known to MB..... t.path[j].PathKeySetLeftTangent(0, FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0), True) t.path[j].PathKeySetRightTangent(0, FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0), True) ################################### # Circle Point 1 ################################### circlePoint = FBVector3d(direction * radius, radius, 0.0) rotatedCirclePoint = RotateVectorRadians(circlePoint, circleAngleOffset) # add any offset between object and mover rotatedCirclePoint += objectOffsetFromMover # convert to WS & set FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedCirclePoint[0], rotatedCirclePoint[1], rotatedCirclePoint[2], 0.0)) # cache circle point position for tangent use transformedCirclePoint = FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0) t.path[j].PathKeySet(1, transformedCirclePoint) # rotate tangent tangent = FBVector3d(-direction * tangent_length, 0.0, 0.0) rotatedTangent = RotateVectorRadians(tangent, circleAngleOffset) # convert to WS FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedTangent[0], rotatedTangent[1], rotatedTangent[2], 0.0)) # create relative vector from mover to tangent end, and add to circle point worldCoords = FBVector4d((worldCoords[0] - mover_start_position[0]) + transformedCirclePoint[0], (worldCoords[1] - mover_start_position[1]) + transformedCirclePoint[1], (worldCoords[2] - mover_start_position[2]) + transformedCirclePoint[2], 0.0) t.path[j].PathKeySetLeftTangent(1, FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0), True) ################################### # Circle Point 2 ################################### circlePoint = FBVector3d(direction * radius * 2.0, 0.0, 0.0) rotatedCirclePoint = RotateVectorRadians(circlePoint, circleAngleOffset) # add any offset between object and mover rotatedCirclePoint += objectOffsetFromMover # convert to WS & set FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedCirclePoint[0], rotatedCirclePoint[1], rotatedCirclePoint[2], 0.0)) # cache circle point position for tangent use transformedCirclePoint = FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0) t.path[j].PathKeySet(2, transformedCirclePoint) # rotate tangent tangent = FBVector3d(0.0, tangent_length, 0.0) rotatedTangent = RotateVectorRadians(tangent, circleAngleOffset) # convert to WS FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedTangent[0], rotatedTangent[1], rotatedTangent[2], 0.0)) # create relative vector from mover to tangent end, and add to circle point worldCoords = FBVector4d((worldCoords[0] - mover_start_position[0]) + transformedCirclePoint[0], (worldCoords[1] - mover_start_position[1]) + transformedCirclePoint[1], (worldCoords[2] - mover_start_position[2]) + transformedCirclePoint[2], 0.0) t.path[j].PathKeySetLeftTangent(2, FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0), True) ################################### # Circle Point 3 ################################### circlePoint = FBVector3d(direction * radius, -radius, 0.0) rotatedCirclePoint = RotateVectorRadians(circlePoint, circleAngleOffset) # add any offset between object and mover rotatedCirclePoint += objectOffsetFromMover # convert to WS & set FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedCirclePoint[0], rotatedCirclePoint[1], rotatedCirclePoint[2], 0.0)) # cache circle point position for tangent use transformedCirclePoint = FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0) t.path[j].PathKeySet(3, worldCoords) # rotate tangent tangent = FBVector3d(direction * tangent_length, 0.0, 0.0) rotatedTangent = RotateVectorRadians(tangent, circleAngleOffset) # convert to WS FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedTangent[0], rotatedTangent[1], rotatedTangent[2], 0.0)) # create relative vector from mover to tangent end, and add to circle point worldCoords = FBVector4d((worldCoords[0] - mover_start_position[0]) + transformedCirclePoint[0], (worldCoords[1] - mover_start_position[1]) + transformedCirclePoint[1], (worldCoords[2] - mover_start_position[2]) + transformedCirclePoint[2], 0.0) t.path[j].PathKeySetLeftTangent(3, FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0), True) ################################### # Circle Point 4 ################################### circlePoint = FBVector3d(0.0, 0.0, 0.0) rotatedCirclePoint = RotateVectorRadians(circlePoint, circleAngleOffset) # add any offset between object and mover rotatedCirclePoint += objectOffsetFromMover # convert to WS & set FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedCirclePoint[0], rotatedCirclePoint[1], rotatedCirclePoint[2], 0.0)) # cache circle point position for tangent use transformedCirclePoint = FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0) t.path[j].PathKeySet(4, worldCoords) # rotate tangent tangent = FBVector3d(0.0, -tangent_length, 0.0) rotatedTangent = RotateVectorRadians(tangent, circleAngleOffset) # convert to WS FBVectorMatrixMult(worldCoords, moverWorldMx, FBVector4d(rotatedTangent[0], rotatedTangent[1], rotatedTangent[2], 0.0)) # create relative vector from mover to tangent end, and add to circle point worldCoords = FBVector4d((worldCoords[0] - mover_start_position[0]) + transformedCirclePoint[0], (worldCoords[1] - mover_start_position[1]) + transformedCirclePoint[1], (worldCoords[2] - mover_start_position[2]) + transformedCirclePoint[2], 0.0) t.path[j].PathKeySetLeftTangent(4, FBVector4d(worldCoords[0], worldCoords[1], worldCoords[2], 0.0), True) ############################### # alter curve pivot to sit at centre and move null to beginning ############################### pivot = t.path[j].PropertyList.Find('Scaling Pivot') if pivot: pivot.Data = FBVector3d(centrePoint[0], path_start_position[j][1], centrePoint[2]) pivot = t.path[j].PropertyList.Find('Rotation Pivot') if pivot: pivot.Data = FBVector3d(centrePoint[0], path_start_position[j][1], centrePoint[2]) # move null to beginning of circle t.null[j].SetVector(FBVector3d(path_start_position[j][0], circle_height, path_start_position[j][2]), FBModelTransformationType.kModelTranslation, True) ############################### # alter path warp to match object % ############################### warpProperty = t.pathConstraint[j].PropertyList.Find("Warp") warpAn = warpProperty.GetAnimationNode() warpFc = warpAn.FCurve keys = warpProperty.GetAnimationNode().FCurve.Keys for key in keys: key.Value = (key.Value / 100.0) * ( (path_length[j] / (2.0 * math.pi * prescaled_radius * scale)) * 100.0) # handle circle wrap if (key.Value > 100.0): key.Value -= 100.0 * int(key.Value / 100.0) print "Original path length was ~ " + str(path_length[j]) + ", circle circumference is " + str( 2.0 * math.pi * radius * scale) print "Warp value has been scaled proportionally by ~" + str( (path_length[j] / (2.0 * math.pi * radius * scale)) * 100.0) + " % " ################################################################################### # Zonal Start Mode # # if mode selected rotate spline points around at angle but maintain start pose ################################################################################### if t.zonal_start_button.State: print "Zonal Start Mode!" # note. Reverse the angle to match convention rotationAngleAV3 = FBVector3d(0.0, -t.angle_a.Value, 0.0) for i in range(0, t.numOfObjects): # grab starting position pre rotation startKeyPositionV4 = t.path[i].PathKeyGet(0) # rotate splines t.path[i].Rotation = FBVector3d(t.path[i].Rotation[0] + rotationAngleAV3[0], t.path[i].Rotation[1] + rotationAngleAV3[1], t.path[i].Rotation[2] + rotationAngleAV3[2]) # have to force a refresh then get key position again. Note, PathKeyGet( 0 ) does not work after rotation, even with a scene refresh. MB bug. Total_GlobalPathEvaluate does work... FBSystem().Scene.Evaluate() rotatedKeyPositionV4 = t.path[i].Total_GlobalPathEvaluate(0.0) # calc difference in new position and use this to set translation offset offsetV4 = rotatedKeyPositionV4 - startKeyPositionV4 # set offsets so pose at start is the same yaw_prop = t.pathConstraint[i].PropertyList.Find("Yaw") if yaw_prop: yaw_prop.SetAnimated(True) existing_data = yaw_prop.Data # set key on first frame yaw_prop.GetAnimationNode().FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data - rotationAngleAV3[1]) # set key on last frame yaw_prop.GetAnimationNode().FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data) FlattenKeys(yaw_prop.GetAnimationNode().FCurve) translation_prop = t.pathConstraint[i].PropertyList.Find("Translation Offset") if translation_prop: translation_prop.SetAnimated(True) existing_data = translation_prop.Data # set key on first frame translation_prop.GetAnimationNode().Nodes[0].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame),existing_data[0] - offsetV4[0]) translation_prop.GetAnimationNode().Nodes[1].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data[1] - offsetV4[1]) translation_prop.GetAnimationNode().Nodes[2].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data[2] - offsetV4[2]) # set key on last frame translation_prop.GetAnimationNode().Nodes[0].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame),existing_data[0]) translation_prop.GetAnimationNode().Nodes[1].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame),existing_data[1]) translation_prop.GetAnimationNode().Nodes[2].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data[2]) for node in translation_prop.GetAnimationNode().Nodes: FlattenKeys(node.FCurve) ################################################################################### # Enable Zonal Helper UI ################################################################################### t.zonalKeyHelper.Enabled = True t.zonalKeyHelper.ReadOnly = False ################################################################################### # Zonal Stop Mode # # if mode selected rotate spline points around at angle but maintain start pose ################################################################################### if t.zonal_stop_button.State: print "Zonal Stop Mode!" # note. Reverse the angle to match convention rotationAngleAV3 = FBVector3d(0.0, -t.angle_a.Value, 0.0) # create a new matrix with the rotation angle rotationAngleMx = FBMatrix() FBTRSToMatrix(rotationAngleMx, FBVector4d(), rotationAngleAV3, FBSVector()) for i in range(0, t.numOfObjects): # grab starting position pre rotation of last point endKeyPositionV4 = t.path[i].PathKeyGet(t.path[i].PathKeyGetCount() - 1) # create vector as offset from mover offsetObjectV3 = FBVector3d(endKeyPositionV4[0], endKeyPositionV4[1], endKeyPositionV4[2]) - mover_end_position rotatedOffsetObjectV4 = FBVector4d() # multiply offsetObjectV3 by rotationAngleMx to find new transformed position FBVectorMatrixMult(rotatedOffsetObjectV4, rotationAngleMx, FBVector4d(offsetObjectV3[0], offsetObjectV3[1], offsetObjectV3[2], 1.0)) # move back to mover rotatedObjectV3 = mover_end_position + FBVector3d(rotatedOffsetObjectV4[0], rotatedOffsetObjectV4[1], rotatedOffsetObjectV4[2]) # work out difference between prerotate and rotated translationOffsetV3 = rotatedObjectV3 - FBVector3d(endKeyPositionV4[0], endKeyPositionV4[1], endKeyPositionV4[2]) # set offsets so pose at end is rotated yaw_prop = t.pathConstraint[i].PropertyList.Find("Yaw") if yaw_prop: yaw_prop.SetAnimated(True) existing_data = yaw_prop.Data # set key on first frame yaw_prop.GetAnimationNode().FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data) # set key on last frame yaw_prop.GetAnimationNode().FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data + rotationAngleAV3[1]) FlattenKeys(yaw_prop.GetAnimationNode().FCurve) translation_prop = t.pathConstraint[i].PropertyList.Find("Translation Offset") if translation_prop: translation_prop.SetAnimated(True) existing_data = translation_prop.Data # set key on first frame translation_prop.GetAnimationNode().Nodes[0].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data[0]) translation_prop.GetAnimationNode().Nodes[1].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data[1]) translation_prop.GetAnimationNode().Nodes[2].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data[2]) # set key on last frame translation_prop.GetAnimationNode().Nodes[0].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data[0] + translationOffsetV3[0]) translation_prop.GetAnimationNode().Nodes[1].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data[1] + translationOffsetV3[1]) translation_prop.GetAnimationNode().Nodes[2].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data[2] + translationOffsetV3[2]) for node in translation_prop.GetAnimationNode().Nodes: FlattenKeys(node.FCurve) ################################################################################### # Enable Zonal Helper UI ################################################################################### t.zonalKeyHelper.Enabled = True t.zonalKeyHelper.ReadOnly = False ################################################################################### # Zonal Advanced Mode # # if mode selected allows user to set both an offset to the moving direction and end heading # mainly for creating strafing style assets ################################################################################### if t.zonal_advanced_button.State: FBSystem().Scene.Evaluate() print "Zonal Advanced Mode!" # note. Reverse the angle to match convention rotationAngleAV3 = FBVector3d(0.0, -t.angle_a.Value, 0.0) for i in range(0, t.numOfObjects): # grab starting position pre rotation startKeyPositionV4 = t.path[i].PathKeyGet(0) # rotate splines t.path[i].Rotation = FBVector3d(t.path[i].Rotation[0] + rotationAngleAV3[0], t.path[i].Rotation[1] + rotationAngleAV3[1], t.path[i].Rotation[2] + rotationAngleAV3[2]) # have to force a refresh then get key position again. Note, PathKeyGet( 0 ) does not work after rotation, even with a scene refresh. MB bug. Total_GlobalPathEvaluate does work... FBSystem().Scene.Evaluate() rotatedKeyPositionV4 = t.path[i].Total_GlobalPathEvaluate(0.0) # calc difference in new position and use this to set translation offset offsetV4 = rotatedKeyPositionV4 - startKeyPositionV4 # set offsets so pose at start is the same yaw_prop = t.pathConstraint[i].PropertyList.Find("Yaw") if yaw_prop: yaw_prop.SetAnimated(True) existing_data = yaw_prop.Data # set key on first frame yaw_prop.GetAnimationNode().FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data - rotationAngleAV3[1]) # set key on last frame yaw_prop.GetAnimationNode().FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data) FlattenKeys(yaw_prop.GetAnimationNode().FCurve) translation_prop = t.pathConstraint[i].PropertyList.Find("Translation Offset") if translation_prop: translation_prop.SetAnimated(True) existing_data = translation_prop.Data # set key on first frame translation_prop.GetAnimationNode().Nodes[0].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data[0] - offsetV4[0]) translation_prop.GetAnimationNode().Nodes[1].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data[1] - offsetV4[1]) translation_prop.GetAnimationNode().Nodes[2].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_start_frame), existing_data[2] - offsetV4[2]) # set key on last frame translation_prop.GetAnimationNode().Nodes[0].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data[0]) translation_prop.GetAnimationNode().Nodes[1].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data[1]) translation_prop.GetAnimationNode().Nodes[2].FCurve.KeyAdd(FBTime(0, 0, 0, t.take_end_frame), existing_data[2]) for node in translation_prop.GetAnimationNode().Nodes: FlattenKeys(node.FCurve) #################################################################################### # incorporate second offset #################################################################################### # go to end frame and eval FBPlayerControl().Goto(FBTime(0, 0, 0, t.take_end_frame)) FBSystem().Scene.Evaluate() # create matrix around mover end position rotationAngleBV3 = FBVector3d(0.0, -t.angle_b.Value, 0.0) straferotationAngleAV3 = -rotationAngleAV3 + rotationAngleBV3 pre_strafe_mover_end_pos = FBVector3d() t.moverObject.GetVector(pre_strafe_mover_end_pos, FBModelTransformationType.kModelTranslation, True) rotationAngleMx = FBMatrix() FBTRSToMatrix(rotationAngleMx, FBVector4d(pre_strafe_mover_end_pos[0], pre_strafe_mover_end_pos[1], pre_strafe_mover_end_pos[2], 0.0), straferotationAngleAV3, FBSVector()) # go through each object and transform current ws position by rotationAngleMx # this should give us the position of object as if it had not been rotated by the spline for i in range(0, t.numOfObjects): current_ws_obj_pos = FBVector3d() t.selected_obj[i].GetVector(current_ws_obj_pos, FBModelTransformationType.kModelTranslation, True) transformed_ws_obj_pos = FBVector4d() FBVectorMatrixMult(transformed_ws_obj_pos, rotationAngleMx, FBVector4d(current_ws_obj_pos[0] - pre_strafe_mover_end_pos[0], current_ws_obj_pos[1] - pre_strafe_mover_end_pos[1], current_ws_obj_pos[2] - pre_strafe_mover_end_pos[2], 0.0)) final_offset_ws_pos = transformed_ws_obj_pos - FBVector4d(current_ws_obj_pos[0], current_ws_obj_pos[1], current_ws_obj_pos[2], 0.0) # finally set prop values yaw_prop = t.pathConstraint[i].PropertyList.Find("Yaw") if yaw_prop: existing_data = yaw_prop.Data # update key on last frame to be same as first yaw_prop.GetAnimationNode().FCurve.Keys[-1].Value = yaw_prop.GetAnimationNode().FCurve.Keys[ 0].Value - t.angle_b.Value translation_prop = t.pathConstraint[i].PropertyList.Find("Translation Offset") if translation_prop: existing_data = translation_prop.Data # update keys on last frame translation_prop.GetAnimationNode().Nodes[0].FCurve.Keys[-1].Value = final_offset_ws_pos[0] translation_prop.GetAnimationNode().Nodes[1].FCurve.Keys[-1].Value = final_offset_ws_pos[1] translation_prop.GetAnimationNode().Nodes[2].FCurve.Keys[-1].Value = final_offset_ws_pos[2] ################################################################################### # Enable Zonal Helper UI ################################################################################### t.zonalKeyHelper.Enabled = True t.zonalKeyHelper.ReadOnly = False ################################################################################### # update UI ################################################################################### # turn expressions back on # ToggleDevices() t.delete_button.Enabled = True # prevent user from altering curve steps t.slider.Enabled = False t.status.Enabled = False # prevent user from altering Mode t.circle_button.Enabled = False t.spline_button.Enabled = False t.zonal_start_button.Enabled = False t.zonal_stop_button.Enabled = False if t.numOfObjects == 1: t.pin_button.Caption = "Plot MotionWarp from '" + t.selected_obj_name[0] + "'" else: t.pin_button.Caption = "Plot MotionWarp from " + str(t.numOfObjects) + " objects" ################################################################################### # restore user timeslider ################################################################################### FBPlayerControl().Goto(FBTime(0, 0, 0, t.current_frame)) t.pinned = True else: ######################################## # Apply Motionwarp! ######################################## # if user has selected character mode then simply plot current character if t.plot_mode_dropdown.Items[t.plot_mode_dropdown.ItemIndex] == "Plot Character": current_character = FBApplication().CurrentCharacter plotToSkeleton( current_character ) # else plot each individual selected node else: clearSelection() for j in range(0, t.numOfObjects): # plot selected object t.selected_obj[j].Selected = True FBSystem().CurrentTake.PlotTakeOnSelected(plotOptions()) # eval scene following plot FBSystem().Scene.Evaluate() # if we were in a zonal mode store the settings to custom properties if t.zonal_start_button.State or t.zonal_stop_button.State or t.zonal_advanced_button.State: storeZonalValues() if t.zonal_start_button.State or t.zonal_stop_button.State or t.zonal_advanced_button.State: if hasattr(t, 'zonal'): closeZonalHelperWindow() # and we're done... cleanupAndReset() def plotOptions(): plot_options = FBPlotOptions() plot_options.PlotAllTakes = False plot_options.PlotOnFrame = True plot_options.PlotPeriod = FBTime(0, 0, 0, 1) plot_options.RotationFilterToApply = FBRotationFilter.kFBRotationFilterUnroll plot_options.UseConstantKeyReducer = False plot_options.ConstantKeyReducerKeepOneKey = True plot_options.PlotTranslationOnRootOnly = False return plot_options def RotateVectorRadians(vector, radians): ca = math.cos(radians) sa = math.sin(radians) return FBVector3d(ca * vector[0] - sa * vector[1], sa * vector[0] + ca * vector[1], vector[2]) #################################################################### # UI definition #################################################################### def PopulateTool(t): # get selected object models = FBModelList() FBGetSelectedModels(models) t.selected_obj = [] t.selected_obj_name = [] parseMotionWarpXML(t) if len(models) == 0: FBMessageBox("Message", "Nothing selected", "OK", None, None) elif t.XMLRead == False: print str(t.XMLRead) FBMessageBox("Message", ("Could not find: " + t.motionWarpPath), "OK", None, None) else: # set up arrays t.null = [] t.midNull = [] t.pathNull = [] t.path = [] t.pathConstraint = [] t.midNullConstraint = [] t.constraint = [] ui_x_offset = 5 ui_y_offset = 5 ui_x_width = 240 ui_button_height = 30 ui_button_width = 40 ui_column_a = 20 ui_column_b = 110 ui_column_c = 100 for obj in models: t.selected_obj.append(obj) t.selected_obj_name.append(obj.Name) t.numOfObjects = len(models) # Pin button x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("go_button_region", "go_button_region", x, y, w, h) t.pin_button = FBButton() t.SetControl("go_button_region", t.pin_button) t.pin_button.Visible = True t.pin_button.ReadOnly = False t.pin_button.Enabled = True t.pin_button.Hint = "" if t.numOfObjects == 1: t.pin_button.Caption = "Apply MotionWarp to '" + t.selected_obj_name[0] + "'" else: t.pin_button.Caption = "Apply MotionWarp to " + str(t.numOfObjects) + " objects" t.pin_button.State = 0 t.pin_button.Style = FBButtonStyle.kFBPushButton t.pin_button.Justify = FBTextJustify.kFBTextJustifyCenter t.pin_button.Look = FBButtonLook.kFBLookNormal t.pin_button.OnClick.Add(goButtonCallBack) # set pin state t.pinned = False # Delete button ui_y_offset += 35 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("delete_button_region", "delete_button_region", x, y, w, h) t.delete_button = FBButton() t.SetControl("delete_button_region", t.delete_button) t.delete_button.Visible = True t.delete_button.ReadOnly = False t.delete_button.Enabled = False t.delete_button.Hint = "" if t.numOfObjects == 1: t.delete_button.Caption = "Delete MotionWarp from '" + t.selected_obj_name[0] + "'" else: t.delete_button.Caption = "Delete MotionWarp from " + str(t.numOfObjects) + " objects" t.delete_button.State = 0 t.delete_button.Style = FBButtonStyle.kFBPushButton t.delete_button.Justify = FBTextJustify.kFBTextJustifyCenter t.delete_button.Look = FBButtonLook.kFBLookNormal t.delete_button.OnClick.Add(deleteButtonCallBack) ########################################################################################## # Mode Border and Controls ########################################################################################## ui_y_offset += 40 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(100, FBAttachType.kFBAttachNone, "") t.AddRegion("mode_border", "Mode", x, y, w, h) t.SetBorder("mode_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ui_y_offset += 5 # Buttons x = FBAddRegionParam(ui_column_a, FBAttachType.kFBAttachTop, "circle_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("circle_button", "circle_button", x, y, w, h) t.circle_button = FBButton() t.SetControl("circle_button", t.circle_button) t.circle_button.Visible = True t.circle_button.ReadOnly = False t.circle_button.Enabled = True t.circle_button.Hint = "" t.circle_button.Caption = "Turn Circle" t.circle_button.State = 1 t.circle_button.Style = FBButtonStyle.kFBRadioButton t.circle_button.Justify = FBTextJustify.kFBTextJustifyLeft t.circle_button.Look = FBButtonLook.kFBLookNormal t.circle_button.OnClick.Add(modeButtonsCallback) x = FBAddRegionParam(ui_column_b, FBAttachType.kFBAttachTop, "spline_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("spline_button", "spline_button", x, y, w, h) t.spline_button = FBButton() t.SetControl("spline_button", t.spline_button) t.spline_button.Visible = True t.spline_button.ReadOnly = False t.spline_button.Enabled = True t.spline_button.Hint = "" t.spline_button.Caption = "Spline" t.spline_button.State = 0 t.spline_button.Style = FBButtonStyle.kFBRadioButton t.spline_button.Justify = FBTextJustify.kFBTextJustifyLeft t.spline_button.Look = FBButtonLook.kFBLookNormal t.spline_button.OnClick.Add(modeButtonsCallback) ui_y_offset += 30 x = FBAddRegionParam(ui_column_a, FBAttachType.kFBAttachTop, "zonal_start") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("zonal_start", "zonal_start", x, y, w, h) t.zonal_start_button = FBButton() t.SetControl("zonal_start", t.zonal_start_button) t.zonal_start_button.Visible = True t.zonal_start_button.ReadOnly = False t.zonal_start_button.Enabled = True t.zonal_start_button.Hint = "" t.zonal_start_button.Caption = "Zonal Start" t.zonal_start_button.State = 0 t.zonal_start_button.Style = FBButtonStyle.kFBRadioButton t.zonal_start_button.Justify = FBTextJustify.kFBTextJustifyLeft t.zonal_start_button.Look = FBButtonLook.kFBLookNormal t.zonal_start_button.OnClick.Add(modeButtonsCallback) x = FBAddRegionParam(ui_column_b, FBAttachType.kFBAttachTop, "zonal_stop") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width / 2 + 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("zonal_stop", "zonal_stop", x, y, w, h) t.zonal_stop_button = FBButton() t.SetControl("zonal_stop", t.zonal_stop_button) t.zonal_stop_button.Visible = True t.zonal_stop_button.ReadOnly = False t.zonal_stop_button.Enabled = True t.zonal_stop_button.Hint = "" t.zonal_stop_button.Caption = "Zonal Stop / Idle Turn" t.zonal_stop_button.State = 0 t.zonal_stop_button.Style = FBButtonStyle.kFBRadioButton t.zonal_stop_button.Justify = FBTextJustify.kFBTextJustifyLeft t.zonal_stop_button.Look = FBButtonLook.kFBLookNormal t.zonal_stop_button.OnClick.Add(modeButtonsCallback) t.mode_radio_buttons = FBButtonGroup() t.mode_radio_buttons.Add(t.circle_button) t.mode_radio_buttons.Add(t.spline_button) t.mode_radio_buttons.Add(t.zonal_start_button) t.mode_radio_buttons.Add(t.zonal_stop_button) ui_y_offset += 30 x = FBAddRegionParam(ui_column_a, FBAttachType.kFBAttachTop, "zonal_advanced") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width / 2 + 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("zonal_advanced", "zonal_advanced", x, y, w, h) t.zonal_advanced_button = FBButton() t.SetControl("zonal_advanced", t.zonal_advanced_button) t.zonal_advanced_button.Visible = True t.zonal_advanced_button.ReadOnly = False t.zonal_advanced_button.Enabled = True t.zonal_advanced_button.Hint = "Allows user to set both Moving Direction and Heading Direction offset." t.zonal_advanced_button.Caption = "Zonal Advanced" t.zonal_advanced_button.State = 0 t.zonal_advanced_button.Style = FBButtonStyle.kFBRadioButton t.zonal_advanced_button.Justify = FBTextJustify.kFBTextJustifyLeft t.zonal_advanced_button.Look = FBButtonLook.kFBLookNormal t.zonal_advanced_button.OnClick.Add(modeButtonsCallback) t.mode_radio_buttons = FBButtonGroup() t.mode_radio_buttons.Add(t.circle_button) t.mode_radio_buttons.Add(t.spline_button) t.mode_radio_buttons.Add(t.zonal_start_button) t.mode_radio_buttons.Add(t.zonal_stop_button) t.mode_radio_buttons.Add(t.zonal_advanced_button) ########################################################################################## # Circle Options Border ########################################################################################## ui_y_offset += 50 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(170, FBAttachType.kFBAttachNone, "") t.AddRegion("circle_border", "Circle Radius & Direction", x, y, w, h) t.SetBorder("circle_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ########################################################################################## # Circle Options ########################################################################################## ui_y_offset += 10 # MODE x = FBAddRegionParam(ui_column_a, FBAttachType.kFBAttachTop, "radius_mode_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("radius_mode_button", "radius_mode_button", x, y, w, h) t.radius_mode_button = FBButton() t.SetControl("radius_mode_button", t.radius_mode_button) t.radius_mode_button.Visible = True t.radius_mode_button.ReadOnly = False t.radius_mode_button.Enabled = True t.radius_mode_button.Hint = "User defines radius of circle in Metres." t.radius_mode_button.Caption = "Radius" t.radius_mode_button.State = 1 t.radius_mode_button.Style = FBButtonStyle.kFBRadioButton t.radius_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.radius_mode_button.Look = FBButtonLook.kFBLookNormal t.radius_mode_button.OnClick.Add(radiusModeButtonsCallback) x = FBAddRegionParam(85, FBAttachType.kFBAttachTop, "manual_AV_mode_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(80, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("manual_AV_mode_button", "manual_AV_mode_button", x, y, w, h) t.manual_AV_mode_button = FBButton() t.SetControl("manual_AV_mode_button", t.manual_AV_mode_button) t.manual_AV_mode_button.Visible = True t.manual_AV_mode_button.ReadOnly = False t.manual_AV_mode_button.Enabled = True t.manual_AV_mode_button.Hint = "Radius of turn its automatically generated based on average velocity to create a wide turning circle." t.manual_AV_mode_button.Caption = "Manual A.V." t.manual_AV_mode_button.State = 0 t.manual_AV_mode_button.Style = FBButtonStyle.kFBRadioButton t.manual_AV_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.manual_AV_mode_button.Look = FBButtonLook.kFBLookNormal t.manual_AV_mode_button.OnClick.Add(radiusModeButtonsCallback) x = FBAddRegionParam(170, FBAttachType.kFBAttachTop, "auto_AV_mode_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(70, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("auto_AV_mode_button", "auto_AV_mode_button", x, y, w, h) t.auto_AV_mode_button = FBButton() t.SetControl("auto_AV_mode_button", t.auto_AV_mode_button) t.auto_AV_mode_button.Visible = True t.auto_AV_mode_button.ReadOnly = False t.auto_AV_mode_button.Enabled = True t.auto_AV_mode_button.Hint = "Radius of turn its automatically generated based on average velocity to create a tight turning circle." t.auto_AV_mode_button.Caption = "Auto A.V." t.auto_AV_mode_button.State = 0 t.auto_AV_mode_button.Style = FBButtonStyle.kFBRadioButton t.auto_AV_mode_button.Justify = FBTextJustify.kFBTextJustifyLeft t.auto_AV_mode_button.Look = FBButtonLook.kFBLookNormal t.auto_AV_mode_button.OnClick.Add(radiusModeButtonsCallback) t.circle_direction_radio_buttons = FBButtonGroup() t.circle_direction_radio_buttons.Add(t.manual_AV_mode_button) t.circle_direction_radio_buttons.Add(t.auto_AV_mode_button) t.circle_direction_radio_buttons.Add(t.radius_mode_button) ui_y_offset += 35 x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("radius_edit", "radius_edit", x, y, w, h) t.radius = FBEditNumber() t.SetControl("radius_edit", t.radius) # t.radius.Justify = FBTextJustify.kFBTextJustifyLeft t.radius.Visible = True t.radius.ReadOnly = False t.radius.Enabled = True t.radius.Value = 1.0 t.radius.Hint = "Radius of circle in Metres" t.radius.Min = 0.0 ui_y_offset += 30 # Drop Down x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(25, FBAttachType.kFBAttachNone, "") t.AddRegion("dropdown", "dropdown", x, y, w, h) t.dropDownList = FBList() t.SetControl("dropdown", t.dropDownList) setupTurnParameterList(t) t.dropDownList.Enabled = False t.dropDownList.ReadOnly = True ui_y_offset += 30 # Buttons x = FBAddRegionParam(ui_column_a, FBAttachType.kFBAttachTop, "small_turn_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("small_turn_button", "small_turn_button", x, y, w, h) t.small_turn_button = FBButton() t.SetControl("small_turn_button", t.small_turn_button) t.small_turn_button.Visible = True t.small_turn_button.ReadOnly = False t.small_turn_button.Enabled = False t.small_turn_button.Hint = "Create a turn normally used for L2 and R2" t.small_turn_button.Caption = "Small Turn" t.small_turn_button.State = 1 t.small_turn_button.Style = FBButtonStyle.kFBRadioButton t.small_turn_button.Justify = FBTextJustify.kFBTextJustifyLeft t.small_turn_button.Look = FBButtonLook.kFBLookNormal x = FBAddRegionParam(ui_column_b, FBAttachType.kFBAttachTop, "large_turn_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("large_turn_button", "large_turn_button", x, y, w, h) t.large_turn_button = FBButton() t.SetControl("large_turn_button", t.large_turn_button) t.large_turn_button.Visible = True t.large_turn_button.ReadOnly = False t.large_turn_button.Enabled = False t.large_turn_button.Hint = "Create a turn normally used for L1 and R1" t.large_turn_button.Caption = "Large Turn" t.large_turn_button.State = 0 t.large_turn_button.Style = FBButtonStyle.kFBRadioButton t.large_turn_button.Justify = FBTextJustify.kFBTextJustifyLeft t.large_turn_button.Look = FBButtonLook.kFBLookNormal t.turn_radius = FBButtonGroup() t.turn_radius.Add(t.small_turn_button) t.turn_radius.Add(t.large_turn_button) ui_y_offset += 30 # Buttons x = FBAddRegionParam(ui_column_a, FBAttachType.kFBAttachTop, "clockwise_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("clockwise_button", "clockwise_button", x, y, w, h) t.clockwise_button = FBButton() t.SetControl("clockwise_button", t.clockwise_button) t.clockwise_button.Visible = True t.clockwise_button.ReadOnly = False t.clockwise_button.Enabled = True t.clockwise_button.Hint = "" t.clockwise_button.Caption = "Clock" t.clockwise_button.State = 1 t.clockwise_button.Style = FBButtonStyle.kFBRadioButton t.clockwise_button.Justify = FBTextJustify.kFBTextJustifyLeft t.clockwise_button.Look = FBButtonLook.kFBLookNormal x = FBAddRegionParam(ui_column_b, FBAttachType.kFBAttachTop, "counterclockwise_button") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(90, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("counterclockwise_button", "counterclockwise_button", x, y, w, h) t.counter_button = FBButton() t.SetControl("counterclockwise_button", t.counter_button) t.counter_button.Visible = True t.counter_button.ReadOnly = False t.counter_button.Enabled = True t.counter_button.Hint = "" t.counter_button.Caption = "Counter" t.counter_button.State = 0 t.counter_button.Style = FBButtonStyle.kFBRadioButton t.counter_button.Justify = FBTextJustify.kFBTextJustifyLeft t.counter_button.Look = FBButtonLook.kFBLookNormal t.circle_direction_radio_buttons = FBButtonGroup() t.circle_direction_radio_buttons.Add(t.clockwise_button) t.circle_direction_radio_buttons.Add(t.counter_button) ########################################################################################## # Spline Points Border ########################################################################################## ui_y_offset += 50 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(60, FBAttachType.kFBAttachNone, "") t.AddRegion("spline_border", "Spline Points", x, y, w, h) t.SetBorder("spline_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ########################################################################################## # Spline Points Options ########################################################################################## ui_y_offset += 15 x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("slider", "slider", x, y, w, h) t.slider = FBSlider() t.slider.Value = 0.0 t.slider.ReadOnly = True t.slider.Enabled = False t.slider.Orientation = FBOrientation.kFBHorizontal t.slider.OnTransaction.Add(sliderUpdated) t.SetControl("slider", t.slider) # Curve Points Label ui_y_offset += 25 x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("status_region", "status_region", x, y, w, h) t.status = FBLabel() t.SetControl("status_region", t.status) t.status.Justify = FBTextJustify.kFBTextJustifyCenter t.status.Visible = True t.status.ReadOnly = False t.status.Enabled = False t.status.Caption = "2" ########################################################################################## # Zonal Angle Border ########################################################################################## ui_y_offset += 35 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(100, FBAttachType.kFBAttachNone, "") t.AddRegion("zonal_angle_border", "Zonal Angle Offset", x, y, w, h) t.SetBorder("zonal_angle_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ui_y_offset += 15 x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("angle_a_edit", "angle_a_edit", x, y, w, h) t.angle_a = FBEditNumber() t.SetControl("angle_a_edit", t.angle_a) # t.angle_a.Justify = FBTextJustify.kFBTextJustifyLeft t.angle_a.Visible = True t.angle_a.Hint = "Angle to offset is relative to Mover orientation for start and stop." t.angle_a.ReadOnly = True t.angle_a.Enabled = False t.angle_a.Value = 0.0 t.angle_a.Min = -180.0 t.angle_a.Max = 180.0 ui_y_offset += 25 x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("angle_b_edit", "angle_b_edit", x, y, w, h) t.angle_b = FBEditNumber() t.SetControl("angle_b_edit", t.angle_b) # t.angle_b.Justify = FBTextJustify.kFBTextJustifyLeft t.angle_b.Visible = True t.angle_b.Hint = "Secondary Angle for Advanced mode only." t.angle_b.ReadOnly = True t.angle_b.Enabled = False t.angle_b.Value = 0.0 t.angle_b.Min = -180.0 t.angle_b.Max = 180.0 ui_y_offset += 30 ########################################################################################## # Zonal Key Helper ########################################################################################## x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(20, FBAttachType.kFBAttachNone, "") t.AddRegion("zonal_key_helper", "zonal_key_helper", x, y, w, h) t.zonalKeyHelper = FBButton() t.SetControl("zonal_key_helper", t.zonalKeyHelper) t.zonalKeyHelper.Visible = True t.zonalKeyHelper.ReadOnly = False t.zonalKeyHelper.Enabled = False t.zonalKeyHelper.Hint = "Will launch a seperate window to aid with key placement." t.zonalKeyHelper.Caption = "Zonal Key Helper" t.zonalKeyHelper.Style = FBButtonStyle.kFBPushButton t.zonalKeyHelper.Justify = FBTextJustify.kFBTextJustifyCenter t.zonalKeyHelper.Look = FBButtonLook.kFBLookNormal t.zonalKeyHelper.OnClick.Add(zonalKeyHelperCallBack) ########################################################################################## # Advanced Settings/Plot Mode ########################################################################################## ui_y_offset += 45 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(70, FBAttachType.kFBAttachNone, "") t.AddRegion("advanced_settings_border", "Advanced Settings", x, y, w, h) t.SetBorder("advanced_settings_border", FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) ui_y_offset += 10 # Drop Down x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(25, FBAttachType.kFBAttachNone, "") t.AddRegion("advanced_settings_dropdown", "advanced_settings_dropdown", x, y, w, h) t.advanced_settings_dropdown = FBList() t.SetControl("advanced_settings_dropdown", t.advanced_settings_dropdown) setupAdvancedSettingsList(t) t.advanced_settings_dropdown.Enabled = False t.advanced_settings_dropdown.ReadOnly = True ui_y_offset += 30 # Drop Down x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width - 10, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(25, FBAttachType.kFBAttachNone, "") t.AddRegion("plot_mode_dropdown", "plot_mode_dropdown", x, y, w, h) t.plot_mode_dropdown = FBList() t.plot_mode_dropdown.Hint = "Defines how results are plotted down. \n\n Plot Character means it will use the current character as defined in the Character Controls window and plot from rig down to skeleton.\n\n Plot Selected Nodes will treat each selected object seperately and plot each node. This is necessary when runnning on non-character entities." t.SetControl("plot_mode_dropdown", t.plot_mode_dropdown) setupPlotModeList(t) ########################################################################################## # Calculate Tool Window Size and Draw ########################################################################################## t.StartSizeX = 267 t.StartSizeY = ui_y_offset + 70 ShowTool(t) #################################################################### # Zonal Methods #################################################################### def PhaseToKeyFrame(phase): frame = t.take_start_frame + ((t.take_end_frame - t.take_start_frame) * phase) return frame def KeyFrameToPhase(keyframe): phase = float(keyframe - t.take_start_frame) / float(t.take_end_frame - t.take_start_frame) return phase #################################################################### # Zonal UI callbacks #################################################################### def ScaleFCurveTime(pFCurve, pTimePivot, pScaleValue, offset): lPivotDouble = pTimePivot.GetSecondDouble() lTime = FBTime(0) for lKey in pFCurve.Keys: lTime.SetSecondDouble(lPivotDouble + (lKey.Time.GetSecondDouble() - lPivotDouble) * pScaleValue) lTime += FBTime(0, 0, 0, int(offset)) lKey.Time = lTime def Scale(pAnimationNode, pTimePivot, pScaleValue, offset): if len(pAnimationNode.Nodes) == 0: ScaleFCurveTime(pAnimationNode.FCurve, pTimePivot, pScaleValue, offset) else: for lNode in pAnimationNode.Nodes: Scale(lNode, pTimePivot, pScaleValue, offset) def zonalSliderUpdated(control, event=''): properties = ['Yaw', 'Translation Offset/X', 'Translation Offset/Y', 'Translation Offset/Z'] # if start slider, make sure end slider isn't any lower in value if control.StartSlider: if t.zonal.startSlider[control.Id].Value >= t.zonal.endSlider[control.Id].Value: t.zonal.startSlider[control.Id].Value = t.zonal.endSlider[control.Id].Value - KeyFrameToPhase( t.take_start_frame + 1) # if end slider, make sure start slider isn't any higher in value else: if t.zonal.endSlider[control.Id].Value <= t.zonal.startSlider[control.Id].Value: t.zonal.endSlider[control.Id].Value = t.zonal.startSlider[control.Id].Value + KeyFrameToPhase( t.take_start_frame + 1) # calculate new times for yaw and translation offset based on slider positions new_start_frame = int(PhaseToKeyFrame(t.zonal.startSlider[control.Id].Value)) new_end_frame = int(PhaseToKeyFrame(t.zonal.endSlider[control.Id].Value)) old_start_frame = int(t.zonal.startSlider[control.Id].range_min) old_end_frame = int(t.zonal.endSlider[control.Id].range_max) diff_start_frame = int(new_start_frame - old_start_frame) # make sure we're not setting invalid times by making end frame always more than start if new_end_frame <= new_start_frame: new_end_frame += 1 # new desired slider range desired_range = new_end_frame - new_start_frame # existing range existing_range = old_end_frame - old_start_frame # new scale ratio scale_ratio = float(desired_range) / existing_range # scale all keys in fcurve for each property on constraint for property in properties: animation_node = FindPropertyAnimationNode(property, t.pathConstraint[control.Id]) Scale(animation_node, FBTime(0, 0, 0, old_start_frame), scale_ratio, diff_start_frame) ''' DEBUG for key in animation_node.FCurve.Keys: print "key.LeftTangentWeight is " + str(key.LeftTangentWeight) print "key.RightTangentWeight is " + str(key.RightTangentWeight) print "key.Interpolation is " + str(key.Interpolation) print "key.LeftDerivative is " + str(key.LeftDerivative) print "key.RightDerivative is " + str(key.RightDerivative) print "key.TangentConstantMode is " + str(key.TangentConstantMode) print "key.TangentMode is " + str(key.TangentMode) ''' # update edit boxes t.zonal.startEdit[control.Id].Value = new_start_frame t.zonal.endEdit[control.Id].Value = new_end_frame t.zonal.endSlider[control.Id].range_max = new_end_frame t.zonal.startSlider[control.Id].range_min = new_start_frame def zonalEditUpdated(control, event=''): if control.StartEdit: if t.zonal.startEdit[control.Id].Value >= t.zonal.endEdit[control.Id].Value: t.zonal.startEdit[control.Id].Value = t.zonal.endEdit[control.Id].Value - 1 t.zonal.startSlider[control.Id].Value = KeyFrameToPhase(control.Value) zonalSliderUpdated(t.zonal.startSlider[control.Id]) else: if t.zonal.endEdit[control.Id].Value <= t.zonal.startEdit[control.Id].Value: t.zonal.endEdit[control.Id].Value = t.zonal.startEdit[control.Id].Value + 1 t.zonal.endSlider[control.Id].Value = KeyFrameToPhase(control.Value) zonalSliderUpdated(t.zonal.endSlider[control.Id]) def zonalEditButtonClicked(control, event): current_keyframe = FBSystem().LocalTime.GetFrame() if control.StartEditButton: t.zonal.startSlider[control.Id].Value = KeyFrameToPhase(current_keyframe) # force slider callback zonalSliderUpdated(t.zonal.startSlider[control.Id]) t.zonal.startEdit[control.Id].Value = current_keyframe else: t.zonal.endSlider[control.Id].Value = KeyFrameToPhase(current_keyframe) # force slider callback zonalSliderUpdated(t.zonal.endSlider[control.Id]) t.zonal.endEdit[control.Id].Value = current_keyframe def fcurveButtonClicked(control, event): # select pathconstraint clearSelection() t.pathConstraint[control.Id].Selected = True # add constraint offset animation nodes yaw_prop = t.pathConstraint[control.Id].PropertyList.Find("Yaw") if yaw_prop: # select custom property yaw_prop.SetFocus(True) translation_prop = t.pathConstraint[control.Id].PropertyList.Find("Translation Offset") if translation_prop: # select custom property translation_prop.SetFocus(True) # close zonal key helper window ( to avoid sync issues ) if t.zonal_start_button.State or t.zonal_stop_button.State: if hasattr(t, 'zonal'): closeZonalHelperWindow() ShowToolByName("FCurve") def zonalRestoreButtonClicked(control, event): restoreZonalValues() # update UI for i in range(len(t.zonal.startSlider)): new_start_frame = t.zonal.startSlider[i].range_min new_end_frame = t.zonal.endSlider[i].range_max new_start_phase = KeyFrameToPhase(new_start_frame) new_end_phase = KeyFrameToPhase(new_end_frame) t.zonal.startEdit[i].Value = new_start_frame t.zonal.endEdit[i].Value = new_end_frame t.zonal.startSlider[i].Value = new_start_phase t.zonal.endSlider[i].Value = new_end_phase #################################################################### # Zonal UI definition #################################################################### def PopulateZonalUI(zonal): t.zonal.startSlider = [] t.zonal.startEdit = [] t.zonal.startEditButton = [] t.zonal.endSlider = [] t.zonal.endEdit = [] t.zonal.endEditButton = [] t.zonal.fcurveButton = [] ui_x_offset = 10 ui_y_offset = 10 slider_height = 15 slider_width = 275 phase_box_width = 30 border_offset = 0 key_button_width = 15 fcurve_button_width = 25 ui_x_width = slider_width + phase_box_width + key_button_width + 8 ui_y_height = 40 object_counter = 0 for object in t.selected_obj_name: start_frame = t.take_start_frame end_frame = t.take_end_frame # fetch any exisiting values in case window has been in previous use # use yaw property as start/end guide though we could use any other properties yaw_prop = t.pathConstraint[object_counter].PropertyList.Find("Yaw") if yaw_prop: start_frame = yaw_prop.GetAnimationNode().FCurve.Keys[0].Time.GetFrame() end_frame = yaw_prop.GetAnimationNode().FCurve.Keys[-1].Time.GetFrame() ########################################################################################## # Border ########################################################################################## object_counter_str = str(object_counter) x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachNone, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(slider_height * 3 + 10, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("object_border" + object_counter_str, object, x, y, w, h) t.zonal.SetBorder("object_border" + object_counter_str, FBBorderStyle.kFBEmbossBorder, True, True, 2, 0, 90.0, 0) border_offset = ui_y_offset ########################################################################################## # Start Slider ########################################################################################## # add slider for start phase ui_y_offset += slider_height x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(slider_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(slider_height, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("start_slider" + object_counter_str, "start_slider" + object_counter_str, x, y, w, h) t.zonal.startSlider.append(FBSlider()) # add id to slider t.zonal.startSlider[object_counter].Id = object_counter t.zonal.startSlider[object_counter].StartSlider = True t.zonal.startSlider[object_counter].Value = KeyFrameToPhase(start_frame) t.zonal.startSlider[object_counter].range_min = start_frame t.zonal.startSlider[object_counter].ReadOnly = False t.zonal.startSlider[object_counter].Enabled = True t.zonal.startSlider[object_counter].Orientation = FBOrientation.kFBHorizontal t.zonal.startSlider[object_counter].OnTransaction.Add(zonalSliderUpdated) t.zonal.SetControl("start_slider" + object_counter_str, t.zonal.startSlider[object_counter]) ########################################################################################## # Start Edit ########################################################################################## # add edit box x = FBAddRegionParam(slider_width + 10, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(phase_box_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(slider_height, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("start_edit" + object_counter_str, "start_edit" + object_counter_str, x, y, w, h) t.zonal.startEdit.append(FBEditNumber()) t.zonal.startEdit[object_counter].Id = object_counter t.zonal.startEdit[object_counter].StartEdit = True t.zonal.startEdit[object_counter].Visible = True t.zonal.startEdit[object_counter].ReadOnly = False t.zonal.startEdit[object_counter].Enabled = True t.zonal.startEdit[object_counter].Precision = 0 t.zonal.startEdit[object_counter].Min = t.take_start_frame t.zonal.startEdit[object_counter].Max = t.take_end_frame t.zonal.startEdit[object_counter].Value = start_frame t.zonal.SetControl("start_edit" + object_counter_str, t.zonal.startEdit[object_counter]) t.zonal.startEdit[object_counter].OnChange.Add(zonalEditUpdated) ########################################################################################## # Start Edit Button ########################################################################################## # add edit box x = FBAddRegionParam(slider_width + 12 + phase_box_width, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(key_button_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(slider_height, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("start_button" + object_counter_str, "start_button" + object_counter_str, x, y, w, h) t.zonal.startEditButton.append(FBButton()) t.zonal.startEditButton[object_counter].Id = object_counter t.zonal.startEditButton[object_counter].StartEditButton = True t.zonal.startEditButton[object_counter].Visible = True t.zonal.startEditButton[object_counter].ReadOnly = False t.zonal.startEditButton[object_counter].Enabled = True t.zonal.startEditButton[object_counter].Caption = '<' t.zonal.startEditButton[object_counter].Hint = 'Set keyframe value to be the same as timeslider.' t.zonal.SetControl("start_button" + object_counter_str, t.zonal.startEditButton[object_counter]) t.zonal.startEditButton[object_counter].OnClick.Add(zonalEditButtonClicked) ########################################################################################## # End Slider ########################################################################################## # add slider for end phase ui_y_offset += slider_height x = FBAddRegionParam(ui_x_offset + 5, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(slider_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(slider_height, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("end_slider" + object_counter_str, "end_slider" + object_counter_str, x, y, w, h) t.zonal.endSlider.append(FBSlider()) t.zonal.endSlider[object_counter].Id = object_counter t.zonal.endSlider[object_counter].StartSlider = False t.zonal.endSlider[object_counter].Value = KeyFrameToPhase(end_frame) t.zonal.endSlider[object_counter].range_max = end_frame t.zonal.endSlider[object_counter].ReadOnly = False t.zonal.endSlider[object_counter].Enabled = True t.zonal.endSlider[object_counter].Orientation = FBOrientation.kFBHorizontal t.zonal.endSlider[object_counter].OnTransaction.Add(zonalSliderUpdated) t.zonal.SetControl("end_slider" + object_counter_str, t.zonal.endSlider[object_counter]) ########################################################################################## # End Edit ########################################################################################## # add edit box x = FBAddRegionParam(slider_width + 10, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(phase_box_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(slider_height, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("end_edit" + object_counter_str, "end_edit" + object_counter_str, x, y, w, h) t.zonal.endEdit.append(FBEditNumber()) t.zonal.endEdit[object_counter].Id = object_counter t.zonal.endEdit[object_counter].StartEdit = False t.zonal.endEdit[object_counter].Visible = True t.zonal.endEdit[object_counter].ReadOnly = False t.zonal.endEdit[object_counter].Enabled = True t.zonal.endEdit[object_counter].Value = end_frame t.zonal.endEdit[object_counter].Precision = 0 t.zonal.endEdit[object_counter].Min = t.take_start_frame t.zonal.endEdit[object_counter].Max = t.take_end_frame t.zonal.endEdit[object_counter].Value = PhaseToKeyFrame(t.zonal.endSlider[object_counter].Value) t.zonal.SetControl("end_edit" + object_counter_str, t.zonal.endEdit[object_counter]) t.zonal.endEdit[object_counter].OnChange.Add(zonalEditUpdated) ########################################################################################## # End Edit Button ########################################################################################## # add edit box x = FBAddRegionParam(slider_width + 12 + phase_box_width, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(key_button_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(slider_height, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("end_button" + object_counter_str, "end_button" + object_counter_str, x, y, w, h) t.zonal.endEditButton.append(FBButton()) t.zonal.endEditButton[object_counter].Id = object_counter t.zonal.endEditButton[object_counter].StartEditButton = False t.zonal.endEditButton[object_counter].Visible = True t.zonal.endEditButton[object_counter].ReadOnly = False t.zonal.endEditButton[object_counter].Enabled = True t.zonal.endEditButton[object_counter].Caption = '<' t.zonal.endEditButton[object_counter].Hint = 'Set keyframe value to be the same as timeslider.' t.zonal.SetControl("end_button" + object_counter_str, t.zonal.endEditButton[object_counter]) t.zonal.endEditButton[object_counter].OnClick.Add(zonalEditButtonClicked) ########################################################################################## # Fcurve Button ########################################################################################## # add edit box x = FBAddRegionParam(slider_width + 12 + phase_box_width + 28, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(border_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(fcurve_button_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(50, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("fcurve_button" + object_counter_str, "fcurve_button" + object_counter_str, x, y, w, h) t.zonal.fcurveButton.append(FBButton()) t.zonal.fcurveButton[object_counter].Id = object_counter t.zonal.fcurveButton[object_counter].Visible = True t.zonal.fcurveButton[object_counter].ReadOnly = False t.zonal.fcurveButton[object_counter].Enabled = True t.zonal.fcurveButton[object_counter].Caption = '~' t.zonal.fcurveButton[ object_counter].Hint = 'Open curve editor window for finer control with relevant properties.' t.zonal.SetControl("fcurve_button" + object_counter_str, t.zonal.fcurveButton[object_counter]) t.zonal.fcurveButton[object_counter].OnClick.Add(fcurveButtonClicked) ########################################################################################## # Next object in UI loop ########################################################################################## ui_y_offset += slider_height * 2 + 10 object_counter += 1 ########################################################################################## # Restore Button ########################################################################################## ui_y_offset -= 5 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(slider_height + 5, FBAttachType.kFBAttachNone, "") t.zonal.AddRegion("restore_button", "restore_button", x, y, w, h) t.zonal.restoreButton = FBButton() t.zonal.SetControl("restore_button", t.zonal.restoreButton) t.zonal.restoreButton.Visible = True save_file_exists = DoesSaveFileExist() if save_file_exists: t.zonal.restoreButton.ReadOnly = False t.zonal.restoreButton.Enabled = True t.zonal.restoreButton.Caption = 'Restore previous helper settings' else: t.zonal.restoreButton.ReadOnly = False t.zonal.restoreButton.Enabled = False t.zonal.restoreButton.Caption = 'Previous helper settings file not found.' t.zonal.restoreButton.Hint = 'Min/Max keytimes will be restored from previous tool use. Previous settings are saved in ' + tempfile.gettempdir() + '\\motionwarp_save_file.txt' t.zonal.restoreButton.Style = FBButtonStyle.kFBPushButton t.zonal.restoreButton.Look = FBButtonLook.kFBLookNormal t.zonal.restoreButton.OnClick.Add(zonalRestoreButtonClicked) ########################################################################################## # Calculate Tool Window Size and Draw ########################################################################################## t.zonal.StartSizeX = ui_x_width + ui_x_offset + fcurve_button_width + 28 t.zonal.StartSizeY = ui_y_offset + 60 ShowTool(zonal) #################################################################### # Main #################################################################### def CreateTool(): global t ################################################################################### # v1.66 - zonal helper keys now default to clamp tangent keys # v1.67 - mover splines are blue to differentiate them from non-mover objects which are red ################################################################################### version = 1.67 t = FBCreateUniqueTool("MotionWarp v" + str(version)) PopulateTool(t) CreateTool() #################################################################### # Switch to next take at start frame #################################################################### from pyfbsdk import * #get current take and increment num_of_takes = len(FBSystem().Scene.Takes) for i in range( num_of_takes ): # go to take if FBSystem().CurrentTake.Name == FBSystem().Scene.Takes[i].Name: #increment take if you can if ( i < ( num_of_takes-1 )): FBSystem().CurrentTake = FBSystem().Scene.Takes[i + 1] break # set time to start start_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() FBPlayerControl().Goto(FBTime(0, 0, 0, start_frame)) FBSystem().Scene.Evaluate()‰PNG from pyfbsdk import * import subprocess path, file = os.path.split( FBApplication().FBXFileName ) subprocess.Popen( "explorer " + path ) ‰PNG #################################################################### # Prints out a relation constraint as text to be scripted #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import tempfile class RelationBoxType(): Operator = 1 Receiver = 2 Sender = 3 class RelationBoxOperatorGroup(): Boolean = 'Boolean' Converters = 'Converters' Number = 'Number' Other = 'Other' Rotation = 'Rotation' Shapes = 'Shapes' Sources = 'Sources' System = 'System' Time = 'Time' Vector = 'Vector' class RelationBoxOperators(): Boolean = {'AND', 'Flip Flop', 'Memory (B1 when REC)', 'Memory (last trigger)', 'NAND', 'NOR', 'NOT', 'OR', 'XNOR', 'XOR' } Converters = {'Deg To Rad', 'HSB to RGB', 'Number To RGBA', 'Number to Vector', 'Number to Vector2', 'Rad To Deg', 'RGB To HSB', 'RGB To RGBA', 'RGBA To Number', 'RGBA To RGB', 'Seconds to Time', 'Time to seconds', 'Time to Timecode', 'Timecode to Time', 'Vector to Number', 'Vector2 to Number'} Number = {'Absolute (|a|)', 'Add (a + b)', 'arccos(a)', 'arcsin(a)', 'arctan(a)', 'arctan2(b/a)', 'Cosine cos(a)', 'Damp', 'Damp (Clock based)', 'Distance Numbers', 'Divide (a/b)', 'exp(a)', 'Exponent (a^b)', 'IF Cond Then A Else B', 'Integer', 'Invert (1/a)', 'Is Between A and B', 'Is Different (a != b)', 'Is Greater (a > b)', 'Is Greater or Equal (a >= b)', 'Is Identical (a == b)', 'Is Less (a < b)', 'Is Less or Equal(a <= b)', 'ln(a)', 'log(a)', 'Max Pass-thru', 'Memory (a when REC)', 'Min Pass-thru', 'Modulo mod(a,b)', 'Multiply (a x b)', 'Precision Numbers', 'Pull Number', 'Scale And Offset (Number)', 'Sine sin(a)', 'sqrt(a)', 'Subtract (a - b)', 'Sum 10 numbers', 'Tangeant tan(a)', 'Triggered Delay (Number)', 'Triggered Delay with Memory(Number)'} Other = {'Bezier Curve', 'Damping (3D)', 'Damping (3D)(Clock based)', 'FCurve Number (%)', 'FCurve Number (Time)', 'Real Time Filter', 'Triggered Plus Minus Counter', 'Triggered Random'} Rotation = {'Add (R1 + R2)', 'Angle Difference (Points)', 'Angle Difference (Rotations)', 'Angular Accleration', 'Angular Speed', 'Damp(Rotation)', 'Global To Local', 'Interpolate', 'Local To Global', 'Rotation Scaling', 'Sensor Rotation Helper', 'Subtract (R1 - R2)', 'Three-Point Constraint'} Shapes = {'Select exclusive', 'Select exclusive 24', 'Shape calibration'} Sources = {'Counter with Play Pause', 'Counter with Start Stop', 'Half Circle Ramp', 'Isoceles Triangle Ramp', 'Pulse', 'Ramp', 'Random', 'Right Triangle Ramp', 'Sine Ramp', 'Square Ramp'} System = {'Current Time', 'Local Time', 'Play Mode', 'Reference Time', 'System Time', 'Transport Control' } Time = {'IF Cond Then T1 Else T2', 'Is Different (T1 != T2)', 'Is Greater (T1 > T2)', 'Is Greater or Equal(T1 >= T2)', 'Is Identical (T1 == T2)', 'Is Less (T1 < T2)', 'Is Less or Equal(T1 <= T2)'} Vector = {'Acceleration', 'Add (V1 + V2)', 'Angle', 'Center Of Mass', 'Damp Position', 'Derive', 'Determinent', 'Displacement', 'Distance', 'Dot Product (V1.V2)', 'Gravity', 'IF Cond Then A Else B', 'Is Different (V1 != V2)', 'Is Identical (V1 == V2)', 'Length', 'Memory (V1 when REC)', 'Middle Point', 'Normalize', 'Orbit Attraction', 'Precision Vectors', 'Pull Vector', 'Scale (a x V)', 'Scale And Offset (Vector)', 'Scale Damping', 'Speed', 'Subtract (V1 - V2)', 'Sum 10 vectors', 'Triggered Delay (Vector)', 'Triggered Delay with Memory(Vector)', 'Vector Product(V1 x V2)'} def FindAnimationNode( pParent, pName ): lResult = None for lNode in pParent.Nodes: if lNode.Name == pName: lResult = lNode break return lResult def PrintConstraint( lCon ): print "*************************************************" print "***These are the %d boxes in the %s Constraint***" % (len(lCon.Boxes), lCon.Name) print "*************************************************" for lBox in lCon.Boxes: lOut = 0 lIn = 0 lAnimationNodesOUT = lBox.AnimationNodeOutGet().Nodes for lAnimationNode in lAnimationNodesOUT: for i in range(lAnimationNode.GetDstCount()): if lAnimationNode.GetDst(0) != None: #Workaround for why it's printing in nodes that aren't connected if not lAnimationNode.GetDst(i).GetOwner().Name.startswith("Relation"): lOut = 1 #IN Animation Nodes lAnimationNodesIN = lBox.AnimationNodeInGet().Nodes for lAnimationNode in lAnimationNodesIN: for i in range(lAnimationNode.GetSrcCount()): if lAnimationNode.GetSrc(0) != None: lIn = 1 if lOut == 0 and lIn == 1: print "%s is a Receiver Box" % lBox.Name elif lOut == 1 and lIn == 0: print "%s is a Sender Box" % lBox.Name else: print "%s is a Function (operator) Box" % lBox.Name print print "**********************************************************************" print "***These are the connections between the boxes in the %s Constraint***" % lCon.Name print "**********************************************************************" for lBox in lCon.Boxes: #TMP if lBox.Name == 'Add (a + b)': ''' print "-------%s Box connected Animation Nodes-------" % lBox.Name #OUT Animation Nodes #looks like we have an issue with Out Animation Nodes, I think it prints in nodes too :( lAnimationNodesOUT = lBox.AnimationNodeOutGet().Nodes for lAnimationNode in lAnimationNodesOUT: for i in range(lAnimationNode.GetDstCount()): if lAnimationNode.GetDst(0) != None: #Workaround for why it's printing in nodes that aren't connected if not lAnimationNode.GetDst(i).GetOwner().Name.startswith("Relation"): print "OUT: %s (%s) > %s (%s) " % (lBox.Name, lAnimationNode.UserName, lAnimationNode.GetDst(i).GetOwner().Name, lAnimationNode.GetDst(i).UserName) ''' #IN Animation Nodes lAnimationNodesIN = lBox.AnimationNodeInGet().Nodes for i in range( len( lAnimationNodesIN ) ): try: # get plug info if exists data = lAnimationNodesIN[i].ReadData() print "data is " + str( data ) except: print "error: Can't read data from " + str( lAnimationNodesIN[i].UserName ) ''' try: # get plug info if exists owner_of_plug = lAnimationNodesIN[i].GetSrc(i).GetOwner() #print "owner is " + str( owner_of_plug ) #print "inplug exists > " + str( lAnimationNode.GetSrc(i).UserName ) except IndexError: print "inplug " + str( lAnimationNodesIN[i].UserName ) + " doesn't exist but value is " + str( lAnimationNodesIN[i].ReadData() ) print "box is " + str( lBox.Name ) ''' ''' for lAnimationNode in lAnimationNodesIN: # first print connected Src Nodes for i in range(lAnimationNode.GetSrcCount()): # print "i is " + str(i) if lAnimationNode.GetSrc(i) != None: print "IN: %s (%s) > %s (%s) " % (lBox.Name, lAnimationNode.UserName, lAnimationNode.GetSrc(0).GetOwner().Name, lAnimationNode.GetSrc(0).UserName) # finally print unconnected Src Node Values #print " len( lAnimationNodesIN )" + str( len( lAnimationNodesIN ) ) for i in range( len( lAnimationNodesIN ) ): try: # get plug info if exists inPlug = lAnimationNodesIN[i].GetSrc(i) print "inplug exists > " + str( lAnimationNode.GetSrc(i).UserName ) except IndexError: print "inplug doesn't exist but value is " + str( lAnimationNodesIN[i].ReadData() ) print lAnimationNodesIN[i].UserName try: # get plug info if exists inPlug = lAnimationNodesIN[i].GetSrc(i) except IndexError: # check for set value print lAnimationNodesIN[i].GetOwner() for prop in lAnimationNodesIN[i].PropertyList: print "prop is " + str( prop.Name ) value = FindAnimationNode( lAnimationNodesIN[i], lAnimationNodesIN[i].UserName ) print "value is " + str(value) ''' print def isAnimNodeTRSType( animationNode ): if ( animationNode.UserName == 'Rotation' or animationNode.UserName == 'Lcl Rotation' or animationNode.UserName == 'Translation' or animationNode.UserName == 'Lcl Translation' or animationNode.UserName == 'Scaling' or animationNode.UserName == 'Lcl Scaling'): return True else: return False def GetRelationType( box, constraint ): relationType = None lOut = 0 lIn = 0 lAnimationNodesOUT = box.AnimationNodeOutGet().Nodes model = None for lAnimationNode in lAnimationNodesOUT: for i in range(lAnimationNode.GetDstCount()): if lAnimationNode.GetDst(0) != None: # ignore bogus/empty connections to the solver if not lAnimationNode.GetDst(i).GetOwner().Name == constraint.Name: lOut = 1 # if a model type, then save model reference if type( lAnimationNode.GetOwner() ).__name__ == 'FBModelPlaceHolder': box.ModelObject = lAnimationNode.GetOwner().Model box.GlobalTransforms = lAnimationNode.GetOwner().UseGlobalTransforms break #IN Animation Nodes lAnimationNodesIN = box.AnimationNodeInGet().Nodes for lAnimationNode in lAnimationNodesIN: for i in range(lAnimationNode.GetSrcCount()): if lAnimationNode.GetSrc(0) != None: # has this got T/R/S connections and its already been flagged as destination? # then ignore if not ( lOut == 1 and isAnimNodeTRSType( lAnimationNode ) ): lIn = 1 # if a model type, then save model reference if type( lAnimationNode.GetOwner() ).__name__ == 'FBModelPlaceHolder': box.ModelObject = lAnimationNode.GetOwner().Model box.GlobalTransforms = lAnimationNode.GetOwner().UseGlobalTransforms break if lOut == 0 and lIn == 1: box.BoxType = RelationBoxType.Receiver return box.BoxType elif lOut == 1 and lIn == 0: box.BoxType = RelationBoxType.Sender return box.BoxType else: box.BoxType = RelationBoxType.Operator return box.BoxType def GetBoxNameStripped( box ): return box.Name.rstrip('1234567890 ') def GetOperatorGroup( box ): # remove any numbers and whitespace due to user duplication box_stripped_name = GetBoxNameStripped( box ) # find box_operator_group = None if box_stripped_name in RelationBoxOperators.Boolean: box_operator_group = RelationBoxOperatorGroup.Boolean elif box_stripped_name in RelationBoxOperators.Converters: box_operator_group = RelationBoxOperatorGroup.Converters elif box_stripped_name in RelationBoxOperators.Number: box_operator_group = RelationBoxOperatorGroup.Number elif box_stripped_name in RelationBoxOperators.Other: box_operator_group = RelationBoxOperatorGroup.Other elif box_stripped_name in RelationBoxOperators.Rotation: box_operator_group = RelationBoxOperatorGroup.Rotation elif box_stripped_name in RelationBoxOperators.Shapes: box_operator_group = RelationBoxOperatorGroup.Shapes elif box_stripped_name in RelationBoxOperators.Sources: box_operator_group = RelationBoxOperatorGroup.Sources elif box_stripped_name in RelationBoxOperators.System: box_operator_group = RelationBoxOperatorGroup.System elif box_stripped_name in RelationBoxOperators.Time: box_operator_group = RelationBoxOperatorGroup.Time elif box_stripped_name in RelationBoxOperators.Vector: box_operator_group = RelationBoxOperatorGroup.Vector if not box_operator_group: print "error: Operator type NOT found! for " + box_stripped_name return box_operator_group def CreateRelationConstraint(): lMgr = FBConstraintManager() # get index to parent/child constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Relation': lIndex = i break # create constraint relationConstraint = lMgr.TypeCreateConstraint(lIndex) relationConstraint.Name = 'RigFacingSolver' return relationConstraint def GetCharacterObjectFromBoxName( name ): input_name = name split_name = input_name.rsplit('_', 1 ) object = FBFindModelByLabelName(split_name[0] + ':' + split_name[1]) if object == None: print "error in GetCharacterObjectFromBox: cannot find object " + str (split_name[0] + ':' + split_name[1]) else: return object def GetBoxIndexInConstraint( box, constraint ): for i in range( len( constraint.Boxes )): if box == constraint.Boxes[i]: return i print "error in GetBoxIndexInConstraint: match not found" def ConnectPlugs( old_box, source_index, source_constraint, dest_constraint, output ): lAnimationNodesOut = old_box.AnimationNodeOutGet().Nodes lAnimationNodesIn = old_box.AnimationNodeInGet().Nodes for i in range ( len( lAnimationNodesOut ) ): for j in range( lAnimationNodesOut[i].GetDstCount() ): if lAnimationNodesOut[i].GetDst(j) != None and lAnimationNodesOut[i].GetDst(j).GetOwner().Name != source_constraint.Name: ''' print "source_index is " + str( source_index ) print "Num of destination nodes is " + str(lAnimationNodesOut[i].GetDstCount()) print "Source Object is " + str( lAnimationNodesOut[i].GetOwner().Name ) print "Source Plug is " + str( lAnimationNodesOut[i].UserName ) print "Destination Object is " + str(lAnimationNodesOut[i].GetDst(j).GetOwner().Name ) print "Destination Plug is " + str(lAnimationNodesOut[i].GetDst(j).UserName ) ''' dest_index = GetBoxIndexInConstraint( lAnimationNodesOut[i].GetDst(j).GetOwner(), source_constraint ) new_box_OUT_anim_node = FindAnimationNode( dest_constraint.Boxes[ source_index ].AnimationNodeOutGet(), lAnimationNodesOut[i].UserName ) new_box_IN_anim_node = FindAnimationNode( dest_constraint.Boxes[ dest_index ].AnimationNodeInGet(), lAnimationNodesOut[i].GetDst(j).UserName ) if new_box_OUT_anim_node and new_box_IN_anim_node: FBConnect( new_box_OUT_anim_node, new_box_IN_anim_node ) else: print "error in ConnectDestinationPlugs(OUT): Can't find nodes" # >>>>>>>>>>>>> output output += "new_box_OUT_anim_node = FindAnimationNode( new_constraint.Boxes[ " + str( source_index ) + " ].AnimationNodeOutGet(), '" + str( lAnimationNodesOut[i].UserName) + "' )" output += "\n" output += "new_box_IN_anim_node = FindAnimationNode( new_constraint.Boxes[ " + str( dest_index ) + " ].AnimationNodeInGet(), '" + str( lAnimationNodesOut[i].GetDst(j).UserName ) + "' )" output += "\n" output += "FBConnect( new_box_OUT_anim_node, new_box_IN_anim_node )" output += "\n" output += "\n" for i in range ( len( lAnimationNodesIn ) ): for j in range( lAnimationNodesIn[i].GetSrcCount() ): if lAnimationNodesIn[i].GetSrc(j) != None and lAnimationNodesIn[i].GetSrc(j).GetOwner().Name != source_constraint.Name: ''' print "source_index is " + str( source_index ) print "Num of Source nodes is " + str(lAnimationNodesIn[i].GetSrcCount()) print "Destination Object is " + str( lAnimationNodesIn[i].GetOwner().Name ) print "Destination Plug is " + str( lAnimationNodesIn[i].UserName ) print "Source Object is " + str(lAnimationNodesIn[i].GetSrc(j).GetOwner().Name ) print "Source Plug is " + str(lAnimationNodesIn[i].GetSrc(j).UserName ) ''' dest_index = GetBoxIndexInConstraint( lAnimationNodesIn[i].GetSrc(j).GetOwner(), source_constraint ) new_box_IN_anim_node = FindAnimationNode( dest_constraint.Boxes[ source_index ].AnimationNodeInGet(), lAnimationNodesIn[i].UserName ) new_box_OUT_anim_node = FindAnimationNode( dest_constraint.Boxes[ dest_index ].AnimationNodeOutGet(), lAnimationNodesIn[i].GetSrc(j).UserName ) if new_box_OUT_anim_node and new_box_IN_anim_node: FBConnect( new_box_OUT_anim_node, new_box_IN_anim_node ) else: print "error in ConnectDestinationPlugs (IN): Can't find nodes" # >>>>>>>>>>>>> output output += "new_box_IN_anim_node = FindAnimationNode( dest_constraint.Boxes[ " + str( source_index ) + " ].AnimationNodeInGet(), '" + str( lAnimationNodesIn[i].UserName) + "' )" output += "\n" output += "new_box_OUT_anim_node = FindAnimationNode( dest_constraint.Boxes[ " + str( dest_index) + " ].AnimationNodeOutGet(), '" + str( lAnimationNodesIn[i].GetSrc(j).UserName ) + "' )" output += "\n" output += "FBConnect( new_box_OUT_anim_node, new_box_IN_anim_node )" output += "\n" output += "\n" return output def SetValues( old_box, source_index, source_constraint, dest_constraint, output ): lAnimationNodesIN = old_box.AnimationNodeInGet().Nodes for i in range( len( lAnimationNodesIN ) ): try: inPlug = lAnimationNodesIN[i].GetSrc(i) # get plug info if exists except: data = lAnimationNodesIN[i].ReadData() ######################################################################## # Set Value ( only on non Models for now ) ######################################################################## if type( lAnimationNodesIN[i].GetOwner() ).__name__ != 'FBModelPlaceHolder': dest_animation_node = FindAnimationNode( dest_constraint.Boxes[ source_index ].AnimationNodeInGet(), lAnimationNodesIN[i].UserName ) if dest_animation_node: dest_animation_node.WriteData( data ) output += "dest_animation_node = FindAnimationNode( dest_constraint.Boxes[ " + str( source_index ) + " ].AnimationNodeInGet(), '" + str( lAnimationNodesIN[i].UserName ) + "' )" output += "\n" output += "dest_animation_node.WriteData( " + str( data ) + " )" output += "\n" return output def CopyFCurves( old_box, source_index, source_constraint, dest_constraint, output ): # only copy fcurves for constraint boxes - not animated models if type( old_box ).__name__ != 'FBModelPlaceHolder': lAnimationNodesOUT = old_box.AnimationNodeOutGet().Nodes for i in range( len( lAnimationNodesOUT ) ): try: fcurve = lAnimationNodesOUT[i].FCurve # copy fcurve if exists dest_animation_node = FindAnimationNode( dest_constraint.Boxes[ source_index ].AnimationNodeOutGet(), lAnimationNodesOUT[i].UserName ) if dest_animation_node: dest_animation_node.FCurve.KeyReplaceBy( fcurve ) output += "dest_animation_node = FindAnimationNode( dest_constraint.Boxes[ " + str( source_index ) + " ].AnimationNodeOutGet(), '" + str( lAnimationNodesOUT[i].UserName ) + "' )" output += "\n" for key in dest_animation_node.FCurve.Keys: output += "dest_animation_node.FCurve.KeyAdd( " + str( key.Time) + ", " + str( key.Value ) + ", " + str( key.Interpolation ) + ", " + str( key.TangentMode ) + " )" output += "\n" except: pass return output def PrintOutToTextFile( output ): #################################################################### # Open text file with final result #################################################################### output_file_path = tempfile.gettempdir() + '\\mj_OutputRelationConstraint_output.txt' with open( output_file_path , 'w') as f: f.write( output ) os.startfile( output_file_path ) def OutputRelationConstraint(): constraints = FBSystem().Scene.Constraints character = FBApplication().CurrentCharacter selected_constraint = None box_relation_type = [] for constraint in constraints: if constraint.Selected: if type(constraint).__name__ == 'FBConstraintRelation': selected_constraint = constraint break if selected_constraint: new_constraint = CreateRelationConstraint() #PrintConstraint( selected_constraint ) # output string to write python commands to output = '' ################################################################################## # Create Boxes ################################################################################## for box in selected_constraint.Boxes: # get receiver, sender or operator type box_relation_type.append( GetRelationType( box, selected_constraint ) ) box_position = selected_constraint.GetBoxPosition( box ) # >>>>>>>>>>>>> output output += "\n" output += "#######################################################" output += "\n" output += "# Adding Box > " + str( box.Name ) output += "\n" output += "#######################################################" output += "\n" output += "\n" if box_relation_type[-1] == RelationBoxType.Operator: # get operator group and operator ( assumes name is unmodified from creation ) box_operator_group = GetOperatorGroup( box ) box_operator_name = GetBoxNameStripped( box ) new_box = new_constraint.CreateFunctionBox( box_operator_group, box_operator_name ) # >>>>>>>>>>>>> output output += "new_box = new_constraint.CreateFunctionBox( '" + str( box_operator_group ) + "', '" + str( box_operator_name ) + "' )" output += "\n" # set position new_constraint.SetBoxPosition( new_box, box_position[1], box_position[2] ) # >>>>>>>>>>>>> output output += "new_constraint.SetBoxPosition( new_box, " + str( box_position[1]) + ", " + str( box_position[2]) + " )" output += "\n" if box_relation_type[-1] == RelationBoxType.Receiver: new_box = new_constraint.ConstrainObject( box.ModelObject ) # >>>>>>>>>>>>> output output += "new_box = new_constraint.ConstrainObject( < insert pointer to object" + str( box.ModelObject.Name ) + " > )" output += "\n" new_box.UseGlobalTransforms = box.GlobalTransforms # >>>>>>>>>>>>> output output += "new_box.UseGlobalTransforms = " + str( box.GlobalTransforms ) output += "\n" # set position new_constraint.SetBoxPosition( new_box, box_position[1], box_position[2] ) # >>>>>>>>>>>>> output output += "new_constraint.SetBoxPosition( new_box , " + str( box_position[1]) + ", " + str( box_position[2]) + " )" output += "\n" if box_relation_type[-1] == RelationBoxType.Sender: new_box = new_constraint.SetAsSource( box.ModelObject ) # >>>>>>>>>>>>> output output += "new_box = new_constraint.SetAsSource( < insert pointer to object" + str( box.ModelObject.Name) + " > )" output += "\n" new_box.UseGlobalTransforms = box.GlobalTransforms # >>>>>>>>>>>>> output output += "new_box.UseGlobalTransforms = " + str( box.GlobalTransforms ) output += "\n" # set position new_constraint.SetBoxPosition( new_box, box_position[1], box_position[2] ) # >>>>>>>>>>>>> output output += "new_constraint.SetBoxPosition( new_box , " + str( box_position[1]) + ", " + str( box_position[2]) + " )" output += "\n" ################################################################################## # Create Connections ################################################################################## # >>>>>>>>>>>>> output output += "\n" output += "#######################################################" output += "\n" output += "# Creating Connections" output += "\n" output += "#######################################################" output += "\n" output += "\n" for i in range( len(selected_constraint.Boxes) ): output = ConnectPlugs( selected_constraint.Boxes[i], i, selected_constraint, new_constraint, output ) ################################################################################## # Create Fixed Input Values/FCurves ################################################################################## # >>>>>>>>>>>>> output output += "\n" output += "#######################################################" output += "\n" output += "# Create Fixed Input Values/FCurves" output += "\n" output += "#######################################################" output += "\n" output += "\n" for i in range( len(selected_constraint.Boxes) ): output = SetValues( selected_constraint.Boxes[i], i, selected_constraint, new_constraint, output ) output = CopyFCurves( selected_constraint.Boxes[i], i, selected_constraint, new_constraint, output ) ################################################################################## # Output text file ################################################################################## PrintOutToTextFile( output ) else: FBMessageBox( "Message", "Please select a single relation constraint to output.", "OK", None, None ) OutputRelationConstraint()#################################################################### # Allows user to paste a pose on character extensions #################################################################### from pyfbsdk import * from pyfbsdk_additions import * # Global UI components #take control ui comps take_radio_group = FBButtonGroup() take_yes_button = FBButton() take_no_button = FBButton() take_radio_group.Add(take_yes_button) take_radio_group.Add(take_no_button) status = FBLabel() button = [] selected_character = "" UI_char_extension_objs = [] char_extension_objs = [] char_rollbone_objs = [] char_3lateral_objs = [] def clearSelection(): ''' Clear selection function ''' # Get selected models modelList = FBModelList () FBGetSelectedModels (modelList, None, True) # Deselect models for model in modelList: model.Selected = False def ClearAnim( pNode ): # The FCurve property will not be null on a terminal node. # i.e. the 'Lcl Translation' node will not have any animation on it # directly... only the sub-nodes 'X', 'Y' or 'Z' may have animation. if pNode.FCurve: # Ah! there is a FCurve! Let's remove all the keys. pNode.FCurve.EditClear() else: # Then we are dealing with a parent node. Let's look at it # children nodes. for lNode in pNode.Nodes: # Recursively call ourselves to deal with sub-nodes. ClearAnim( lNode ) # Cleanup del( lNode ) def getAllSceneCharacterExtensions(): exts = FBSystem().Scene.CharacterExtensions for e in exts: components = e.Components for c in components: existing = False for ext in char_extension_objs: if c.Name == ext.Name: existing = True if not existing: char_extension_objs.append( c ) def getSpecificCharacterExtension( obj ): global selected_character # get namespace of selected character namespace = ( selected_character ).split(":")[0] if namespace != "": namespace += ":" exts = FBSystem().Scene.CharacterExtensions for e in exts: components = e.Components for c in components: #print "combined namespace and obj.Name is " + namespace + obj.Name #print "c.LongName is " + c.LongName if namespace + obj.Name == c.LongName: #print "found match! with component " + c.LongName return c def getUniqueCharacterExtensions(): exts = FBSystem().Scene.CharacterExtensions for e in exts: components = e.Components for c in components: existing = False for ext in UI_char_extension_objs: if c.Name == ext.Name: existing = True if not existing: UI_char_extension_objs.append( c ) def getCharacters(): return FBSystem().Scene.Characters def setupPropertyList(tool): tool.list.Items.removeAll() tool.prop_list = [] character_list = getCharacters() if len(character_list): for char in character_list: tool.list.Items.append(char.LongName) else: tool.list.Items.append('No characters found!') selected_character = character_list[0] #################################################################### # UI CallBacks #################################################################### def ListCallback(control, event): global selected_character selected_character = control.Items[control.ItemIndex] def OnDragAndDropCallback(control, event): if event.State == FBDragAndDropState.kFBDragAndDropDrag: event.Accept() if event.State == FBDragAndDropState.kFBDragAndDropDrop: for e in event.Components: print type(e) # only allow poses to be dropped if type(e) is pyfbsdk.FBPose or type(e) is pyfbsdk.FBCharacterPose: event.Accept() control.Items.removeAll() control.Items.append(e.Name) t.pasteButton.Enabled = True def pasteButtonCallBack(control, event): system = FBSystem() currentTake = system.CurrentTake ############################################################# # get char reference ############################################################# chars = system.Scene.Characters characterIndex = 0 for char in chars: if char.LongName == t.list.Items[t.list.ItemIndex]: break else: characterIndex += 1 selectedCharacter = chars[ characterIndex ] ############################################################# # get pose reference ############################################################# poses = FBSystem().Scene.CharacterPoses characterPoseIndex = 0 for p in poses: if p.Name == t.container.Items[0]: break else: characterPoseIndex += 1 targetPose = poses[ characterPoseIndex ] poseOptions = FBCharacterPoseOptions() poseOptions.mCharacterPoseKeyingMode = FBCharacterPoseKeyingMode.kFBCharacterPoseKeyingModeFullBody player = FBPlayerControl() ############################################################# # set key controls # first store current setting ############################################################# currentKeyMode = selectedCharacter.KeyingMode selectedCharacter.KeyingMode = FBCharacterKeyingMode.kFBCharacterKeyingSelection ####################################################################### # ALL TAKES ####################################################################### if take_yes_button.State == 1: for take in FBSystem().Scene.Takes: system.CurrentTake = take FBSystem().Scene.Evaluate() index = 0 #deselect all clearSelection() # paste pose (Fullbody) targetPose.PastePose( selectedCharacter, poseOptions ) FBSystem().Scene.Evaluate() for obj in char_extension_objs: if ( button[index].State == True ): obj.Selected = True FBSystem().Scene.Evaluate() index += 1 # key player.Key() FBSystem().Scene.Evaluate() # restore take system.CurrentTake = currentTake ####################################################################### # SINGLE TAKE ####################################################################### else: print "Pasting just this take" index = 0 #deselect all clearSelection() # paste pose (Fullbody) targetPose.PastePose( selectedCharacter, poseOptions ) FBSystem().Scene.Evaluate() for obj in char_extension_objs: if ( button[index].State == True ): obj.Selected = True index += 1 # key player.Key() # restore key controls selectedCharacter.KeyingMode = currentKeyMode print "Selected extensions pasted!" status.Caption = "Extensions keyed with pose!" FBSystem().Scene.Evaluate() #################################################################### # UI #################################################################### def PopulateTool(t): # ui variables for easier modification ui_border_x = 10 ui_border_y = 10 ui_border_width = 140 ui_border_height = 100 ui_column_a = 20 ui_column_b = 60 ui_column_c = 100 ui_row_offset = 10 ui_button_width = 120 ui_button_height = 15 #Mover Selection---------------------------------------------- #Drop Down x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(25,FBAttachType.kFBAttachNone,"") t.AddRegion("dropdown","dropdown", x, y, w, h) t.list = FBList() t.SetControl("dropdown", t.list) setupPropertyList(t) #Extensions-------------------------------------------------- #get all scene character extension objects getAllSceneCharacterExtensions() ui_row_offset += 40 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( ( 20 * len(char_extension_objs) ) + 15,FBAttachType.kFBAttachNone,"") t.AddRegion("trans_border","Character Extensions", x, y, w, h) t.SetBorder("trans_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons # create a button for each extension obj for i in range( 0, len(char_extension_objs) ): region = "extButton" + str(i) x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop, region ) y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height,FBAttachType.kFBAttachNone,"") t.AddRegion(region,region, x, y, w, h) button.append( FBButton() ) t.SetControl(region, button[i] ) button[i].Visible = True button[i].ReadOnly = False button[i].Enabled = True button[i].Hint = "" button[i].Caption = char_extension_objs[i].Name # set certain options as off for ease of use if ( "mover" in char_extension_objs[i].Name ) or \ ( "PH_R_Hand" in char_extension_objs[i].Name ) or \ ( "PH_L_Hand" in char_extension_objs[i].Name ): button[i].State = 0 else: button[i].State = 1 button[i].Style = FBButtonStyle.kFBCheckbox button[i].Justify = FBTextJustify.kFBTextJustifyLeft button[i].Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 #Takes-------------------------------------------------- #clear all takes? ui_row_offset += 20 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam( 60 ,FBAttachType.kFBAttachNone,"") t.AddRegion("takes","Paste On All Takes?", x, y, w, h) t.SetBorder("takes",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"take_yes_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("take_yes_button","take_yes_button", x, y, w, h) t.SetControl("take_yes_button", take_yes_button ) take_yes_button.Visible = True take_yes_button.ReadOnly = False take_yes_button.Enabled = True take_yes_button.Hint = "" take_yes_button.Caption = "Yes" take_yes_button.State = 1 take_yes_button.Style = FBButtonStyle.kFBRadioButton take_yes_button.Justify = FBTextJustify.kFBTextJustifyLeft take_yes_button.Look = FBButtonLook.kFBLookNormal ui_row_offset += 20 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"take_no_button") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("take_no_button","take_no_button", x, y, w, h) t.SetControl("take_no_button", take_no_button ) take_no_button.Visible = True take_no_button.ReadOnly = False take_no_button.Enabled = True take_no_button.Hint = "" take_no_button.Caption = "No" take_no_button.State = 0 take_no_button.Style = FBButtonStyle.kFBRadioButton take_no_button.Justify = FBTextJustify.kFBTextJustifyLeft take_no_button.Look = FBButtonLook.kFBLookNormal #Pose Container-------------------------------------------------- ui_row_offset = ui_row_offset + 40 t.container = FBVisualContainer() x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"container_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("container_region","container_region", x, y, w, h) t.SetControl("container_region", t.container) t.container.Items.append("Drag Pose Here") t.container.ItemHeight = 30 t.container.ItemWidth = ui_border_width - 2 t.container.OnDragAndDrop.Add(OnDragAndDropCallback) #Paste Button-------------------------------------------------- ui_row_offset = ui_row_offset + 40 t.pasteButton = FBButton() x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"paste_button_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("paste_button_region","paste_button_region", x, y, w, h) t.SetControl("paste_button_region", t.pasteButton) t.pasteButton.Visible = True t.pasteButton.ReadOnly = False t.pasteButton.Enabled = False t.pasteButton.Hint = "" t.pasteButton.Caption = "Paste Pose on Selected" t.pasteButton.State = 0 t.pasteButton.Style = FBButtonStyle.kFBPushButton t.pasteButton.Justify = FBTextJustify.kFBTextJustifyCenter t.pasteButton.Look = FBButtonLook.kFBLookNormal t.pasteButton.OnClick.Add(pasteButtonCallBack) #Status -------------------------------------------------- ui_row_offset = ui_row_offset + 35 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"status_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("status_region","status_region", x, y, w, h) t.SetControl("status_region", status) status.Visible = True status.ReadOnly = False status.Enabled = True status.Caption = ">" # set tool ui size t.StartSizeX = 175 t.StartSizeY = ui_row_offset + 60 #################################################################### # Main #################################################################### def CreateTool(): print "DEBUG" global t t = FBCreateUniqueTool("Paste Pose On Extensions") PopulateTool(t) ShowTool(t) CreateTool() ‰PNG from pyfbsdk import * from pyfbsdk_additions import * def clearSelection(): ''' Clear selection function ''' # Get selected models modelList = FBModelList () FBGetSelectedModels (modelList, None, True) # Deselect models for model in modelList: model.Selected = False #################################################################### # UI callbacks #################################################################### def goButtonCallBack(control, event): if t.pinned == False: # get time span current_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() current_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() t.null = [] # lists for storing object pos and rotations trans_array = [] rot_array = [] ######################################## # Pin object ######################################## for i in range( 0, t.numOfObjects ): # create null t.null.append( FBModelNull('constraint_null_' + t.selected_obj_name[i]) ) t.null[i].Parent = FBModelNull('constraint_null_Parent') #---------------------------------------------------------------------------------- # loop for every frame - get pos/rot of selected object #---------------------------------------------------------------------------------- for frame in range(current_start, current_end + 1): # update slider FBPlayerControl().Goto(FBTime(0,0,0,frame)) FBSystem().Scene.Evaluate() # get selected translation and rotation for object obj_trans = FBVector3d() obj_rot = FBVector3d() for i in range( 0, t.numOfObjects ): t.selected_obj[i].GetVector (obj_trans, FBModelTransformationType.kModelTranslation, True) t.selected_obj[i].GetVector (obj_rot, FBModelTransformationType.kModelRotation, True) FBSystem().Scene.Evaluate() # store translation/rotation trans_array.append(obj_trans) rot_array.append(obj_rot) # only select nulls clearSelection() for i in range( 0, t.numOfObjects ): t.null[i].Selected = True #---------------------------------------------------------------------------------- # loop for every frame - set pos/rot on null #---------------------------------------------------------------------------------- count = 0 for frame in range(current_start, current_end + 1): # update slider FBPlayerControl().Goto(FBTime(0,0,0,frame)) FBSystem().Scene.Evaluate() for i in range( 0, t.numOfObjects ): # if we're on the first frame set up the parent of the null to sit in the starting position # this parent won't be keyed. if count == 0: t.null[i].Parent.SetVector(trans_array[ i + count * t.numOfObjects ],FBModelTransformationType.kModelTranslation, True) t.null[i].Parent.SetVector(rot_array[i + count * t.numOfObjects], FBModelTransformationType.kModelRotation, True) FBSystem().Scene.Evaluate() # set selected translation and rotation for object t.null[i].SetVector (trans_array[i + count * t.numOfObjects], FBModelTransformationType.kModelTranslation, True) t.null[i].SetVector (rot_array[i + count * t.numOfObjects], FBModelTransformationType.kModelRotation, True) FBSystem().Scene.Evaluate() # key null FBPlayerControl().Key() FBSystem().Scene.Evaluate() count += 1 #---------------------------------------------------------------------------------- # create constraints #---------------------------------------------------------------------------------- # constrain selected object to null lMgr = FBConstraintManager() # get index to parent/child constraint lIndex = None t.constraint = [] for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Parent/Child': lIndex = i break for j in range( 0, t.numOfObjects ): # create constraint lPosConst = lMgr.TypeCreateConstraint(lIndex) # add ref objects for i in range(0, lPosConst.ReferenceGroupGetCount()): if lPosConst.ReferenceGroupGetName(i) == 'Source (Parent)': lPosConst.ReferenceAdd(i, t.null[j]) elif lPosConst.ReferenceGroupGetName(i) == 'Constrained object (Child)': lPosConst.ReferenceAdd(i, t.selected_obj[j]) # snap/activate constraint lPosConst.Snap() # store ref to constraint objects t.constraint.append( lPosConst ) # update UI if t.numOfObjects == 1: t.pin_button.Caption = "Plot & Unpin '" + t.selected_obj_name[0] + "'" else: t.pin_button.Caption = "Plot & Unpin " + str(t.numOfObjects) + " objects" t.pinned = True else: ######################################## # UnPin object ######################################## # plot selected object clearSelection() for i in range( 0, t.numOfObjects ): t.selected_obj[i].Selected = True FBSystem().CurrentTake.PlotTakeOnSelected( plotOptions() ) FBSystem().Scene.Evaluate() # delete constraint for i in range( 0, t.numOfObjects ): t.constraint[i].FBDelete() # delete Parent Null t.null[ i ].Parent.FBDelete() # delete null t.null[i].FBDelete() print 'contraint(s) deleted' # update UI if t.numOfObjects == 1: t.pin_button.Caption = "Plot & Pin '" + t.selected_obj_name[0] + "'" else: t.pin_button.Caption = "Plot & Pin " + str(t.numOfObjects) + " objects" t.pinned = False def plotOptions(): plot_options = FBPlotOptions() plot_options.PlotAllTakes = False plot_options.PlotOnFrame = True plot_options.PlotPeriod = FBTime(0, 0, 0, 1) plot_options.RotationFilterToApply = FBRotationFilter.kFBRotationFilterUnroll plot_options.UseConstantKeyReducer = False plot_options.ConstantKeyReducerKeepOneKey = True plot_options.PlotTranslationOnRootOnly = False return plot_options #################################################################### # UI definition #################################################################### def PopulateTool(t): t.selected_obj = [] t.selected_obj_name = [] # get selected object models = FBModelList() FBGetSelectedModels( models ) if len(models) == 0: FBMessageBox( "Message", "Nothing selected", "OK", None, None ) else: for obj in models: t.selected_obj.append( obj ) t.selected_obj_name.append( obj.Name ) t.numOfObjects = len( models ) #Pin button x = FBAddRegionParam(5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(5,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(240,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("go_button_region","go_button_region", x, y, w, h) t.pin_button = FBButton() t.SetControl("go_button_region", t.pin_button) t.pin_button.Visible = True t.pin_button.ReadOnly = False t.pin_button.Enabled = True t.pin_button.Hint = "" if t.numOfObjects == 1: t.pin_button.Caption = "Plot & Pin '" + t.selected_obj_name[0] + "'" else: t.pin_button.Caption = "Plot & Pin " + str(t.numOfObjects) + " objects" t.pin_button.State = 0 t.pin_button.Style = FBButtonStyle.kFBPushButton t.pin_button.Justify = FBTextJustify.kFBTextJustifyCenter t.pin_button.Look = FBButtonLook.kFBLookNormal t.pin_button.OnClick.Add(goButtonCallBack) # set pin state t.pinned = False ShowTool(t) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Pin Selected") t.StartSizeX = 265 t.StartSizeY = 75 PopulateTool(t) CreateTool() ‰PNG #################################################################### # Plot from a selection of Takes #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import RS.Globals # Global UI components MAX_LIST_BOX_ENTRIES = 40 #################################################################### # UI CallBacks #################################################################### def AllTakesCallback(control, event): for item in range( len(t.list.Items) ): t.list.Selected( item, True ) def NoTakesCallback(control, event): for item in range( len(t.list.Items) ): t.list.Selected( item, False ) def plotOptions(): plot_options = FBPlotOptions() plot_options.PlotAllTakes = False plot_options.PlotOnFrame = True plot_options.PlotPeriod = FBTime(0, 0, 0, 1) plot_options.RotationFilterToApply = FBRotationFilter.kFBRotationFilterUnroll plot_options.UseConstantKeyReducer = False plot_options.ConstantKeyReducerKeepOneKey = True plot_options.PlotTranslationOnRootOnly = False return plot_options def PlotButtonCallBack(control, event): if t.skeletonModeButton.State: print "Plotting Selected Takes to Skeleton" else: print "Plotting Selected Takes to Rig" character_list = [] if t.currentCharacterButton.State == True: character_list.append( FBApplication().CurrentCharacter ) else: for char in RS.Globals.Characters: character_list.append( char ) for character in character_list: # only plot on selected takes for item in range( len(t.list.Items) ): if( t.list.IsSelected( item ) ): # go to take FBSystem().CurrentTake = FBSystem().Scene.Takes[ item ] # plot to skeleton if t.skeletonModeButton.State: # make sure rig is active character.Active = True # plot character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton, plotOptions() ) # plot to rig else: # if rig is active already then do a plot to skeleton first if character.Active == True: # plot to skeleton first character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton, plotOptions() ) # make sure rig is deactivated character.Active = False # plot character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnControlRig, plotOptions() ) print "Plotting Complete" #################################################################### # UI #################################################################### def PopulateTool(t): # ui variables for easier modification ui_border_x = 10 ui_border_y = 10 ui_border_width = 250 ui_border_height = 40 ui_column_a = 20 ui_column_b = ui_border_width / 2 ui_row_offset = 10 ui_button_width = 40 ######################################################################## # All Takes Button x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 6 ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("all_takes_button","all_takes_button", x, y, w, h) t.alltakesbutton = FBButton() t.alltakesbutton.Caption = "All" t.SetControl("all_takes_button", t.alltakesbutton) #callback t.alltakesbutton.OnClick.Add( AllTakesCallback ) ######################################################################## # No Takes Button x = FBAddRegionParam(ui_border_width + 10 - ui_border_width / 4 ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 4 ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("no_takes_button","no_takes_button", x, y, w, h) t.nonetakesbutton = FBButton() t.nonetakesbutton.Caption = "None" t.SetControl("no_takes_button", t.nonetakesbutton) #callback t.nonetakesbutton.OnClick.Add( NoTakesCallback ) ######################################################################## ui_row_offset += 25 #Take List t.list = FBList() t.list.Style = FBListStyle.kFBVerticalList t.list.MultiSelect = True t.list.ExtendedSelect = True x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") # get take names and calculate size required for UI list at same time for iTake in FBSystem().Scene.Takes: t.list.Items.append(iTake.Name) # select UI if user had take selected if ( iTake.Selected == True ): t.list.Selected( len( t.list.Items) - 1, True ) #limit UI size if( len(t.list.Items) < MAX_LIST_BOX_ENTRIES ): ui_row_offset += 15 w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") t.AddRegion("takes","takes", x, y, w, h) t.SetControl("takes", t.list) ui_row_offset = ui_row_offset + 10 ######################################################################## ui_row_offset += 40 t.modeRadios = FBButtonGroup() t.skeletonModeButton = FBButton() t.rigModeButton = FBButton() t.modeRadios.Add( t.skeletonModeButton ) t.modeRadios.Add( t.rigModeButton ) #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height,FBAttachType.kFBAttachNone,"") t.AddRegion("mode_border","Plot Mode", x, y, w, h) t.SetBorder("mode_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"skeleton_mode_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height / 2,FBAttachType.kFBAttachNone,"") t.AddRegion("skeleton_mode_region","skeleton_mode_region", x, y, w, h) t.SetControl("skeleton_mode_region", t.skeletonModeButton) t.skeletonModeButton.Visible = True t.skeletonModeButton.ReadOnly = False t.skeletonModeButton.Enabled = True t.skeletonModeButton.Hint = "" t.skeletonModeButton.Caption = "To Skeleton" t.skeletonModeButton.State = 1 t.skeletonModeButton.Style = FBButtonStyle.kFBRadioButton t.skeletonModeButton.Justify = FBTextJustify.kFBTextJustifyLeft t.skeletonModeButton.Look = FBButtonLook.kFBLookNormal x = FBAddRegionParam(ui_column_b,FBAttachType.kFBAttachTop,"rig_mode_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height / 2,FBAttachType.kFBAttachNone,"") t.AddRegion("rig_mode_region","rig_mode_region", x, y, w, h) t.SetControl("rig_mode_region", t.rigModeButton ) t.rigModeButton.Visible = True t.rigModeButton.ReadOnly = False t.rigModeButton.Enabled = True t.rigModeButton.Hint = "" t.rigModeButton.Caption = "To Rig" t.rigModeButton.State = 0 t.rigModeButton.Style = FBButtonStyle.kFBRadioButton t.rigModeButton.Justify = FBTextJustify.kFBTextJustifyLeft t.rigModeButton.Look = FBButtonLook.kFBLookNormal ######################################################################## # Current Character or All? ######################################################################## ui_row_offset += 45 t.modeRadios = FBButtonGroup() t.currentCharacterButton = FBButton() t.allCharacterButton = FBButton() t.modeRadios.Add( t.currentCharacterButton ) t.modeRadios.Add( t.allCharacterButton ) #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height,FBAttachType.kFBAttachNone,"") t.AddRegion("character_mode_border","Character?", x, y, w, h) t.SetBorder("character_mode_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"current_mode_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height / 2,FBAttachType.kFBAttachNone,"") t.AddRegion("current_mode_region","current_mode_region", x, y, w, h) t.SetControl("current_mode_region", t.currentCharacterButton) t.currentCharacterButton.Visible = True t.currentCharacterButton.ReadOnly = False t.currentCharacterButton.Enabled = True t.currentCharacterButton.Hint = "" t.currentCharacterButton.Caption = "Current" t.currentCharacterButton.State = 1 t.currentCharacterButton.Style = FBButtonStyle.kFBRadioButton t.currentCharacterButton.Justify = FBTextJustify.kFBTextJustifyLeft t.currentCharacterButton.Look = FBButtonLook.kFBLookNormal x = FBAddRegionParam(ui_column_b,FBAttachType.kFBAttachTop,"all_mode_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 2,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height / 2,FBAttachType.kFBAttachNone,"") t.AddRegion("all_mode_region","all_mode_region", x, y, w, h) t.SetControl("all_mode_region", t.allCharacterButton ) t.allCharacterButton.Visible = True t.allCharacterButton.ReadOnly = False t.allCharacterButton.Enabled = True t.allCharacterButton.Hint = "" t.allCharacterButton.Caption = "All" t.allCharacterButton.State = 0 t.allCharacterButton.Style = FBButtonStyle.kFBRadioButton t.allCharacterButton.Justify = FBTextJustify.kFBTextJustifyLeft t.allCharacterButton.Look = FBButtonLook.kFBLookNormal ######################################################################## #Plot Button ui_row_offset += 40 x = FBAddRegionParam(ui_border_x ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("plot_button","plot_button", x, y, w, h) t.plotButton = FBButton() t.plotButton.Caption = "Plot Selected Takes" t.SetControl("plot_button", t.plotButton) #callback t.plotButton.OnClick.Add( PlotButtonCallBack ) ######################################################################## # draw tool ui_row_offset += 70 # set tool ui size t.StartSizeX = ui_border_width + 35 t.StartSizeY = ui_row_offset #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Plot Selected Takes") PopulateTool(t) ShowTool(t) CreateTool() ‰PNG #################################################################### # Quickly Mirror Selected Character and Takes #################################################################### from pyfbsdk import * import RS.Globals from RS.Utils import Scene def plotOptions(): plot_options = FBPlotOptions() plot_options.PlotAllTakes = False plot_options.PlotOnFrame = True plot_options.PlotPeriod = FBTime(0, 0, 0, 1) plot_options.RotationFilterToApply = FBRotationFilter.kFBRotationFilterUnroll plot_options.UseConstantKeyReducer = False plot_options.ConstantKeyReducerKeepOneKey = True plot_options.PlotTranslationOnRootOnly = False return plot_options def getNumberOfSelectedTakes(): number_of_selected_takes = 0 for take_index in range( len( FBSystem().Scene.Takes ) ): if FBSystem().Scene.Takes[ take_index ].Selected == True: number_of_selected_takes += 1 return number_of_selected_takes def plotToRig( character ): character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnControlRig, plotOptions() ) def plotToSkeleton( character ): character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton, plotOptions() ) def getCharacterNodeList( character ): characterNodeList = [] hips = character.GetModel( FBBodyNodeId.kFBHipsNodeId ) dummy = Scene.GetParent( hips ) Scene.GetChildren( dummy, characterNodeList, "", True ) return characterNodeList def getCharacterNodeFromList( characterNodeList, node_name ): for node in characterNodeList: if node.Name == node_name: return node def mirrorCurrentCharacter(): # get current character current_character = FBApplication().CurrentCharacter # make sure rig is off current_character.Active = False # turn on mirror mode current_character.MirrorMode = True # plot to rig plotToRig( current_character ) # turn off mirror mode current_character.MirrorMode = False # get mover node characterNodeList = getCharacterNodeList( current_character ) mover = getCharacterNodeFromList( characterNodeList, 'mover' ) oh_facing = getCharacterNodeFromList( characterNodeList, 'OH_FacingDirection' ) oh_fixup = getCharacterNodeFromList( characterNodeList, 'OH_UpperFixupDirection' ) ######################################################################################## # mirror mover key values ######################################################################################## if mover: # flip Y translation axis key values for key in mover.Translation.GetAnimationNode().Nodes[1].FCurve.Keys: key.Value *= -1.0 # flip Z rotation axis key values and add 180' for key in mover.Rotation.GetAnimationNode().Nodes[2].FCurve.Keys: key.Value *= -1.0 key.Value += 180.0 ######################################################################################## # mirror oh_facing key values ######################################################################################## if oh_facing: # flip Z rotation axis key values for key in oh_facing.Rotation.GetAnimationNode().Nodes[2].FCurve.Keys: key.Value *= -1.0 ######################################################################################## # mirror oh_facing key values ######################################################################################## if oh_fixup: # flip Z rotation axis key values for key in oh_fixup.Rotation.GetAnimationNode().Nodes[2].FCurve.Keys: key.Value *= -1.0 # plot back to skeleton plotToSkeleton( current_character ) ######################################################################################## # flip mover around so animation is facing the same way as it started ######################################################################################## if mover: FBSystem().Scene.Evaluate() mover.Selected = True keyControl = FBKeyControl() current_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart() current_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop() range = FBTimeSpan( current_start, current_end ) current_mover_rotation = FBVector3d() current_mover_translation = FBVector3d() mover.GetVector( current_mover_translation, FBModelTransformationType.kModelTranslation, True ) mover.GetVector( current_mover_rotation, FBModelTransformationType.kModelRotation, True ) keyControl.MoveKeys( range, mover, current_mover_translation, FBVector3d(current_mover_rotation[0], current_mover_rotation[1] - 180.0, current_mover_rotation[2]) , FBVector3d(100.0, 100.0, 100.0), current_start ) mover.Selected = False def quickMirror(): number_of_selected_takes = getNumberOfSelectedTakes() if number_of_selected_takes == 0: mirrorCurrentCharacter() else: for take_index in range( len( FBSystem().Scene.Takes ) ): if FBSystem().Scene.Takes[ take_index ].Selected == True: # go to take FBSystem().CurrentTake = FBSystem().Scene.Takes[ take_index ] mirrorCurrentCharacter() print "Mirroring Complete" #################################################################### # Main #################################################################### quickMirror()‰PNG #################################################################### # Quickly Plot either down to skeleton or vice versa depending on current state #################################################################### from pyfbsdk import * import RS.Globals def plotOptions(): plot_options = FBPlotOptions() plot_options.PlotAllTakes = False plot_options.PlotOnFrame = True plot_options.PlotPeriod = FBTime(0, 0, 0, 1) plot_options.RotationFilterToApply = FBRotationFilter.kFBRotationFilterUnroll plot_options.UseConstantKeyReducer = False plot_options.ConstantKeyReducerKeepOneKey = True plot_options.PlotTranslationOnRootOnly = False return plot_options def getNumberOfSelectedTakes(): number_of_selected_takes = 0 for take_index in range( len( FBSystem().Scene.Takes ) ): if FBSystem().Scene.Takes[ take_index ].Selected == True: number_of_selected_takes += 1 return number_of_selected_takes def plotToRig( character ): character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnControlRig, plotOptions() ) def plotToSkeleton( character ): character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton, plotOptions() ) def ToggleDevices(): for device in FBSystem().Scene.Devices: device.Online = not device.Online def QuickPlot(): # ToggleDevices() current_character = FBApplication().CurrentCharacter # need to create control rig first? if current_character.GetCurrentControlSet() == None: current_character.CreateControlRig( True ) if current_character.Active == True: to_skeleton = True else: to_skeleton = False number_of_selected_takes = getNumberOfSelectedTakes() # if there is one selected take, still ignore it and use the current take # it's a bit annoying when you have one take selected different from the current - chances are the user didn't mean to. if number_of_selected_takes > 1: for take_index in range( len( FBSystem().Scene.Takes ) ): if FBSystem().Scene.Takes[ take_index ].Selected == True: # go to take FBSystem().CurrentTake = FBSystem().Scene.Takes[ take_index ] if to_skeleton == True: plotToSkeleton( current_character ) else: plotToRig( current_character ) else: if to_skeleton == True: plotToSkeleton( current_character ) else: plotToRig( current_character ) # ToggleDevices() print "Plotting Complete" #################################################################### # Main #################################################################### QuickPlot()‰PNG #################################################################### # Captures the current take soft range to a tmp clipboard. # Allows user to set another takes soft range to match this. # Useful for reviewing mocap. #################################################################### from pyfbsdk import * from pyfbsdk_additions import * #################################################################### # UI callbacks #################################################################### def sliderUpdated(control, event): current_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() current_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() current_frame = FBSystem().LocalTime.GetFrame() total_frame_count = current_end - current_start # set zoom bars to be 10% (of total frames) either side of the current time new_zoom_start = current_frame - (control.Value * control.Value * total_frame_count) new_zoom_stop = current_frame + (control.Value * control.Value * total_frame_count) # set values FBPlayerControl().ZoomWindowStart = (FBTime(0,0,0,int(new_zoom_start))) FBPlayerControl().ZoomWindowStop = (FBTime(0,0,0,int(new_zoom_stop))) #################################################################### # UI definition #################################################################### def pasteButtonCallBack(control, event): FBPlayerControl().ZoomWindowStart = (FBTime(0, 0, 0, int(t.zoom_start))) FBPlayerControl().ZoomWindowStop = (FBTime(0, 0, 0, int(t.zoom_end))) def copyButtonCallBack(control, event): t.zoom_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() t.zoom_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() # update paste button UI t.paste_button.Caption = "Set Zoom Range to [ " + str(t.zoom_start) + ":" + str(t.zoom_end) + " ]" def PopulateTool(t): # ui modifiers for easier element positioning ui_tool_width = 275 ui_gap = 5 ui_copy_button_width = 25 ui_copy_button_height = 25 ui_paste_button_width = ui_tool_width - ui_copy_button_width - ui_gap*6 ui_paste_button_height = 25 ui_offset_width = 5 ui_offset_height = 0 # ---------------------------------------------------------------------------- # Reset Button # ---------------------------------------------------------------------------- ui_offset_height += ui_gap x = FBAddRegionParam( ui_offset_width, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam( ui_offset_height, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam( ui_copy_button_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam( ui_copy_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("copy_button_region", "copy_button_region", x, y, w, h) t.copy_button = FBButton() t.SetControl("copy_button_region", t.copy_button) t.copy_button.Visible = True t.copy_button.ReadOnly = False t.copy_button.Enabled = True t.copy_button.Hint = "Copy the zoom range from the current take." t.copy_button.Caption = ">" t.copy_button.State = 0 t.copy_button.Style = FBButtonStyle.kFBPushButton t.copy_button.Justify = FBTextJustify.kFBTextJustifyCenter t.copy_button.Look = FBButtonLook.kFBLookNormal t.copy_button.OnClick.Add(copyButtonCallBack) # ---------------------------------------------------------------------------- # Paste Range Button # ---------------------------------------------------------------------------- # set zoom extents on tool start t.zoom_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() t.zoom_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() ui_offset_width += ui_copy_button_width + ui_gap x = FBAddRegionParam(ui_offset_width, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_offset_height, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_paste_button_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_paste_button_height, FBAttachType.kFBAttachNone, "") t.AddRegion("paste_button_region", "paste_button_region", x, y, w, h) t.paste_button = FBButton() t.SetControl("paste_button_region", t.paste_button) t.paste_button.Visible = True t.paste_button.ReadOnly = False t.paste_button.Enabled = True t.paste_button.Hint = "Sets zoom time range to previous captured range. \nTo update range to current take extents press > button." t.paste_button.Caption = "Set Zoom Range to [ " + str(t.zoom_start) + ":"+ str(t.zoom_end) + " ]" t.paste_button.State = 0 t.paste_button.Style = FBButtonStyle.kFBPushButton t.paste_button.Justify = FBTextJustify.kFBTextJustifyCenter t.paste_button.Look = FBButtonLook.kFBLookNormal t.paste_button.OnClick.Add(pasteButtonCallBack) # ---------------------------------------------------------------------------- # Draw Tool # ---------------------------------------------------------------------------- ui_offset_height += 70 t.StartSizeX = ui_tool_width t.StartSizeY = ui_offset_height ShowTool(t) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Range Clipboard") PopulateTool(t) CreateTool() ‰PNG #################################################################### # Records from a selected object controlled by Physics #################################################################### from pyfbsdk import * from pyfbsdk_additions import * class RecordPhysics(): def __init__(self): self.lSystem = FBSystem() self.lScene = self.lSystem.Scene self.lSysOnUIIdle = self.lSystem.OnUIIdle self.lSysPlayer = FBPlayerControl() self.recording_state = False self.callback_added = False self.selected_model_pos = [ ] self.selected_model_rot = [ ] self.captured_frame_number = [ ] self.record_button = FBButton() # get selected object self.selected_models = FBModelList() FBGetSelectedModels(self.selected_models) if len(self.selected_models) == 0: FBMessageBox("Message", "Nothing selected", "OK", None, None) else: self.selected_model = self.selected_models[ 0 ] # clear selection to allow physics to work! self.clearSelection() def populateLayout(self, tool): """ UI definition """ ui_x_offset = 5 ui_y_offset = 5 ui_x_width = 250 ui_button_height = 30 x = FBAddRegionParam(ui_x_offset, FBAttachType.kFBAttachTop, "") y = FBAddRegionParam(ui_y_offset, FBAttachType.kFBAttachNone, "") w = FBAddRegionParam(ui_x_width, FBAttachType.kFBAttachNone, "") h = FBAddRegionParam(ui_button_height, FBAttachType.kFBAttachNone, "") tool.AddRegion("record_button_region", "record_button_region", x, y, w, h) tool.SetControl("record_button_region", self.record_button) self.record_button.Visible = True self.record_button.ReadOnly = False self.record_button.Enabled = True self.record_button.Hint = "Record" self.record_button.Caption = "Record Current Take" self.record_button.OnClick.Add(self.recordButtonCallBack) def clearSelection(self): """ Clear selection function """ # Get selected models model_list = FBModelList() FBGetSelectedModels(model_list, None, True) # Deselect models for model in model_list: model.Selected = False def recordButtonCallBack(self, control, event): if self.callback_added == False: self.callback_added = True self.record_button.Caption = "Recording" # create new take current_take_name = self.lSystem.CurrentTake.Name self.lSystem.CurrentTake.CopyTake('recorded_' + current_take_name) # go to new take self.lSystem.CurrentTake = self.lSystem.Scene.Takes[ -1 ] # set playback control at start self.lSysPlayer.GotoStart() # start playback self.lSysPlayer.Play() self.lSystem.Scene.Evaluate() print "Capturing animation of " + self.selected_model.Name + "." # add UI callback self.lSysOnUIIdle.Add( self.playbackCallback ) def playbackCallback(self, control, event): if self.lSysPlayer.IsPlaying: self.recording_state = True current_frame = self.lSystem.LocalTime.GetFrame() frame_index = current_frame - self.lSystem.CurrentTake.LocalTimeSpan.GetStart().GetFrame() # only capture pos/rot if it hasn't already this frame if len(self.selected_model_pos) < frame_index + 1: # store frame we are capturing on since we need to handle skipped frames self.captured_frame_number.append(frame_index) # print "capturing position, frame index is " + str(frame_index) + " len of arrays is " + str(len(self.selected_model_pos)) obj_trans = FBVector3d() obj_rot = FBVector3d() self.selected_model.GetVector(obj_trans, FBModelTransformationType.kModelTranslation, True) self.selected_model.GetVector(obj_rot, FBModelTransformationType.kModelRotation, True) self.selected_model_pos.append(obj_trans) self.selected_model_rot.append(obj_rot) ################################################################################### # end frame reached, stop recording paste results ################################################################################### if not self.lSysPlayer.IsPlaying and self.recording_state == True: #if current_frame >= self.lSystem.CurrentTake.LocalTimeSpan.GetStop().GetFrame(): self.lSysPlayer.Stop() # go to new take self.lSystem.CurrentTake = self.lSystem.Scene.Takes[ -1 ] # create null self.null = FBModelNull('recording_of_' + self.selected_model.Name) self.null.Selected = True frame_index = 0 for frame in range(0, len(self.captured_frame_number)): self.lSysPlayer.Goto(FBTime(0, 0, 0, self.captured_frame_number[ frame ])) self.lSystem.Scene.Evaluate() self.null.SetVector(self.selected_model_pos[ frame_index ], FBModelTransformationType.kModelTranslation, True) self.null.SetVector(self.selected_model_rot[ frame_index ], FBModelTransformationType.kModelRotation, True) self.lSystem.Scene.Evaluate() self.lSysPlayer.Key() frame_index += 1 print "Completed capture." # remove callback self.lSystem.OnUIIdle.Remove( self.playbackCallback ) def createTool(): """ Record physics on selected object """ t = FBCreateUniqueTool("Record Physics") t.StartSizeX = 275 t.StartSizeY = 80 rpt = RecordPhysics() rpt.populateLayout(t) ShowTool(t) createTool() ‰PNG #################################################################### # Allows user to paste a pose on character extensions #################################################################### from pyfbsdk import * from pyfbsdk_additions import * # Global UI components MAX_LIST_BOX_ENTRIES = 40 def GetTakeNames(): pass #################################################################### # UI CallBacks #################################################################### def AllTakesCallback(control, event): for item in range( len(t.list.Items) ): t.list.Selected( item, True ) def NoTakesCallback(control, event): for item in range( len(t.list.Items) ): t.list.Selected( item, False ) def RenameCallback(control, event): # capture current user selection selection_list = [] print "Renaming Takes" for item in range( len(t.list.Items) ): #only apply to selected takes if( t.list.IsSelected( item ) ): # cache user selection selection_list.append( True ) #replace string operation first... new_name = FBSystem().Scene.Takes[ item ].Name.replace( t.findEdit.Text, t.replaceEdit.Text) #... then add any prefix or suffix new_name = t.prefix.Text + new_name + t.suffix.Text #capitalise string if wanted if( t.capitaliseButton.State == True ): new_name = new_name.upper() #remove any spaces in string if wanted if( t.whiteSpaceButton.State == True ): new_name = new_name.replace(" ", "") # rename take! FBSystem().Scene.Takes[ item ].Name = new_name else: # cache user selection selection_list.append( False ) # update UI to match new names t.list.Items.removeAll() for iTake in FBSystem().Scene.Takes: t.list.Items.append(iTake.Name) # restore user selection for index in range( len(t.list.Items) ): t.list.Selected( index, selection_list[ index ] ) #################################################################### # UI #################################################################### def PopulateTool(t): # ui variables for easier modification ui_border_x = 10 ui_border_y = 10 ui_border_width = 215 ui_border_height = 100 ui_row_offset = 10 ######################################################################## # All Takes Button x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 6 ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("all_takes_button","all_takes_button", x, y, w, h) t.alltakesbutton = FBButton() t.alltakesbutton.Caption = "All" t.SetControl("all_takes_button", t.alltakesbutton) #callback t.alltakesbutton.OnClick.Add( AllTakesCallback ) ######################################################################## # No Takes Button x = FBAddRegionParam(ui_border_width + 10 - ui_border_width / 4 ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width / 4 ,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("no_takes_button","no_takes_button", x, y, w, h) t.nonetakesbutton = FBButton() t.nonetakesbutton.Caption = "None" t.SetControl("no_takes_button", t.nonetakesbutton) #callback t.nonetakesbutton.OnClick.Add( NoTakesCallback ) ######################################################################## ui_row_offset += 25 #Take List t.list = FBList() t.list.Style = FBListStyle.kFBVerticalList t.list.MultiSelect = True t.list.ExtendedSelect = True x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") # get take names and calculate size required for UI list at same time for iTake in FBSystem().Scene.Takes: t.list.Items.append(iTake.Name) # select UI if user had take selected if ( iTake.Selected == True ): t.list.Selected( len( t.list.Items) - 1, True ) #limit UI size if( len(t.list.Items) < MAX_LIST_BOX_ENTRIES ): ui_row_offset += 15 w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") t.AddRegion("takes","takes", x, y, w, h) t.SetControl("takes", t.list) ######################################################################## #Border ui_row_offset += 40 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height,FBAttachType.kFBAttachNone,"") t.AddRegion("border_a","", x, y, w, h) t.SetBorder("border_a",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ######################################################################## #Prefix Label x = FBAddRegionParam(ui_border_x + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width - 10,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("prefix_region","prefix_region", x, y, w, h) t.prefixLabel = FBLabel() t.SetControl("prefix_region", t.prefixLabel) t.prefixLabel.Justify = FBTextJustify.kFBTextJustifyLeft t.prefixLabel.Visible = True t.prefixLabel.ReadOnly = False t.prefixLabel.Enabled = True t.prefixLabel.Caption = "Prefix" #Prefix Edit ui_row_offset += 20 x = FBAddRegionParam(ui_border_x + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width - 10,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("prefix_edit","prefix_edit", x, y, w, h) t.prefix = FBEdit() t.SetControl("prefix_edit", t.prefix) ######################################################################## #Suffix Label ui_row_offset += 25 x = FBAddRegionParam(ui_border_x + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width - 10,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("suffix_region","suffix_region", x, y, w, h) t.suffixLabel = FBLabel() t.SetControl("suffix_region", t.suffixLabel) t.suffixLabel.Justify = FBTextJustify.kFBTextJustifyLeft t.suffixLabel.Visible = True t.suffixLabel.ReadOnly = False t.suffixLabel.Enabled = True t.suffixLabel.Caption = "Suffix" #Suffix Edit ui_row_offset += 20 x = FBAddRegionParam(ui_border_x + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width - 10,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("suffix_edit","suffix_edit", x, y, w, h) t.suffix = FBEdit() t.SetControl("suffix_edit", t.suffix) ######################################################################## #Border ui_row_offset += 40 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height,FBAttachType.kFBAttachNone,"") t.AddRegion("border_b","", x, y, w, h) t.SetBorder("border_b",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ######################################################################## #Find Label x = FBAddRegionParam(ui_border_x + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width - 10,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("find_region","find_region", x, y, w, h) t.findLabel = FBLabel() t.SetControl("find_region", t.findLabel) t.findLabel.Justify = FBTextJustify.kFBTextJustifyLeft t.findLabel.Visible = True t.findLabel.ReadOnly = False t.findLabel.Enabled = True t.findLabel.Caption = "Find" #Find Edit ui_row_offset += 20 x = FBAddRegionParam(ui_border_x + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width - 10,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("find_edit","find_edit", x, y, w, h) t.findEdit = FBEdit() t.SetControl("find_edit", t.findEdit) ######################################################################## #Replace Label ui_row_offset += 25 x = FBAddRegionParam(ui_border_x + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width - 10,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("replace_label","replace_label", x, y, w, h) t.replaceLabel = FBLabel() t.SetControl("replace_label", t.replaceLabel) t.replaceLabel.Justify = FBTextJustify.kFBTextJustifyLeft t.replaceLabel.Visible = True t.replaceLabel.ReadOnly = False t.replaceLabel.Enabled = True t.replaceLabel.Caption = "Replace" #Replace Edit ui_row_offset += 20 x = FBAddRegionParam(ui_border_x + 5,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width - 10,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("replace_edit","replace_edit", x, y, w, h) t.replaceEdit = FBEdit() t.SetControl("replace_edit", t.replaceEdit) ######################################################################## # Capitalise? ui_row_offset += 45 x = FBAddRegionParam(ui_border_x ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("capitalise","capitalise", x, y, w, h) t.capitaliseButton = FBButton() t.SetControl("capitalise", t.capitaliseButton) t.capitaliseButton.Visible = True t.capitaliseButton.ReadOnly = False t.capitaliseButton.Enabled = True t.capitaliseButton.Hint = "capitalise selected take names" t.capitaliseButton.Caption = "Capitalise names?" t.capitaliseButton.State = 1 t.capitaliseButton.Style = FBButtonStyle.kFBCheckbox t.capitaliseButton.Justify = FBTextJustify.kFBTextJustifyLeft t.capitaliseButton.Look = FBButtonLook.kFBLookNormal ######################################################################## # Remove White Space? ui_row_offset += 25 x = FBAddRegionParam(ui_border_x ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("whiteSpace","whiteSpace", x, y, w, h) t.whiteSpaceButton = FBButton() t.SetControl("whiteSpace", t.whiteSpaceButton) t.whiteSpaceButton.Visible = True t.whiteSpaceButton.ReadOnly = False t.whiteSpaceButton.Enabled = True t.whiteSpaceButton.Hint = "Delete any spaces contained within take names" t.whiteSpaceButton.Caption = "Remove white space?" t.whiteSpaceButton.State = 1 t.whiteSpaceButton.Style = FBButtonStyle.kFBCheckbox t.whiteSpaceButton.Justify = FBTextJustify.kFBTextJustifyLeft t.whiteSpaceButton.Look = FBButtonLook.kFBLookNormal ######################################################################## #Rename Button ui_row_offset += 30 x = FBAddRegionParam(ui_border_x ,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("rename_button","rename_button", x, y, w, h) t.renameButton = FBButton() t.renameButton.Caption = "Rename Selected" t.SetControl("rename_button", t.renameButton) #callback t.renameButton.OnClick.Add( RenameCallback ) ######################################################################## # draw tool ui_row_offset += 70 # set tool ui size t.StartSizeX = 250 t.StartSizeY = ui_row_offset + 10 #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Rename Takes") PopulateTool(t) ShowTool(t) CreateTool() ‰PNG #################################################################### # Allows user to lock selected horse's saddle #################################################################### from pyfbsdk import * SKEL_SADDLE = None SKEL_RearSeat = None # Get Skel_Seat obj_list = FBComponentList() FBFindObjectsByName('SKEL_SADDLE', obj_list , False, False) if len(obj_list) > 0: SKEL_SADDLE = obj_list[0] obj_list = FBComponentList() FBFindObjectsByName('SKEL_RearSeat', obj_list , False, False) if len(obj_list) > 0: SKEL_RearSeat = obj_list[0] if SKEL_SADDLE != None and SKEL_RearSeat != None: # Lock SKEL_SADDLE SKEL_SADDLE.PropertyList.Find("Enable Translation DOF").Data = True SKEL_SADDLE.PropertyList.Find("TranslationMinX").Data = True SKEL_SADDLE.PropertyList.Find("TranslationMinY").Data = True SKEL_SADDLE.PropertyList.Find("TranslationMinZ").Data = True SKEL_SADDLE.PropertyList.Find("TranslationMaxX").Data = True SKEL_SADDLE.PropertyList.Find("TranslationMaxY").Data = True SKEL_SADDLE.PropertyList.Find("TranslationMaxZ").Data = True SKEL_SADDLE.PropertyList.Find("TranslationMin").Data = FBVector3d(0.11934,-0.12884,0.00000) SKEL_SADDLE.PropertyList.Find("TranslationMax").Data = FBVector3d(0.11934,-0.12884,0.00000) # Lock SKEL_RearSeat SKEL_RearSeat.PropertyList.Find("Enable Translation DOF").Data = True SKEL_RearSeat.PropertyList.Find("TranslationMinX").Data = True SKEL_RearSeat.PropertyList.Find("TranslationMinY").Data = True SKEL_RearSeat.PropertyList.Find("TranslationMinZ").Data = True SKEL_RearSeat.PropertyList.Find("TranslationMaxX").Data = True SKEL_RearSeat.PropertyList.Find("TranslationMaxY").Data = True SKEL_RearSeat.PropertyList.Find("TranslationMaxZ").Data = True SKEL_RearSeat.PropertyList.Find("TranslationMin").Data = FBVector3d(-0.18352,-0.14310,0.00000) SKEL_RearSeat.PropertyList.Find("TranslationMax").Data = FBVector3d(-0.18352,-0.14310,0.00000) else: print "SKEL_SADDLE or SKEL_RearSeat dofs not found in scene!"‰PNG from pyfbsdk import * SEARCH_STRING = 'mover' #get current character character = FBApplication().CurrentCharacter if character is not None: obj_to_select = FBFindModelByLabelName(character.OwnerNamespace.Name + ':' + SEARCH_STRING) if obj_to_select is not None: obj_to_select.Selected = True else: print SEARCH_STRING + ' Not Found!'‰PNG from pyfbsdk import * SEARCH_STRING = 'PH_R_Hand' #get current character character = FBApplication().CurrentCharacter if character is not None: obj_to_select = FBFindModelByLabelName(character.OwnerNamespace.Name + ':' + SEARCH_STRING) if obj_to_select is not None: obj_to_select.Selected = True else: print SEARCH_STRING + ' Not Found!'‰PNG from pyfbsdk import * from pyfbsdk_additions import * from RS.Utils.ContextManagers import SceneExpressionsDisabled def clamp(n, minn, maxn): return max(min(maxn, n), minn) #################################################################### # UI callbacks #################################################################### def enableAngleButtonCallback(control, event): if t.enableAngleButton.State: t.absAngle.Enabled = True else: t.absAngle.Enabled = False return def enableAutoButtonCallback(control, event): if t.enableAutoButton.State: t.minHeight.Enabled = False t.maxHeight.Enabled = False else: t.minHeight.Enabled = True t.maxHeight.Enabled = True return def findAnimationNode( pName, pNode ): lResult = None lName = pName.split( '/' ) for lNode in pNode.Nodes: if lNode.Name == lName[0]: if len( lName ) > 1: lResult = findAnimationNode( pName.replace( '%s/' % lName[0], '' ), lNode ) else: lResult = lNode return lResult def resetButtonCallBack(control, event): # Disable all expressions for the duration of this event with SceneExpressionsDisabled(): # get selection models = FBModelList() FBGetSelectedModels( models ) num_of_models = len( models ) if num_of_models == 0: FBMessageBox( "Message", "Nothing selected", "OK", None, None ) else: print "Resetting IK" for model in models: ik_property = model.PropertyList.Find('IK Reach Translation') if ik_property: animNode = ik_property.GetAnimationNode() if animNode: animNode.FCurve.EditClear() ik_property.Data = 100.0 ik_property.SetAnimated( False ) ik_property = model.PropertyList.Find('IK Reach Rotation') if ik_property: animNode = ik_property.GetAnimationNode() if animNode: animNode.FCurve.EditClear() ik_property.Data = 100.0 ik_property.SetAnimated( False ) ik_property = model.PropertyList.Find('IK Pull') if ik_property: animNode = ik_property.GetAnimationNode() if animNode: animNode.FCurve.EditClear() ik_property.Data = 0.0 ik_property.SetAnimated( False ) ik_property = model.PropertyList.Find('IK Pull Hips') if ik_property: animNode = ik_property.GetAnimationNode() if animNode: animNode.FCurve.EditClear() ik_property.Data = 0.0 ik_property.SetAnimated( False ) def applyButtonCallBack(control, event): # Disable all expressions for the duration of this event with SceneExpressionsDisabled(): # get selection models = FBModelList() FBGetSelectedModels( models ) model_height = [] model_rotation_z = [] model_translation = FBVector3d() num_of_models = len(models) if num_of_models == 0: FBMessageBox( "Message", "Nothing selected", "OK", None, None ) else: # get time span current_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() current_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() lowest_height = [] highest_height = [] # initialise heights for model in models: lowest_height.append( 1000 ) highest_height.append( -1000 ) for frame in range(current_start, current_end + 1): # update slider FBPlayerControl().Goto(FBTime(0,0,0,frame)) model_count = 0 # get height in WS of each object for model in models: FBSystem().Scene.Evaluate() # get ws position model.GetVector( model_translation, FBModelTransformationType.kModelTranslation, True) #model.GetVector( model_rotation, FBModelTransformationType.kModelRotation, False ) if model_translation[1] < lowest_height[model_count]: lowest_height[model_count] = model_translation[1] if model_translation[1] > highest_height[model_count]: highest_height[model_count] = model_translation[1] rotationNode = findAnimationNode( 'Lcl Rotation/Z', model.AnimationNode ) model_rotation = rotationNode.FCurve.Evaluate( FBTime(0,0,0,frame) ) # store height (Y axis) model_height.append( model_translation[1] ) # store angle (z axis) model_rotation_z.append( model_rotation ) model_count += 1 key_count = 0 for frame in range(current_start, current_end + 1): model_count = 0 # print percentage of zero height for each object for model in models: if t.enableAutoButton.State: max_height = highest_height[ model_count ] min_height = lowest_height[ model_count ] else: max_height = t.maxHeight.Value min_height = t.minHeight.Value # simply clamp values outside ranges since we want to deal with IK weights between 0, 100 only clamped_model_height = clamp( model_height[ key_count * num_of_models + model_count ], min_height, max_height ) height_weight_percent = ( ( clamped_model_height - min_height ) / ( max_height - min_height ) ) * 100.0 # finally we need to flip the percentage (since max means zero weight and vice versa) height_weight_percent = 100 - height_weight_percent # set appropriate IK weight for selected transforms # first see if attribute exists (valid IK effector) if t.transButton.State: ik_property = model.PropertyList.Find('IK Reach Translation') if ik_property: ik_property.SetAnimated(True) ik_property.GetAnimationNode().FCurve.KeyAdd( FBTime(0,0,0,frame), height_weight_percent ) # if we're at the end frame and we want to filter curves if frame == current_end and t.enableSmoothFilterButton.State: lFilter = FBFilterManager().CreateFilter( 'Smooth' ) if lFilter: lFilter.Apply( ik_property.GetAnimationNode(), True ) if t.rotButton.State: ik_property = model.PropertyList.Find('IK Reach Rotation') if ik_property: ik_property.SetAnimated(True) if t.enableAngleButton.State == 1: # calculate abs angle and obtain rotation weighting abs_angle = abs( model_rotation_z[ key_count * num_of_models + model_count ] ) clamped_abs_angle = clamp( abs_angle, 0.0, t.absAngle.Value ) angle_weight_percent = ( clamped_abs_angle / t.absAngle.Value ) * 100.0 # we need to flip the percentage (since max means zero weight and vice versa) angle_weight_percent = 100 - angle_weight_percent multiplied_height_and_angle_percent = ( angle_weight_percent * height_weight_percent ) / 100.0 ik_property.GetAnimationNode().FCurve.KeyAdd( FBTime(0,0,0,frame), multiplied_height_and_angle_percent ) else: ik_property.GetAnimationNode().FCurve.KeyAdd( FBTime(0,0,0,frame), height_weight_percent ) # if we're at the end frame and we want to filter curves if frame == current_end and t.enableSmoothFilterButton.State: lFilter = FBFilterManager().CreateFilter( 'Smooth' ) if lFilter: lFilter.Apply( ik_property.GetAnimationNode(), True ) model_count += 1 key_count += 1 #################################################################### # UI definition #################################################################### def PopulateTool(t): # UI constants ui_x_offset = 10 ui_y_offset = 5 ui_x_width = 240 ui_button_height = 30 ui_button_width = 40 ui_column_a = 20 ui_column_b = 100 ui_column_c = 100 ########################################################################################## # Reset IK Button ######################################################################################### x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(25,FBAttachType.kFBAttachNone,"") t.AddRegion("reset_button","reset_button", x, y, w, h) t.resetButton = FBButton() t.SetControl("reset_button", t.resetButton ) t.resetButton.Visible = True t.resetButton.ReadOnly = False t.resetButton.Enabled = True t.resetButton.Hint = "Deletes any IK T/R animation on selected IK pivots, and resets them to 100%" t.resetButton.Caption = "Reset Selected IK" t.resetButton.State = 0 t.resetButton.Style = FBButtonStyle.kFBPushButton t.resetButton.Justify = FBTextJustify.kFBTextJustifyCenter t.resetButton.Look = FBButtonLook.kFBLookNormal t.resetButton.OnClick.Add( resetButtonCallBack ) ui_y_offset += 30 ########################################################################################## # Min Height for IK effectors ########################################################################################## ui_y_offset += 10 x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height + 10,FBAttachType.kFBAttachNone,"") t.AddRegion("min_height_border","Height which IK weight is 100% (cms)", x, y, w, h) t.SetBorder("min_height_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_y_offset += 10 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 40,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("min_height_edit","min_height_edit", x, y, w, h) t.minHeight = FBEditNumber() t.SetControl("min_height_edit", t.minHeight) t.minHeight.Visible = True t.minHeight.ReadOnly = False t.minHeight.Enabled = False t.minHeight.Value = 10.0 ui_y_offset += ui_button_height + 10 ########################################################################################## # Max Height for IK effectors ########################################################################################## ui_y_offset += 10 x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height + 10,FBAttachType.kFBAttachNone,"") t.AddRegion("max_height_border","Height which IK weight is 0% (cms)", x, y, w, h) t.SetBorder("max_height_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_y_offset += 10 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 40,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("max_height_edit","max_height_edit", x, y, w, h) t.maxHeight = FBEditNumber() t.SetControl("max_height_edit", t.maxHeight) t.maxHeight.Visible = True t.maxHeight.ReadOnly = False t.maxHeight.Enabled = False t.maxHeight.Value = 60.0 ########################################################################################## # Use Auto Extents instead ########################################################################################## ui_y_offset += ui_button_height + 10 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 40,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("enable_auto","enable_auto", x, y, w, h) t.enableAutoButton = FBButton() t.SetControl("enable_auto", t.enableAutoButton) t.enableAutoButton.Visible = True t.enableAutoButton.ReadOnly = False t.enableAutoButton.Enabled = True t.enableAutoButton.Caption = "Auto Extents" t.enableAutoButton.State = 1 t.enableAutoButton.Style = FBButtonStyle.kFBCheckbox t.enableAutoButton.Justify = FBTextJustify.kFBTextJustifyLeft t.enableAutoButton.Look = FBButtonLook.kFBLookNormal t.enableAutoButton.OnClick.Add( enableAutoButtonCallback ) ########################################################################################## # Min Angle for IK effectors ########################################################################################## ui_y_offset += ui_button_height + 5 x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height + 40,FBAttachType.kFBAttachNone,"") t.AddRegion("abs_angle_border","Absolute Angle which IK weight is 0% (Z Axis)", x, y, w, h) t.SetBorder("abs_angle_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) # enable button ui_y_offset += 15 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 40,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("enable_angle","enable_angle", x, y, w, h) t.enableAngleButton = FBButton() t.SetControl("enable_angle", t.enableAngleButton) t.enableAngleButton.Visible = True t.enableAngleButton.ReadOnly = False t.enableAngleButton.Enabled = True t.enableAngleButton.Caption = "Enable?" t.enableAngleButton.State = 0 t.enableAngleButton.Style = FBButtonStyle.kFBCheckbox t.enableAngleButton.Justify = FBTextJustify.kFBTextJustifyLeft t.enableAngleButton.Look = FBButtonLook.kFBLookNormal t.enableAngleButton.OnClick.Add( enableAngleButtonCallback ) # edit box ui_y_offset += 25 x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 40,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("abs_angle_edit","abs_angle_edit", x, y, w, h) t.absAngle = FBEditNumber() t.SetControl("abs_angle_edit", t.absAngle) t.absAngle.Visible = True t.absAngle.ReadOnly = False t.absAngle.Enabled = False t.absAngle.Value = 30.0 ui_y_offset += ui_button_height + 10 ########################################################################################## # IK effectors to modify ########################################################################################## ui_y_offset += 10 # border x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_height * 2 + 10,FBAttachType.kFBAttachNone,"") t.AddRegion("IK_Effectors_border","IK transforms to modify", x, y, w, h) t.SetBorder("IK_Effectors_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_y_offset += 15 # translation button x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 40,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("trans_button","trans_button", x, y, w, h) t.transButton = FBButton() t.SetControl("trans_button", t.transButton) t.transButton.Visible = True t.transButton.ReadOnly = False t.transButton.Enabled = True t.transButton.Hint = "Modify weight of Translation on IK effector?" t.transButton.Caption = "IK Blend T" t.transButton.State = 1 t.transButton.Style = FBButtonStyle.kFBCheckbox t.transButton.Justify = FBTextJustify.kFBTextJustifyLeft t.transButton.Look = FBButtonLook.kFBLookNormal ui_y_offset += 25 # rotation button x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 40,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("rot_button","rot_button", x, y, w, h) t.rotButton = FBButton() t.SetControl("rot_button", t.rotButton) t.rotButton.Visible = True t.rotButton.ReadOnly = False t.rotButton.Enabled = True t.rotButton.Hint = "Modify weight of Rotation on IK effector?" t.rotButton.Caption = "IK Blend R" t.rotButton.State = 1 t.rotButton.Style = FBButtonStyle.kFBCheckbox t.rotButton.Justify = FBTextJustify.kFBTextJustifyLeft t.rotButton.Look = FBButtonLook.kFBLookNormal ui_y_offset += 40 ########################################################################################## # Use Smooth Filter? ########################################################################################## x = FBAddRegionParam(ui_x_offset + 10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 40,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(20,FBAttachType.kFBAttachNone,"") t.AddRegion("smooth_filter","smooth_filter", x, y, w, h) t.enableSmoothFilterButton = FBButton() t.SetControl("smooth_filter", t.enableSmoothFilterButton) t.enableSmoothFilterButton.Visible = True t.enableSmoothFilterButton.ReadOnly = False t.enableSmoothFilterButton.Enabled = True t.enableSmoothFilterButton.Caption = "Smooth Filter on Weight Curves?" t.enableSmoothFilterButton.State = 1 t.enableSmoothFilterButton.Style = FBButtonStyle.kFBCheckbox t.enableSmoothFilterButton.Justify = FBTextJustify.kFBTextJustifyLeft t.enableSmoothFilterButton.Look = FBButtonLook.kFBLookNormal ########################################################################################## # Apply Button ########################################################################################## ui_y_offset += 30 x = FBAddRegionParam(ui_x_offset,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_y_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_x_width - 20,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(25,FBAttachType.kFBAttachNone,"") t.AddRegion("apply_button","apply_button", x, y, w, h) t.applyButton = FBButton() t.SetControl("apply_button", t.applyButton ) t.applyButton.Visible = True t.applyButton.ReadOnly = False t.applyButton.Enabled = True t.applyButton.Hint = "" t.applyButton.Caption = "Apply" t.applyButton.State = 0 t.applyButton.Style = FBButtonStyle.kFBPushButton t.applyButton.Justify = FBTextJustify.kFBTextJustifyCenter t.applyButton.Look = FBButtonLook.kFBLookNormal t.applyButton.OnClick.Add( applyButtonCallBack ) ui_y_offset += ui_button_height + 30 ########################################################################################## # Calculate Tool Window Size and Draw ########################################################################################## t.StartSizeX = ui_x_width + ui_x_offset * 2 t.StartSizeY = ui_y_offset + 10 ShowTool(t) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("SetIKRelativeToHeight 2014") PopulateTool(t) CreateTool() ‰PNG #################################################################### # Changes the in and out interpolation of all story clips to smooth #################################################################### from pyfbsdk import * def getClipsInTrack(track,clips): for clip in track.Clips: clips.append(clip) for subtrack in track.SubTracks: getClipsInTrack(subtrack, clips) return clips def smoothAllCharacterTracks(): story = FBStory() tracks = story.RootFolder.Tracks clips = [] for track in tracks: getClipsInTrack(track,clips) for clip in clips: # Set to smooth. Enum value is '2'. prop = clip.PropertyList.Find("Fade In Interpolation") if prop: prop.Data = 2 prop = clip.PropertyList.Find("Fade Out Interpolation") if prop: prop.Data = 2 smoothAllCharacterTracks() #################################################################### # Create Secondary Motion #################################################################### from pyfbsdk import * from pyfbsdk_additions import * import math #################################################################### # Vector Ops - Replace? #################################################################### def VectorAdd( a, b ): c = FBVector3d() c[0] = a[0] + b[0] c[1] = a[1] + b[1] c[2] = a[2] + b[2] return c def VectorSub( a, b ): c = FBVector3d() c[0] = a[0] - b[0] c[1] = a[1] - b[1] c[2] = a[2] - b[2] return c def VectorLength( a ): return math.sqrt( a[0]*a[0] + a[1]*a[1] + a[2]*a[2] ) def VectorNorm( v ): len = VectorLength( v ) if len > 0.0: v[0] /= len v[1] /= len v[2] /= len return v def VectorMul(a, scale ): c = FBVector3d() c[0] = a[0] * scale c[1] = a[1] * scale c[2] = a[2] * scale return c def VectorDiv( a, scale ): c = FBVector3d() c[0] = a[0] / scale c[1] = a[1] / scale c[2] = a[2] / scale return c def VectorDot( a, b ): return a[0]*b[0] + a[1]*b[1] + a[2]*b[2] #################################################################### def GetSelectedObject(): models = FBModelList() FBGetSelectedModels(models) if len(models) == 0: return None else: return models[0] def CalculateSpring( dt, inputPos, oldPos, linVel ): mass = 1.0 # <-- replace with mass variable! friction = 0.1 # <-- replace with friction variable! stiffness = 0.1 # <-- replace with stiffness variable! damping = 0.01 # <-- replace with damping variable! lForce = FBVector3d() resistance = FBVector3d() # position & velocity px1 = FBVector3d() px2 = FBVector3d() pv1 = FBVector3d() pv2 = FBVector3d() dx = FBVector3d() dv = FBVector3d() # vector length m = 0.0 r = 0.0 value = 0.0 # position px1 = inputPos px2 = oldPos dx = VectorSub(px1, px2) # velocity pv1 = VectorSub(inputPos, oldPos) pv2 = linVel dv = VectorSub(pv1, pv2) # force r = 0.0 # <-- replace with length variable! m = VectorLength(dx) if m == 0.0: m = 0.0001 lForce = dx lForce = VectorNorm(lForce) value = (stiffness * (m - r) + damping * (VectorDot( dv, dx )/m) ) lForce = VectorMul(lForce, value) lForce = VectorDiv(lForce, mass) # add friction resistance = VectorMul(linVel, -friction) # add resistance to total forces lForce = VectorAdd(lForce, resistance ) # update velocity linVel = VectorAdd(linVel, lForce ) # update position based on new velocity newPositions = VectorAdd( oldPos, linVel ) return newPositions, linVel def Main(): # get selected object obj = GetSelectedObject() if obj is None: FBMessageBox("Message", "Nothing selected", "OK", None, None) else: FBSystem().Scene.Evaluate() # frame range take_start_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() take_end_frame = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() # initialise inputPositions = [] inputRotations = [] newPositions = [] newRotations = [] linVel = FBVector3d() angVel = FBVector3d() inputPos = FBVector3d() inputRot = FBVector3d() # init position obj.GetVector( inputPos, FBModelTransformationType.kModelTranslation, True ) currentPos = inputPos oldPos = currentPos # init rotation obj.GetVector(inputRot, FBModelTransformationType.kModelRotation, True) currentRot = inputRot oldRot = currentRot dt = 1.0 / 30.0 #################################################################### # main loop through frames to calculate simulation #################################################################### frame_count = 0 for frame in range(take_start_frame, take_end_frame + 1): # update frame FBPlayerControl().Goto(FBTime(0, 0, 0, frame)) FBSystem().Scene.Evaluate() # get current transformation obj.GetVector(inputPos, FBModelTransformationType.kModelTranslation, True) obj.GetVector(inputRot, FBModelTransformationType.kModelRotation, True) # apply Spring Forces outPos, linVel = CalculateSpring( dt, inputPos, oldPos, linVel ) outRot, angVel = CalculateSpring( dt, inputRot, oldRot, angVel ) # Store pre/post simulation results newPositions.append(FBVector3d(outPos[0],outPos[1],outPos[2])) inputPositions.append(FBVector3d(inputPos[0],inputPos[1],inputPos[2])) newRotations.append(FBVector3d(outRot[ 0 ], outRot[ 1 ], outRot[ 2 ])) inputRotations.append(FBVector3d(inputRot[ 0 ], inputRot[ 1 ], inputRot[ 2 ])) oldPos = currentPos currentPos = newPositions[frame_count] oldRot = currentRot currentRot = newRotations[frame_count] frame_count += 1 #################################################################### # apply results #################################################################### # create layer for results FBSystem().CurrentTake.CreateNewLayer() # select new layer and rename num_layers = FBSystem().CurrentTake.GetLayerCount() new_layer = FBSystem().CurrentTake.GetLayer(num_layers - 1) FBSystem().CurrentTake.SetCurrentLayer(num_layers - 1) new_layer.SelectLayer(True, True) new_layer.Name = "SpringLayer (" + str(obj.Name) + ")" frame_count = 0 for frame in range(take_start_frame, take_end_frame + 1): # apply position obj.Translation.GetAnimationNode().Nodes[ 0 ].KeyAdd(FBTime(0, 0, 0, frame), newPositions[frame_count][ 0 ] - inputPositions[frame_count][0] ) obj.Translation.GetAnimationNode().Nodes[ 1 ].KeyAdd(FBTime(0, 0, 0, frame), newPositions[ frame_count ][ 1 ] - inputPositions[frame_count][1] ) obj.Translation.GetAnimationNode().Nodes[ 2 ].KeyAdd(FBTime(0, 0, 0, frame), newPositions[ frame_count ][ 2 ] - inputPositions[frame_count][2] ) # apply rotation obj.Rotation.GetAnimationNode().Nodes[ 0 ].KeyAdd(FBTime(0, 0, 0, frame), newRotations[frame_count][ 0 ] - inputRotations[frame_count][0] ) obj.Rotation.GetAnimationNode().Nodes[ 1 ].KeyAdd(FBTime(0, 0, 0, frame), newRotations[ frame_count ][ 1 ] - inputRotations[frame_count][1] ) obj.Rotation.GetAnimationNode().Nodes[ 2 ].KeyAdd(FBTime(0, 0, 0, frame), newRotations[ frame_count ][ 2 ] - inputRotations[frame_count][2] ) frame_count += 1 Main() ‰PNG #################################################################### # Toggles visibility of various OH dofs on character, and # renders them as toon shader #################################################################### from pyfbsdk import * from RS.Utils import Scene def getCharacterHeirarchyList(character): characterNodeList = [] hips = character.GetModel( FBBodyNodeId.kFBHipsNodeId ) dummy = Scene.GetParent( hips ) Scene.GetChildren( dummy, characterNodeList, "", True ) return characterNodeList def createOrGetToonShader(): for shader in FBSystem().Scene.Shaders: if 'FlatCartoonShader' in shader.Name or 'Edge Cartoon' in shader.Name: return shader # create a cartoon shader if there isn't one available to use return FBShaderManager().CreateShader("FlatCartoonShader") currentCharacter = FBApplication().CurrentCharacter if not currentCharacter == None: characterNodeList = getCharacterHeirarchyList(currentCharacter) dofs = { 'mover':None, 'oh_independentmoverdirection':None, 'oh_upperfixupdirection':None, 'oh_torsodirection':None, 'oh_facingdirection':None, 'oh_moveroffset':None, 'ph_movergroundlocation':None, 'camerabasepivotoffset_controller':None } for node in characterNodeList: node_name = node.Name.lower() if node_name in dofs: dofs[node_name] = node myToonShader = createOrGetToonShader() if dofs['mover'] != None: moverVisibility = dofs['mover'].Show for dof in dofs: if dofs[dof] != None: myToonShader.ReplaceAll( dofs[dof] ) dofs[dof].ShadingMode = FBModelShadingMode.kFBModelShadingAll dofs[dof].Show = not moverVisibility‰PNG #################################################################### # Update Mover Node # Mike Jones - November 2009 # # Will create UI and allow user to auto animate a character's mover node. # # Known limitations: # Mover node has to be called player:mover # Assumes user will want mover to default to (global) pos 0,100,0 rot -90,0,0 # Gets rotational info from player:SKEL_Pelvis not player:SKEL_Root - Needs to be tested with new skel! # # #################################################################### from pyfbsdk import * from pyfbsdk_additions import * from RS.Utils.ContextManagers import SceneExpressionsDisabled from RS import Globals # Global UI components trans_x_button = FBButton() trans_y_button = FBButton() trans_z_button = FBButton() trans_offset_button = FBButton() rot_y_button = FBButton() rot_offset_button = FBButton() rot_gimble_button = FBButton() frame_radios = FBButtonGroup() frame_current_button = FBButton() frame_all_button = FBButton() frame_radios.Add(frame_current_button) frame_radios.Add(frame_all_button) take_radios = FBButtonGroup() take_current_button = FBButton() take_selected_button = FBButton() take_all_button = FBButton() take_radios.Add(take_current_button) take_radios.Add(take_selected_button) take_radios.Add(take_all_button) go_button = FBButton() def getMoverNodes(): # get list of all models called 'mover' mover_list = FBComponentList() FBFindObjectsByName('mover', mover_list , False, True) FBFindObjectsByName('Mover', mover_list , False, True) FBFindObjectsByName('MOVER', mover_list , False, True) filtered_mover_list = [] chars = FBSystem().Scene.Characters # go through each model called mover and find which ones actually belong to characters # so we get rid of vehicles etc. # put result in new filtered_mover_list for object in mover_list: for char in chars: extensions = char.CharacterExtensions for e in extensions: components = e.Components for c in components: if c.LongName == object.LongName: filtered_mover_list.append(object) return filtered_mover_list def setupPropertyList(tool): tool.list.Items.removeAll() tool.prop_list = [] mover_list = getMoverNodes() if len(mover_list): for mover in mover_list: tool.list.Items.append(mover.LongName) else: tool.list.Items.append('No mover node found!') def resetMoverNode(moverNode): # current mover defaults (local coords) vMoverDefaultT = FBVector3d(0.0, 0.0, 1.0) vMoverDefaultR = FBVector3d(0.0, 0.0, 0.0) # set pos/rot moverNode.SetVector (vMoverDefaultR, FBModelTransformationType.kModelRotation, False) moverNode.SetVector (vMoverDefaultT, FBModelTransformationType.kModelTranslation, False) FBSystem().Scene.Evaluate() return def rotButtonCallback(control, event): if rot_y_button.State: rot_offset_button.Enabled = True rot_gimble_button.Enabled = True else: rot_offset_button.Enabled = False rot_gimble_button.Enabled = False return def transButtonCallback(control, event): if trans_x_button.State or trans_y_button.State or trans_z_button.State: trans_offset_button.Enabled = True else: trans_offset_button.Enabled = False return #################################################################### # Go Button CallBack #################################################################### def goButtonCallBack(control, event): with SceneExpressionsDisabled(): # get bones chosenMover = t.list.Items[t.list.ItemIndex] mMover = FBFindModelByLabelName(chosenMover) # need to replace the mover with bone mRoot = FBFindModelByLabelName( ( chosenMover.rsplit(':', 1 ) )[0] + ":SKEL_ROOT" ) mPelvis = FBFindModelByLabelName( ( chosenMover.rsplit(':', 1 ) )[0] + ":SKEL_Pelvis" ) # error check models if not (mRoot or mPelvis or mMover): print "Error! Cannot find part of heirarchy!" return # -------------------------------------------------------- # create take list depending on Takes radio buttons # -------------------------------------------------------- take_list = [] if take_all_button.State: take_list = FBSystem().Scene.Takes user_response = FBMessageBox( "Warning", "Are you sure you want to update all takes?", "Yes", "No" ) if user_response == 2: return elif take_selected_button.State: user_response = FBMessageBox( "Warning", "Do you have all of your Takes selected?\n\nIf not, Hit 'No', select your takes and then try again.", "Yes", "No" ) if user_response == 2: return for take in Globals.Takes: if take.Selected == True: take_list.append(take) if len(take_list) == 0: FBMessageBox( "Warning", "There are no Takes selected.\n\nPlease select required takes and try again.", "Ok") else: take_list.append(FBSystem().CurrentTake) # -------------------------------------------------------- # main loop for every take # -------------------------------------------------------- for take in take_list: FBSystem().CurrentTake = take # make sure keys are set on the base layer FBSystem().CurrentTake.SetCurrentLayer(0) # -------------------------------------------------------- # get scene info - time/takes etc. # -------------------------------------------------------- current_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() current_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() current_frame = FBSystem().LocalTime.GetFrame() current_take = FBSystem().CurrentTake # -------------------------------------------------------- # switch to first frame of animation, # and reset mover position/rotation to creation defaults(keep offset), # or align to root(no offset), # before applying constraints # -------------------------------------------------------- FBPlayerControl().Goto(FBTime(0,0,0,current_start)) resetMoverNode(mMover) if not trans_offset_button.State: # get root position root_trans = FBVector3d() mRoot.GetVector (root_trans, FBModelTransformationType.kModelTranslation, True) # set mover to match root pos mMover.SetVector (root_trans, FBModelTransformationType.kModelTranslation, True) if not rot_offset_button.State: pelvis_rot = FBVector3d() mPelvis.GetVector (pelvis_rot, FBModelTransformationType.kModelRotation, False) # set mover to match root pos mMover.SetVector (pelvis_rot, FBModelTransformationType.kModelRotation, False) FBSystem().Scene.Evaluate() # -------------------------------------------------------- # create position constraint between SKEL_Root/mover # -------------------------------------------------------- if trans_x_button.State or trans_y_button.State or trans_z_button.State: lMgr = FBConstraintManager() # get index to parent/child constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Position': lIndex = i break # create constraint lPosConst = lMgr.TypeCreateConstraint(lIndex) #FBSystem().Scene.Constraints.append(lPosConst) # add ref objects for i in range(0, lPosConst.ReferenceGroupGetCount()): if lPosConst.ReferenceGroupGetName(i) == 'Source': lPosConst.ReferenceAdd(i, mRoot) elif lPosConst.ReferenceGroupGetName(i) == 'Constrained Object': lPosConst.ReferenceAdd(i, mMover) # snap/activate constraint lPosConst.Snap() # -------------------------------------------------------- # create rotation constraint between SKEL_Pelvis/mover # -------------------------------------------------------- if rot_y_button.State: lMgr = FBConstraintManager() # get index to parent/child constraint lIndex = None for i in range(0, lMgr.TypeGetCount()): if lMgr.TypeGetName(i) == 'Rotation': lIndex = i break # create constraint lRotConst = lMgr.TypeCreateConstraint(lIndex) #FBSystem().Scene.Constraints.append(lRotConst) # add ref objects for i in range(0, lRotConst.ReferenceGroupGetCount()): if lRotConst.ReferenceGroupGetName(i) == 'Source': lRotConst.ReferenceAdd(i, mPelvis) elif lRotConst.ReferenceGroupGetName(i) == 'Constrained Object': lRotConst.ReferenceAdd(i, mMover) # snap/activate constraint lRotConst.Snap() # -------------------------------------------------------- # key constraints - current frame only # -------------------------------------------------------- if frame_current_button.State == True: # return player to original frame FBPlayerControl().Goto(FBTime(0,0,0,current_frame)) # get mover translation and rotation mover_trans = FBVector3d() mover_rot = FBVector3d() FBSystem().Scene.Evaluate() mMover.GetVector (mover_trans, FBModelTransformationType.kModelTranslation, False) mMover.GetVector (mover_rot, FBModelTransformationType.kModelRotation, False) # key mover if trans_x_button.State: mMover.Translation.GetAnimationNode().Nodes[0].KeyAdd(mover_trans[0]) if trans_y_button.State: mMover.Translation.GetAnimationNode().Nodes[1].KeyAdd(mover_trans[1]) if trans_z_button.State: mMover.Translation.GetAnimationNode().Nodes[2].KeyAdd(mover_trans[2]) if rot_y_button.State: mMover.Rotation.GetAnimationNode().Nodes[0].KeyAdd(0) mMover.Rotation.GetAnimationNode().Nodes[1].KeyAdd(0) mMover.Rotation.GetAnimationNode().Nodes[2].KeyAdd(mover_rot[2]) # reset mover before deleting constraints resetMoverNode(mMover) # delete constraints if trans_x_button.State or trans_y_button.State or trans_z_button.State: lPosConst.FBDelete() if rot_y_button.State: lRotConst.FBDelete() # -------------------------------------------------------- # key constraints - all frames # -------------------------------------------------------- else: trans_array = [] rot_array = [] #---------------------------------------------------------------------------- # copy pos/rot values for index in range(current_start,current_end+1): # update time slider FBPlayerControl().Goto(FBTime(0,0,0,index)) FBSystem().Scene.Evaluate() # get mover translation and rotation mover_trans = FBVector3d() mover_rot = FBVector3d() mMover.GetVector (mover_trans, FBModelTransformationType.kModelTranslation, False) mMover.GetVector (mover_rot, FBModelTransformationType.kModelRotation, False) trans_array.append(mover_trans) rot_array.append(mover_rot) #---------------------------------------------------------------------------- # reset mover before deleting constraints resetMoverNode(mMover) # delete constraints if trans_x_button.State or trans_y_button.State or trans_z_button.State: lPosConst.FBDelete() if rot_y_button.State: lRotConst.FBDelete() #---------------------------------------------------------------------------- # set pos/rot values count = 0 for index in range(current_start,current_end+1): # key values on to mover FBPlayerControl().Goto(FBTime(0,0,0,index)) FBSystem().Scene.Evaluate() # key mover if trans_x_button.State: mMover.Translation.GetAnimationNode().Nodes[0].KeyAdd(trans_array[count][0]) if trans_y_button.State: mMover.Translation.GetAnimationNode().Nodes[1].KeyAdd(trans_array[count][1]) if trans_z_button.State: mMover.Translation.GetAnimationNode().Nodes[2].KeyAdd(trans_array[count][2]) if rot_y_button.State: mMover.Rotation.GetAnimationNode().Nodes[0].KeyAdd(0) mMover.Rotation.GetAnimationNode().Nodes[1].KeyAdd(0) mMover.Rotation.GetAnimationNode().Nodes[2].KeyAdd(rot_array[count][2]) count = count + 1 # -------------------------------------------------------- # Gimble Killer filter rotation curve to avoid Euler problems # -------------------------------------------------------- if rot_y_button.State and rot_gimble_button.State: # get list of keys before filter(use rot z) curr_key_times = [] keys = mMover.Rotation.GetAnimationNode().Nodes[2].FCurve.Keys for key in keys: curr_key_times.append(key.Time.Get()) #---------------------------------------------------------------- # apply gimble killer lFilter = FBFilterManager().CreateFilter( 'Gimble Killer' ) if lFilter: lFilter.Apply( mMover.Rotation.GetAnimationNode(), True ) del(lFilter) #---------------------------------------------------------------- # get list of keys after filter(use rot z) new_key_times = [] keys = mMover.Rotation.GetAnimationNode().Nodes[2].FCurve.Keys for key in keys: new_key_times.append(key.Time.Get()) #---------------------------------------------------------------- # go through before and after lists and remove new keys added by filter i = 0 for loop in range(len(new_key_times)): if new_key_times[i] != curr_key_times[i]: # we have a mismatch - delete new keys mMover.Rotation.GetAnimationNode().Nodes[0].FCurve.KeyRemove(i) mMover.Rotation.GetAnimationNode().Nodes[1].FCurve.KeyRemove(i) mMover.Rotation.GetAnimationNode().Nodes[2].FCurve.KeyRemove(i) # remove from new list and don't iterate counter del new_key_times[i] else: # fine, existing key from before filter - just iterate counter i = i + 1 # -------------------------------------------------------- # leave user back at frame when script began and refresh # need to go back a frame to force proper update - ??? # -------------------------------------------------------- FBPlayerControl().Goto(FBTime(0,0,0,current_frame-1)) FBSystem().Scene.Evaluate() FBPlayerControl().Goto(FBTime(0,0,0,current_frame)) FBSystem().Scene.Evaluate() #################################################################### # UI #################################################################### def PopulateTool(t): # ui variables for easier modification ui_border_x = 10 ui_border_y = 10 ui_border_width = 140 ui_border_height = 100 ui_column_a = 20 ui_column_b = 60 ui_column_c = 100 ui_row_offset = 10 ui_button_width = 40 #Mover Selection---------------------------------------------- #Drop Down x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(25,FBAttachType.kFBAttachNone,"") t.AddRegion("dropdown","dropdown", x, y, w, h) t.list = FBList() t.SetControl("dropdown", t.list) setupPropertyList(t) #Translation-------------------------------------------------- ui_row_offset += 40 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height,FBAttachType.kFBAttachNone,"") t.AddRegion("trans_border","Translation", x, y, w, h) t.SetBorder("trans_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset += 10 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"region1") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("region1","region1", x, y, w, h) t.SetControl("region1", trans_x_button) trans_x_button.Visible = True trans_x_button.ReadOnly = False trans_x_button.Enabled = True trans_x_button.Hint = "" trans_x_button.Caption = "X" trans_x_button.State = 1 trans_x_button.Style = FBButtonStyle.kFBCheckbox trans_x_button.Justify = FBTextJustify.kFBTextJustifyLeft trans_x_button.Look = FBButtonLook.kFBLookNormal trans_x_button.OnClick.Add(transButtonCallback) x = FBAddRegionParam(ui_column_b,FBAttachType.kFBAttachNone,"region1") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("region2","region2", x, y, w, h) t.SetControl("region2", trans_y_button) trans_y_button.Visible = True trans_y_button.ReadOnly = False trans_y_button.Enabled = True trans_y_button.Hint = "" trans_y_button.Caption = "Y" trans_y_button.State = 1 trans_y_button.Style = FBButtonStyle.kFBCheckbox trans_y_button.Justify = FBTextJustify.kFBTextJustifyLeft trans_y_button.Look = FBButtonLook.kFBLookNormal trans_y_button.OnClick.Add(transButtonCallback) x = FBAddRegionParam(ui_column_c,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("region3","region3", x, y, w, h) t.SetControl("region3", trans_z_button) trans_z_button.Visible = True trans_z_button.ReadOnly = False trans_z_button.Enabled = True trans_z_button.Hint = "" trans_z_button.Caption = "Up" trans_z_button.State = 0 trans_z_button.Style = FBButtonStyle.kFBCheckbox trans_z_button.Justify = FBTextJustify.kFBTextJustifyLeft trans_z_button.Look = FBButtonLook.kFBLookNormal trans_z_button.OnClick.Add(transButtonCallback) ui_row_offset = ui_row_offset + 40 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("region4","region4", x, y, w, h) t.SetControl("region4", trans_offset_button) trans_offset_button.Visible = True trans_offset_button.ReadOnly = False trans_offset_button.Enabled = True trans_offset_button.Hint = "Maintain initial offset on Mover?" trans_offset_button.Caption = "Keep Offset?" trans_offset_button.State = 0 trans_offset_button.Style = FBButtonStyle.kFBCheckbox trans_offset_button.Justify = FBTextJustify.kFBTextJustifyLeft trans_offset_button.Look = FBButtonLook.kFBLookNormal #Rotation-------------------------------------------------- ui_row_offset = ui_row_offset + 60 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height+30,FBAttachType.kFBAttachNone,"") t.AddRegion("rot_border","Rotation", x, y, w, h) t.SetBorder("rot_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset = ui_row_offset + 10 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"rot_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("rot_region","rot_region", x, y, w, h) t.SetControl("rot_region", rot_y_button) rot_y_button.Visible = True rot_y_button.ReadOnly = False rot_y_button.Enabled = True rot_y_button.Hint = "" rot_y_button.Caption = "Up" rot_y_button.State = 0 rot_y_button.Style = FBButtonStyle.kFBCheckbox rot_y_button.Justify = FBTextJustify.kFBTextJustifyLeft rot_y_button.Look = FBButtonLook.kFBLookNormal rot_y_button.OnClick.Add(rotButtonCallback) ui_row_offset = ui_row_offset + 40 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"rot_offset_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("rot_offset_region","rot_offset_region", x, y, w, h) t.SetControl("rot_offset_region", rot_offset_button) rot_offset_button.Visible = True rot_offset_button.ReadOnly = False rot_offset_button.Enabled = False rot_offset_button.Hint = "Maintain initial offset on Mover?" rot_offset_button.Caption = "Keep Offset?" rot_offset_button.State = 1 rot_offset_button.Style = FBButtonStyle.kFBCheckbox rot_offset_button.Justify = FBTextJustify.kFBTextJustifyLeft rot_offset_button.Look = FBButtonLook.kFBLookNormal ui_row_offset = ui_row_offset + 40 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"rot_gimble_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("rot_gimble_region","rot_gimble_region", x, y, w, h) t.SetControl("rot_gimble_region", rot_gimble_button) rot_gimble_button.Visible = True rot_gimble_button.ReadOnly = False rot_gimble_button.Enabled = False rot_gimble_button.Hint = "Apply Gimble Killer filter on rotation?" rot_gimble_button.Caption = "Gimble Filter?" rot_gimble_button.State = 1 rot_gimble_button.Style = FBButtonStyle.kFBCheckbox rot_gimble_button.Justify = FBTextJustify.kFBTextJustifyLeft rot_gimble_button.Look = FBButtonLook.kFBLookNormal rot_gimble_button.OnClick.Add(rotButtonCallback) #Frames-------------------------------------------------- ui_row_offset = ui_row_offset + 50 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height,FBAttachType.kFBAttachNone,"") t.AddRegion("frame_border","Frames", x, y, w, h) t.SetBorder("frame_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset = ui_row_offset + 10 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"frame_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("frame_region","frame_region", x, y, w, h) t.SetControl("frame_region", frame_current_button) frame_current_button.Visible = True frame_current_button.ReadOnly = False frame_current_button.Enabled = True frame_current_button.Hint = "" frame_current_button.Caption = "Current" frame_current_button.State = 1 frame_current_button.Style = FBButtonStyle.kFBRadioButton frame_current_button.Justify = FBTextJustify.kFBTextJustifyLeft frame_current_button.Look = FBButtonLook.kFBLookNormal ui_row_offset = ui_row_offset + 30 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"frame_all_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("frame_all_region","frame_all_region", x, y, w, h) t.SetControl("frame_all_region", frame_all_button) frame_all_button.Visible = True frame_all_button.ReadOnly = False frame_all_button.Enabled = True frame_all_button.Hint = "" frame_all_button.Caption = "All" frame_all_button.State = 0 frame_all_button.Style = FBButtonStyle.kFBRadioButton frame_all_button.Justify = FBTextJustify.kFBTextJustifyLeft frame_all_button.Look = FBButtonLook.kFBLookNormal #Takes-------------------------------------------------- ui_row_offset = ui_row_offset + 70 #Border x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachNone,"") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_border_height+20,FBAttachType.kFBAttachNone,"") t.AddRegion("take_border","Takes", x, y, w, h) t.SetBorder("take_border",FBBorderStyle.kFBEmbossBorder,True,True,2,0,90.0,0) ui_row_offset = ui_row_offset + 10 #Buttons x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"take_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("take_region","take_region", x, y, w, h) t.SetControl("take_region", take_current_button) take_current_button.Visible = True take_current_button.ReadOnly = False take_current_button.Enabled = True take_current_button.Hint = "" take_current_button.Caption = "Current" take_current_button.State = 1 take_current_button.Style = FBButtonStyle.kFBRadioButton take_current_button.Justify = FBTextJustify.kFBTextJustifyLeft take_current_button.Look = FBButtonLook.kFBLookNormal ui_row_offset = ui_row_offset + 30 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"take_selected_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("take_selected_region","take_selected_region", x, y, w, h) t.SetControl("take_selected_region", take_selected_button) take_selected_button.Visible = True take_selected_button.ReadOnly = False take_selected_button.Enabled = True take_selected_button.Hint = "" take_selected_button.Caption = "Selected" take_selected_button.State = 0 take_selected_button.Style = FBButtonStyle.kFBRadioButton take_selected_button.Justify = FBTextJustify.kFBTextJustifyLeft take_selected_button.Look = FBButtonLook.kFBLookNormal ui_row_offset = ui_row_offset + 30 x = FBAddRegionParam(ui_column_a,FBAttachType.kFBAttachTop,"take_all_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(120,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(ui_button_width,FBAttachType.kFBAttachNone,"") t.AddRegion("take_all_region","take_all_region", x, y, w, h) t.SetControl("take_all_region", take_all_button) take_all_button.Visible = True take_all_button.ReadOnly = False take_all_button.Enabled = True take_all_button.Hint = "" take_all_button.Caption = "All" take_all_button.State = 0 take_all_button.Style = FBButtonStyle.kFBRadioButton take_all_button.Justify = FBTextJustify.kFBTextJustifyLeft take_all_button.Look = FBButtonLook.kFBLookNormal #Go Button-------------------------------------------------- ui_row_offset = ui_row_offset + 65 x = FBAddRegionParam(ui_border_x,FBAttachType.kFBAttachTop,"go_button_region") y = FBAddRegionParam(ui_row_offset,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(ui_border_width,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(30,FBAttachType.kFBAttachNone,"") t.AddRegion("go_button_region","go_button_region", x, y, w, h) t.SetControl("go_button_region", go_button) go_button.Visible = True go_button.ReadOnly = False go_button.Enabled = True go_button.Hint = "" go_button.Caption = "Go" go_button.State = 0 go_button.Style = FBButtonStyle.kFBPushButton go_button.Justify = FBTextJustify.kFBTextJustifyCenter go_button.Look = FBButtonLook.kFBLookNormal go_button.OnClick.Add(goButtonCallBack) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Update Mover") t.StartSizeX = 175 t.StartSizeY = 620 PopulateTool(t) ShowTool(t) CreateTool()‰PNG #################################################################### # Frames the zoom bar around the current time #################################################################### from pyfbsdk import * from pyfbsdk_additions import * #################################################################### # UI callbacks #################################################################### def sliderUpdated(control, event): current_start = FBSystem().CurrentTake.LocalTimeSpan.GetStart().GetFrame() current_end = FBSystem().CurrentTake.LocalTimeSpan.GetStop().GetFrame() current_frame = FBSystem().LocalTime.GetFrame() total_frame_count = current_end - current_start # set zoom bars to be 10% (of total frames) either side of the current time new_zoom_start = current_frame - (control.Value * control.Value * total_frame_count) new_zoom_stop = current_frame + (control.Value * control.Value * total_frame_count) # set values FBPlayerControl().ZoomWindowStart = (FBTime(0,0,0,int(new_zoom_start))) FBPlayerControl().ZoomWindowStop = (FBTime(0,0,0,int(new_zoom_stop))) #################################################################### # UI definition #################################################################### def PopulateTool(t): #Slider x = FBAddRegionParam(10,FBAttachType.kFBAttachTop,"") y = FBAddRegionParam(10,FBAttachType.kFBAttachNone,"") w = FBAddRegionParam(240,FBAttachType.kFBAttachNone,"") h = FBAddRegionParam(15,FBAttachType.kFBAttachNone,"") t.AddRegion("slider","slider", x, y, w, h) t.slider = FBSlider() t.slider.Value = 1.0 t.slider.Orientation = FBOrientation.kFBHorizontal t.slider.OnTransaction.Add(sliderUpdated) t.SetControl("slider", t.slider) #################################################################### # Main #################################################################### def CreateTool(): global t t = FBCreateUniqueTool("Zoom Timebar") t.StartSizeX = 275 t.StartSizeY = 70 PopulateTool(t) ShowTool(t) CreateTool() 0.5453 2.2663 0.5