Files
2025-09-29 00:52:08 +02:00

2621 lines
128 KiB
Python
Executable File

version = "2.02"
from pyfbsdk import *
import pythonidelib
import RS.Tools.UI
from PySide import QtGui, QtCore
import sys
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
import time
#mainUIFile = "X:/wildwest/script/motionbuildersandbox/Mike/2014/MotionWarp/UI/MotionWarp.ui"
#mainPYFile = "X:/wildwest/script/motionbuildersandbox/Mike/2014/MotionWarp/UI/MotionWarp.py"
#RS.Tools.UI.CompileUi(mainUIFile, mainPYFile)
sys.path.append("X:/wildwest/script/motionbuildersandbox/Mike/2014/MotionWarp/")
import UI
reload(UI)
class MotionWarp(RS.Tools.UI.QtMainWindowBase, UI.MotionWarp.Ui_MotionWarp):
def __init__(self):
RS.Tools.UI.QtMainWindowBase.__init__(self, None, title='Motion Warp ' + version)
# run ui from designer file
self.setupUi(self)
# TEST FOR IMAGE OVERLAY SHOWING ANGLE CHOSEN
# self.zonalStartRotationImage = QtGui.QLabel(self.zonalStartCanvas)
# self.zonalStartRotationImage.setAlignment(QtCore.Qt.AlignCenter)
# self.zonalStartRotationImage.setObjectName("zonalStartRotationImage")
# self.zonalStartRotationImage.setGeometry(0, 0, 168, 168)
# self.rotationImage = QtGui.QPixmap("X:/wildwest/script/motionbuildersandbox/Arran/Proto/MotionWarp/UI/Angle.png")
# self.zonalStartRotationImage.setPixmap(self.rotationImage)
# self.verticalLayout_13.addWidget(self.zonalStartRotationImage)
# diag = (self.zonalStartRotationImage.width()**2 + self.zonalStartRotationImage.height()**2)**0.5
# self.zonalStartRotationImage.setMinimumSize(diag, diag)
# self.zonalStartRotationImage.setAlignment(QtCore.Qt.AlignCenter)
self.windowSizeY = 350
self.windowSizeX = 443
# def rotateImage():
# t.r = QtGui.QTransform()
# t.updatedNumBox = t.zonalStart_offsetAngleNumBox.value()
# t.r.rotate(t.updatedNumBox)
# t.rotated_pixmap = t.rotationImage.transformed(t.r)
# t.zonalStartRotationImage.setPixmap(t.rotated_pixmap)
def setupFunctionality(t):
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 = []
for obj in models:
t.selected_obj.append(obj)
t.selected_obj_name.append(obj.Name)
t.numOfObjects = len(models)
if t.numOfObjects == 1:
t.turn_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.spline_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStart_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStop_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalAdvanced_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
else:
t.turn_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.spline_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStart_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStop_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalAdvanced_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.pinned = False
t.turn_applyMotionWarpBtn.clicked.connect(goButtonCallBack)
t.spline_applyMotionWarpBtn.clicked.connect(goButtonCallBack)
t.zonalStart_applyMotionWarpBtn.clicked.connect(goButtonCallBack)
t.zonalStop_applyMotionWarpBtn.clicked.connect(goButtonCallBack)
t.zonalAdvanced_applyMotionWarpBtn.clicked.connect(goButtonCallBack)
t.zonalStart_subtract45Btn.clicked.connect(sub45_start)
t.zonalStart_add45Btn.clicked.connect(add45_start)
t.zonalStop_subtract45Btn.clicked.connect(sub45_stop)
t.zonalStop_add45Btn.clicked.connect(add45_stop)
t.pinned = False
if t.numOfObjects == 1:
t.turn_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.spline_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStart_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStop_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalAdvanced_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
else:
t.turn_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.spline_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStart_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStop_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalAdvanced_deleteMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Delete MotionWarp from " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.turn_deleteMotionWarpBtn.clicked.connect(deleteButtonCallBack)
t.spline_deleteMotionWarpBtn.clicked.connect(deleteButtonCallBack)
t.zonalStart_deleteMotionWarpBtn.clicked.connect(deleteButtonCallBack)
t.zonalStop_deleteMotionWarpBtn.clicked.connect(deleteButtonCallBack)
t.zonalAdvanced_deleteMotionWarpBtn.clicked.connect(deleteButtonCallBack)
setupTurnParameterList(t)
t.zonalStart_keyHelperBtn.clicked.connect(zonalKeyHelperCallBack)
t.zonalStop_keyHelperBtn.clicked.connect(zonalKeyHelperCallBack)
t.zonalAdvanced_keyHelperBtn.clicked.connect(zonalKeyHelperCallBack)
t.zonalStart_keyHelperBtn.setEnabled(False)
t.zonalStop_keyHelperBtn.setEnabled(False)
t.zonalAdvanced_keyHelperBtn.setEnabled(False)
setupPlotModeList(t)
t.tabBox.currentChanged.connect(tabBoxChanged)
t.advancedSettings = False
t.advancedSettingsWidget.hide()
t.advancedSettingsBtn.clicked.connect(toggleAdvancedSettings)
t.resize(t.windowSizeX, t.windowSizeY)
t.setMinimumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
t.setMaximumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
t.statusbar.hide()
t.progressBar.hide()
t.progress = 0
t.progressBar.setValue(t.progress)
#t.zonalStart_offsetAngleNumBox.valueChanged.connect(rotateImage)
t.show()
def toggleAdvancedSettings():
if t.advancedSettings == False:
t.advancedSettings = True
t.advancedSettingsWidget.show()
t.windowSizeY += 56
t.resize(t.windowSizeX, t.windowSizeY)
t.setMinimumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
t.setMaximumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
elif t.advancedSettings == True:
t.advancedSettings = False
t.advancedSettingsWidget.hide()
t.windowSizeY -= 56
t.resize(t.windowSizeX, t.windowSizeY)
t.setMinimumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
t.setMaximumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
def sub45_start():
currentValue = t.zonalStart_offsetAngleNumBox.value()
currentValue -= 45
t.zonalStart_offsetAngleNumBox.setValue(currentValue)
def add45_start():
currentValue = t.zonalStart_offsetAngleNumBox.value()
currentValue += 45
t.zonalStart_offsetAngleNumBox.setValue(currentValue)
def sub45_stop():
currentValue = t.zonalStop_offsetAngleNumBox.value()
currentValue -= 45
t.zonalStop_offsetAngleNumBox.setValue(currentValue)
def add45_stop():
currentValue = t.zonalStop_offsetAngleNumBox.value()
currentValue += 45
t.zonalStop_offsetAngleNumBox.setValue(currentValue)
def plotToSkeleton( character ):
character.PlotAnimation( FBCharacterPlotWhere.kFBCharacterPlotOnSkeleton, plotOptions() )
def tabBoxChanged():
if t.tabBox.currentIndex() == 0:
t.advanced_dimensionComboBox.setEnabled(False)
else:
t.advanced_dimensionComboBox.setEnabled(True)
def calcAV(AvVel, currentDropDownIndex):
multiplier = float(t.angularVelMultiplier[currentDropDownIndex])
y = float(t.y_addition[currentDropDownIndex])
if t.turn_autoAVLargeTurnRadio.isChecked():
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(t):
t.turn_AutoAVComboBox.clear()
t.turn_AutoAVComboBox.addItems(t.angularVelListNames)
def setupPlotModeList(t):
t.plotSettings_comboBox.clear()
t.plotSettings_comboBox.addItem("Plot Character")
t.plotSettings_comboBox.addItem("Plot Selected Nodes")
def clamp(n, minn, maxn):
return max(min(maxn, n), minn)
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']
if t.tabBox.currentIndex() == 2:
offset_angle = t.zonalStart_offsetAngleNumBox.value()
elif t.tabBox.currentIndex() == 3:
offset_angle = t.zonalStop_offsetAngleNumBox
elif t.tabBox.currentIndex() == 4:
offset_angle = t.zonalAdvanced_offsetAngleNumBox01
else:
offset_angle = 0
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()
if t.numOfObjects == 1:
t.turn_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.spline_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStart_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStop_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalAdvanced_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp", "Apply MotionWarp to '" +
t.selected_obj_name[0] + "'",
None, QtGui.QApplication.UnicodeUTF8))
else:
t.turn_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.spline_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStart_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStop_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalAdvanced_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Apply MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.turn_deleteMotionWarpBtn.setEnabled(False)
t.spline_deleteMotionWarpBtn.setEnabled(False)
t.zonalStart_deleteMotionWarpBtn.setEnabled(False)
t.zonalStop_deleteMotionWarpBtn.setEnabled(False)
t.zonalAdvanced_deleteMotionWarpBtn.setEnabled(False)
t.pinned = False
t.turnTab.setEnabled(True)
t.splineTab.setEnabled(True)
t.zonalStartTab.setEnabled(True)
t.zonalStopTab.setEnabled(True)
t.zonalAdvancedtab.setEnabled(True)
t.manualAVTab.setEnabled(True)
t.manualTurnTab.setEnabled(True)
t.autoAVTab.setEnabled(True)
t.zonalStart_keyHelperBtn.setEnabled(False)
t.zonalStop_keyHelperBtn.setEnabled(False)
t.zonalAdvanced_keyHelperBtn.setEnabled(False)
####################################################################
# UI callbacks
####################################################################
def sliderUpdated(control, event):
t.status.Caption = str(int(control.Value * 10) + 2)
def deleteButtonCallBack():
if t.tabBox.currentIndex() == 2 or t.tabBox.currentIndex() == 3:
if hasattr(t, 'zonal'):
if not t.zonal.TypeInfo == 203:
closeZonalHelperWindow()
cleanupAndReset()
def zonalKeyHelperCallBack():
t.zonal = FBCreateUniqueTool("Zonal Key Helper")
PopulateZonalUI(t.zonal)
def goButtonCallBack():
# Disable all expressions for the duration of this event
t.progressBar.show()
t.windowSizeY += 30
t.resize(t.windowSizeX, t.windowSizeY)
t.setMinimumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
t.setMaximumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
with SceneExpressionsDisabled():
# get time span
global take_start_frame
global take_end_frame
global current_frame
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()
t.progress = 0
t.progressBar.setValue(t.progress)
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)
t.progress += ((20 / t.numOfObjects) * i)
t.progressBar.setValue(t.progress)
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):
t.progress += (20 / len(range(t.take_start_frame, t.take_end_frame)) * i)
t.progressBar.setValue(t.progress)
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_dimensionComboBox.currentIndex() == 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.tabBox.currentIndex() == 0:
curve_steps = 2
else:
curve_steps = int(t.spline_splinePointNumSlider.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_dimensionComboBox.currentIndex() == 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.tabBox.currentIndex() == 0):
# 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_dimensionComboBox.currentIndex() == 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.tabBox.currentIndex() == 0:
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.tabBox.currentIndex() == 0:
###################################################################################
# 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)
################################################################################################
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.turnTabBox.currentIndex() == 1:
radius = t.manualTurn_RadiusNumBox.value()
if t.manualTurn_counterClockwiseRadio.isChecked():
direction = -1
else:
direction = 1
elif t.turnTabBox.currentIndex() == 0:
if t.autoAV_counterClockwiseRadio.isChecked():
direction = -1
else:
direction = 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.turn_AutoAVComboBox.currentIndex()
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.turn_autoAVLargeTurnRadio.isChecked():
# 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:
if t.manualAV_counterClockwiseRadio.isChecked():
direction = -1
else:
direction = 1
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.manualAV_AVNumBox.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) + " % "
t.progress += (20 / t.numOfObjects * j)
t.progressBar.setValue(t.progress)
###################################################################################
# Zonal Start Mode
#
# if mode selected rotate spline points around at angle but maintain start pose
###################################################################################
if t.tabBox.currentIndex() == 2:
print "Zonal Start Mode!"
# note. Reverse the angle to match convention
rotationAngleAV3 = FBVector3d(0.0, -t.zonalStart_offsetAngleNumBox.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)
t.progress += (20 / t.numOfObjects * i)
t.progressBar.setValue(t.progress)
###################################################################################
# Enable Zonal Helper UI
###################################################################################
t.zonalStart_keyHelperBtn.setEnabled(True)
###################################################################################
# Zonal Stop Mode
#
# if mode selected rotate spline points around at angle but maintain start pose
###################################################################################
if t.tabBox.currentIndex() == 3:
print "Zonal Stop Mode!"
# note. Reverse the angle to match convention
rotationAngleAV3 = FBVector3d(0.0, -t.zonalStop_offsetAngleNumBox.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.zonalStop_keyHelperBtn.setEnabled(True)
t.progress += (20 / t.numOfObjects * i)
t.progressBar.setValue(t.progress)
###################################################################################
# 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.tabBox.currentIndex() == 4:
FBSystem().Scene.Evaluate()
print "Zonal Advanced Mode!"
# note. Reverse the angle to match convention
rotationAngleAV3 = FBVector3d(0.0, -t.zonalAdvanced_offsetAngleNumBox01.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)
t.progress += (20 / t.numOfObjects * i)
t.progressBar.setValue(t.progress)
####################################################################################
# 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.zonalAdvanced_offsetAngleNumBox02.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.zonalAdvanced_offsetAngleNumBox01.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.zonalAdvanced_keyHelperBtn.setEnabled(True)
###################################################################################
# update UI
###################################################################################
# turn expressions back on
# ToggleDevices()
t.turn_deleteMotionWarpBtn.setEnabled(True)
t.spline_deleteMotionWarpBtn.setEnabled(True)
t.zonalStart_deleteMotionWarpBtn.setEnabled(True)
t.zonalStop_deleteMotionWarpBtn.setEnabled(True)
t.zonalAdvanced_deleteMotionWarpBtn.setEnabled(True)
# prevent user from altering Mode
if t.tabBox.currentIndex() == 0:
if t.turnTabBox.currentIndex() == 1:
t.manualAVTab.setEnabled(False)
t.autoAVTab.setEnabled(False)
elif t.turnTabBox.currentIndex() == 2:
t.manualTurnTab.setEnabled(False)
t.autoAVTab.setEnabled(False)
else:
t.manualTurnTab.setEnabled(False)
t.manualAVTab.setEnabled(False)
t.splineTab.setEnabled(False)
t.zonalStartTab.setEnabled(False)
t.zonalStopTab.setEnabled(False)
t.zonalAdvancedtab.setEnabled(False)
elif t.tabBox.currentIndex() == 1:
t.turnTab.setEnabled(False)
t.zonalStartTab.setEnabled(False)
t.zonalStopTab.setEnabled(False)
t.zonalAdvancedtab.setEnabled(False)
elif t.tabBox.currentIndex() == 2:
t.turnTab.setEnabled(False)
t.splineTab.setEnabled(False)
t.zonalStopTab.setEnabled(False)
t.zonalAdvancedtab.setEnabled(False)
elif t.tabBox.currentIndex() == 3:
t.turnTab.setEnabled(False)
t.splineTab.setEnabled(False)
t.zonalStartTab.setEnabled(False)
t.zonalAdvancedtab.setEnabled(False)
else:
t.turnTab.setEnabled(False)
t.splineTab.setEnabled(False)
t.zonalStartTab.setEnabled(False)
t.zonalStopTab.setEnabled(False)
if t.numOfObjects == 1:
t.turn_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.spline_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStart_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStop_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalAdvanced_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp to " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
else:
t.turn_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp on " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.spline_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp on " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStart_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp on " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalStop_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp on " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
t.zonalAdvanced_applyMotionWarpBtn.setText(QtGui.QApplication.translate("MotionWarp",
"Plot MotionWarp on " + str(
t.numOfObjects) + " objects",
None, QtGui.QApplication.UnicodeUTF8))
###################################################################################
# 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.plotSettings_comboBox.currentIndex() == 0:
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.tabBox.currentIndex() == 1 or t.tabBox.currentIndex() == 2 or t.tabBox.currentIndex() == 3:
storeZonalValues()
if hasattr(t, 'zonal'):
closeZonalHelperWindow()
# and we're done...
cleanupAndReset()
hideProgressBar()
def hideProgressBar():
t.progress = 100
t.progressBar.setValue(t.progress)
t.progressBar.setStyleSheet("QProgressBar::chunk{background-color:rgb(255, 171, 0);}")
time.sleep(0.5)
t.progressBar.hide()
t.windowSizeY -= 30
t.resize(t.windowSizeX, t.windowSizeY)
t.setMinimumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
t.setMaximumSize(QtCore.QSize(t.windowSizeX, t.windowSizeY))
t.progress = 0
t.progressBar.setValue(t.progress)
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])
####################################################################
# 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.tabBox.currentIndex() == 2 or t.tabBox.currentIndex() == 3:
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)
def Run():
global t
t = MotionWarp()
t.setWindowTitle('Motion Warp ' + version)
setupFunctionality(t)
MotionWarp = Run()