Files
gtav-src/tools_ng/techart/dcc/motionbuilder2014/python/RS/Tools/MakeIVRTrack.py
T
2025-09-29 00:52:08 +02:00

225 lines
8.6 KiB
Python
Executable File

"""
Description:
Integrates IVR Tracks into Motion Builder
Authors:
Kat Bodey <kat.bodey@rockstarnorth.com>
David Vega <david.vega@rockstargames.com>
"""
import os
import struct
import pyfbsdk as mobu
from RS import Globals
from RS.Utils import Math
from RS.Utils import Creation
class IVRFile(object):
""" Generates an IVR Track in the scene based on the contents of an ivr file generated from RAGE """
def __init__(self, filepath=""):
"""
Constructor
Arguments:
filepath (string): path to ivr file
"""
self.FilePath = filepath
self.TimeInRecordingArray = []
self.SpeedArray = []
self.MatrixAArray = []
self.MatrixBArray = []
self.SteerAngleArray = []
self.GasArray = []
self.BrakeArray = []
self.HandBrakeArray = []
self.CoordsArray = []
self.ValidData = False
if os.path.exists(filepath):
self.ValidData = self.ParseIVRFile()
def ParseIVRFile(self):
""" Read the contents of the IVR File """
lIvrFile = open(self.FilePath, 'rb')
lIvrData = lIvrFile.read()
lFrames = len(lIvrData) / 32
for frame in xrange(lFrames):
# Convert the binary data to usable strings
bits = frame * 32
self.TimeInRecordingArray.append(struct.unpack('L', lIvrData[0 + bits:4 + bits])[0])
self.SpeedArray.append(struct.unpack('hhh', lIvrData[4 + bits:10 + bits]))
self.MatrixAArray.append(struct.unpack('bbb', lIvrData[10 + bits:13 + bits]))
self.MatrixBArray.append(struct.unpack('bbb', lIvrData[13 + bits:16 + bits]))
self.SteerAngleArray.append(struct.unpack('b', lIvrData[16 + bits])[0])
self.GasArray.append(struct.unpack('b', lIvrData[17 + bits])[0])
self.BrakeArray.append(struct.unpack('b', lIvrData[18 + bits])[0])
self.HandBrakeArray.append(struct.unpack('b', lIvrData[19 + bits])[0])
self.CoordsArray.append(struct.unpack('fff', lIvrData[20 + bits:32 + bits]))
return True
def GenerateComponents(self):
""" Create the mobu.FBComponents that make up the IVR Track and set them up properly"""
mobu.FBSystem().CurrentTake.SetCurrentLayer(0)
lNullA = mobu.FBModelNull("Matrix_A")
lNullB = mobu.FBModelNull("Translate_Null")
rootNull = mobu.FBModelNull("IVR_Null")
for vectorA, vectorB, position, time in \
zip(self.MatrixAArray, self.MatrixBArray, self.CoordsArray, self.TimeInRecordingArray):
# Get vectors that represent the rotation
# We add 0 to the vectors because MB uses 4x4 matricies and the vectors that the
# GetOrthonormalMatrixVectors method return only contain x,y, and z
rotationVectors = [vector + [0] for vector in
Math.GetOrthonormalMatrixVectors(vectorA, vectorB)]
transformVector = list(position) + [1]
lMatrix = mobu.FBMatrix()
lMatrix.Set(rotationVectors + [transformVector])
lNullA.SetMatrix(lMatrix)
time = mobu.FBTime(0, 0, 0, int(time / 33.0))
for translationRotation in ["Translation", "Rotation"]:
translationRotationNode = getattr(lNullA, translationRotation)
translationRotationNode.SetAnimated(True)
animationNode = translationRotationNode.GetAnimationNode()
for node, data in zip(animationNode.Nodes, translationRotationNode.Data):
node.FCurve.KeyAdd(time, data * (100 ** (translationRotation == "Translation")))
mobu.FBSystem().Scene.Evaluate()
path = self.MakeSpline()
path.Parent = lNullB
self.AnimateVehicleAttributes().Parent = lNullB
lNullB.Parent = rootNull
lNullA.Parent = lNullB
lNullB.Rotation.Data = mobu.FBVector3d(-90, 0, 0)
self.SetupMatrixNull(lNullA)
return lNullB
def MakeSpline(self):
""" Generate the 3D Curve Path """
PathName = os.path.basename(self.FilePath)
return Creation.CreatePath(PathName,
*[[coordinate[0] * 100, coordinate[1] * 100, coordinate[2] * 100, 0]
for coordinate in self.CoordsArray])
def AnimateVehicleAttributes(self):
""" Creates Vehicle Animation Properties """
null = mobu.FBCreateObject( "Browsing/Templates/Elements", "Null", "CarProperties" )
for propertyName in ['Speed', 'SteerAngle', 'Gas', 'Brake', 'HandBrake']:
# Create Property and set a Key on it
isSpeed = propertyName == 'Speed'
newProperty = null.PropertyCreate(propertyName,
[mobu.FBPropertyType.kFBPT_double, mobu.FBPropertyType.kFBPT_Vector3D]
[isSpeed],
['Double', 'Vector3D'][isSpeed],
True, True, None)
newProperty.SetAnimated(True)
newProperty.Key()
# In the older method, this loop would iterate over a matrix but not use the values to add keys
# Instead we just use the hard value of 16 as all Motion Builder Matricies are 4 x 4 Matricies
for index in xrange(16):
time = mobu.FBTime(0, 0, 0, index)
animationNode = newProperty.GetAnimationNode()
# The Speed property takes a Vector3D as a value , So we iterate over the xyz values to set them.
# We also switch the y & z values around so the data is represented correctly in Motion Builder
if isSpeed:
for nullIndex, speedIndex in enumerate([0, 2, 1]):
animationNode.Nodes[nullIndex].FCurve.KeyAdd(time, self.SpeedArray[nullIndex][speedIndex])
continue
animationNode.FCurve.KeyAdd(time, getattr(self, "{}Array".format(propertyName))[index])
return null
@staticmethod
def SetupMatrixNull(null):
"""
Setup the Matrix Null so it follows the 3D Curve Path
Arguments:
null (pyfbsdk.FBModelNull): null that travels along the 3D Curve path
Return:
null
"""
translation = null.Translation.GetAnimationNode()
rotation = null.Rotation.GetAnimationNode()
currentTake = mobu.FBSystem().CurrentTake
# push farplane back
for iCamera in Globals.gCameras:
if iCamera.Name == "Producer Perspective":
iCamera.PropertyList.Find("Far Plane").Data = 400000
break
# set end range
lEndRange = translation.Nodes[0].FCurve.Keys[-1].Time.GetFrame()
mobu.FBPlayerControl().LoopStop = mobu.FBTime(0, 0, 0, lEndRange)
# apply gimblekill & resample filters
for each in ['Gimbal Killer', 'Resample']:
lGimbleFilter = mobu.FBFilterManager().CreateFilter('Gimbal Killer')
if each == 'Resample':
lGimbleFilter.Apply(translation, False)
lGimbleFilter.Apply(rotation, False)
lGimbleFilter.FBDelete()
# re-orientate matrix null
lCurrentLayer = currentTake.GetCurrentLayer()
lLayer1 = lCurrentLayer + 1
currentTake.SetCurrentLayer(lLayer1)
[rotation.Nodes[1].FCurve.KeyAdd(mobu.FBTime(0, 0, 0), value) for value in [0.0, 180.0]]
rotation.Nodes[1].Selected = True
currentTake.PlotTakeOnSelectedProperties(mobu.FBTime(0, 0, 0, 1))
mobu.FBSystem().Scene.Evaluate()
return null
@staticmethod
def SceneAdjustment(null, pX, pY, pZ, propertyType="Translation"):
"""
Move scene to user coordinates or orientation
Arguments:
null (pyfbsdk.FBModelNull): null that travels along the 3D Curve path
pX (int/float): translate or rotate x value
pY (int/float): translate or rotate y value
pZ (int/float): translate or rotate z value
type (string): whether the values should be applied as translation or rotation
"""
if null:
setattr(null, propertyType, mobu.FBVector3d(pX, pY, pZ))
else:
mobu.FBMessageBox("Warning", "No Null Selected", "OK")