13283 lines
508 KiB
Python
Executable File
13283 lines
508 KiB
Python
Executable File
‰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 <jason.hayes@rockstarsandiego.com>
|
|
|
|
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 <Condition> in each file
|
|
##############################################################
|
|
conditionElement = root.find('Condition')
|
|
if conditionElement:
|
|
output = checkForBadConditions(filepath, output, conditionElement)
|
|
|
|
##############################################################
|
|
# search in <Motions> 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 <Motions> in each file for missing motions
|
|
##############################################################
|
|
if isFileAMotionType( root ):
|
|
missing_controller_output = findMissingController(filepath, missing_controller_output, root )
|
|
|
|
##############################################################
|
|
# search in <Motions> 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 <Motions> 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()
|
|
<?xml version="1.0"?>
|
|
<data>
|
|
<type name="Human">
|
|
<multiplier>0.5453</multiplier>
|
|
<offset>2.2663</offset>
|
|
<minRadius>0.5</minRadius>
|
|
</type>
|
|
<!-- <type name="Animal - Medium">
|
|
<multiplier>0.3343</multiplier>
|
|
<offset>2.0042</offset>
|
|
<minRadius>0.5</minRadius>
|
|
</type> -->
|
|
</data>
|
|
|