2621 lines
128 KiB
Python
Executable File
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()
|