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

283 lines
11 KiB
Python
Executable File

"""
Description:
Setups the Moving Cutscene hiearchy and connections
Author:
David Vega <david.vega@rockstargames.com>
"""
import pyfbsdk as mobu
import RS.Utils.Scene
from RS import Globals
from RS.Utils import Creation
from RS.Utils.Scene import Constraint
from RS.Core.Animation import Lib
def SetupMovingCutscene(driver, transferMotion=True, **drivers):
"""
Creates the moving cutscene setup based on the drivers passed
Arguments:
driver (pyfbsdk.FBModel): the model that drives the moving cutscene hiearchy, usually it is the Matrix_A model
CAMERAS: list[FBCamera, etc.]; list of cameras to parent under the CAMERA FBModelNull
transferMotion (boolean): Transfer the motion from the driver's children to the driver.
Keyword Arguments:
**drivers: {string : list[FBModel, etc],
string : {string:list[FBModel, etc.]}} ; any keyword argument is treated as the name of a
null that will be created to hold the list of models that make up the the value of that key value
pair. Key value pairs can have dictionaries to create more nulls. FBModelNulls are created based
on the lists passed and are constrained to the object they go with
Example:
SetupMovingCutscene(<FBModel instance>, CAMERAS=[<FBCamera R*Camera 1>], CHARACTERS=[<FBModelNull Character1>],
PROP={"SUITCASES" : [<FBModelNull Suitcase1>]})
#Generates
MASTER_MOVER
-CAMERAS
-R*Camera 1
-CHARACTERS
-Character 1Null
-PROP
-SUITCASES
-Suitcase1 Null
"""
frame = Globals.System.LocalTime.GetFrame()
Globals.Player.GotoStart()
constraintList = []
folderName = GenerateName(driver, "MOVING_CUTSCENE")
folder = CreateConstraintFolder(folderName)
# Create Master Mover, Zero and Cameras nulls if they aren't in the scene
zeroNull = GetNull(GenerateName(driver, "ZERO"))
masterMover = GetNull(GenerateName(driver, "MASTER_MOVER"))
# Transfer Animation from Driver to the Master Null
Lib.TransferMotion(masterMover, driver)
# Position the Master Mover and Zero Null
Lib.MatchPosition(driver, (masterMover, zeroNull))
# Clear out scaling artifacts from matching matricies
masterMover.PropertyList.Find("Lcl Scaling").Data = mobu.FBVector3d(1, 1, 1)
zeroNull.PropertyList.Find("Lcl Scaling").Data = mobu.FBVector3d(1, 1, 1)
mobu.FBSystem().Scene.Evaluate()
# Create Zero Constraint
zeroConstraintName = GenerateName(driver, "ZERO_CONSTRAINT")
zeroConstraint = filter(lambda constraint: zeroConstraintName in (constraint.Name, constraint.LongName),
Globals.Constraints)
if not zeroConstraint:
zeroConstraint = [Creation.CreateConstraint("Parent/Child", zeroConstraintName,
masterMover, zeroNull, Active=True, Lock=True)]
folder.ConnectSrc(zeroConstraint[0])
# Create Nulls
driverNulls, constraints = CreateConstraintedNulls(driver, folder=folder, transferMotion=transferMotion, **drivers)
constraintList += constraints
# Parent driver nulls
for eachNull in driverNulls:
eachNull.Parent = masterMover
eachNull.Scaling = mobu.FBVector3d(1, 1, 1)
zeroConstraint[0].Active = False
Globals.Scene.Evaluate()
# The master constraint isn't activated until after we create the other constraints
masterMoverConstraintName = GenerateName(driver, "MASTER_MOVER_CONSTRAINT").upper()
constraint = filter(lambda constraint: constraint.LongName == masterMoverConstraintName,
Globals.Constraints)
if not constraint and driver:
constraint = Creation.CreateConstraint("Parent/Child",
masterMoverConstraintName,
driver, masterMover, Active=True)
# The name of the offset attributes are the name of the source component plus Offset T/R
# However this may not always be the case, so we are just going to search if Offset T/R is in the
# name of the property to get more consistent results.
for property in constraint.PropertyList:
for tr in "TR":
if ".Offset {}".format(masterMover.Name, tr) in property.Name:
property.Data = mobu.FBVector3d(0, 0, 0)
folder.ConnectSrc(constraint)
if transferMotion:
for constraint in constraints:
constraint.Active = False
parent, child = constraint.GetSrc(0), constraint.GetDst(1)
# Ensures that the active state of the constraint is updated to False
Globals.Scene.Evaluate()
Lib.TransferMotion(parent, child)
constraint.Active = True
Globals.Player.Goto(mobu.FBTime(0, 0, 0, frame))
def GenerateName(driver, name):
"""
Generates a name with the driver's namespace ,or short name if there is no namespace, as the prefix
Arguments:
driver: FBComponent; object whose name will be used as the prefix
name: string; suffix of the new name
Return:
string
"""
return "{}_{}".format(driver.LongName.split()[0], name)
def CreateConstraintFolder(folderName):
"""
Creates a folder to hold constraints if it doesn't exist
Arguments:
folderName: string; name of the folder to create/return
Return:
FBFolder
"""
folders = filter(lambda folder: folderName in [folder.Name, folder.LongName], mobu.FBSystem().Scene.Folders)
if folders:
folder = folders[0]
else:
folder = Creation.rs_CreateFolder(folderName, "Constraints")
return folder
def GetNull(name):
"""
Find the null that matches the given name, if it doesn't exist it creates it
Arguments:
name: string; name of the null to get
Return:
FBModel or subclass of FBModel
"""
null = mobu.FBFindModelByLabelName(name)
if not null:
null = mobu.FBModelNull(name)
return null
def CreateConstraintedNulls(topParent, folder=None, transferMotion=True, **parentChildren):
"""
Constraints parentChildren to those objects
Keyword Arguments:
parentChildren: {string(Name of Parent FBModel): FBModel(), etc}; Finds the FBModel object whose name matches
the keyword argument used and parent constraints all the FBModels passed in as value. Values for the
keyword arguments can be FBModels(), a list of FBModels or a dictionary.
Example:
CreateConstraintedNulls(PARENT=FBModelNull("Child"),
LIST=[FBModelNull("Child1"), FBModelNull("Child1")],
DICTIONARY={"SUBPARENT" : FBModelNull("GranChild")})
"""
parentNulls = []
constraints = []
# Generate Top Level Folder if no folder is passed
if not folder:
folderName = GenerateName(topParent, "MOVING_CUTSCENE")
folder = CreateConstraintFolder(folderName)
# Iterate over the the driver and driven objects
for parentName, children in parentChildren.iteritems():
createConstraint = True
parentNulls.append(GetNull(parentName))
# Forces the null to be positioned on top of the top most parent
parentNulls[-1].Parent = topParent
parentNulls[-1].PropertyList.Find("Lcl Translation").Data = mobu.FBVector3d(0, 0, 0)
Globals.Scene.Evaluate()
if isinstance(children, dict):
# Get/Create folder for nested parentChildren
driverFolder = CreateConstraintFolder(parentName)
folder.ConnectSrc(driverFolder)
# Create Constraints
children, newConstraints = CreateConstraintedNulls(topParent, driverFolder, transferMotion=transferMotion,
**children)
constraints.extend(newConstraints)
createConstraint = False
elif not isinstance(children, list):
children = [children]
# Remove None values from the children list
children = filter(lambda child: child is not None, children)
for child in children:
# Parent objects instead of constraining them
if not createConstraint:
child.Parent = parentNulls[-1]
continue
# Disable constraints attached to the child object
[setattr(currentConstraint, "Active", False) for currentConstraint in
RS.Utils.Scene.GetIncomingConnections(child, [mobu.FBConstraint])]
# Ensure that the constraints active values update
Globals.Scene.Evaluate()
# Position the parent null at the position of the child component
Lib.MatchPosition(child, [parentNulls[-1]])
# Check if the constraints already exist
constraint = None
constraintName = "{}_CONSTRAINT".format(parentName)
for _constraint in Globals.Scene.Constraints:
if constraintName == _constraint.Name:
constraint = _constraint
break
# Create constraint if it is missing
if constraint is None:
constraint = Constraint.Constraint(Constraint.PARENT_CHILD, "{}_CONSTRAINT".format(parentName),
child, parentNulls[-1], Active=True, Lock=False)
constraints.append(constraint)
# Add constraint to folder
folder.ConnectSrc(constraints[-1])
return parentNulls, constraints
def QuickSetup(driver=None, masterMover=None):
""" Creates a predetermined moving cutscene setup """
if not isinstance(driver, mobu.FBModel):
driver = mobu.FBFindModelByLabelName("Matrix_A")
if not isinstance(masterMover, mobu.FBModel):
masterMover = mobu.FBFindModelByLabelName("MASTER_MOVER")
# if masterMover and driver:
drivers = {"CAMERAS": [each for each in mobu.FBSystem().Scene.Cameras[7:] if "R*" in each.Name],
"CHARACTERS": [], "PROPS": []}
# Get top modelNull for each reference
for each in mobu.FBSystem().Scene.Characters:
name = "{}_Ctrl:Reference".format(each.Name.split(":")[0])
if mobu.FBFindModelByLabelName(name):
drivers["CHARACTERS"].append(mobu.FBFindModelByLabelName(name))
# Setup Moving Cutscene
SetupMovingCutscene(driver, masterMover, **drivers)