""" Description: Setups the Moving Cutscene hiearchy and connections Author: David Vega """ 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(, CAMERAS=[], CHARACTERS=[], PROP={"SUITCASES" : []}) #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)