358 lines
14 KiB
Python
Executable File
358 lines
14 KiB
Python
Executable File
"""
|
|
## Written And Maintained By: David Bailey
|
|
## Contributors: Kristine Middlemiss
|
|
## Description: A list of commonly used functions to create scene objects and to simplify and speed
|
|
## up tool production. This also signifies a turning point to minimise the duplication
|
|
## of code in new tools.
|
|
##
|
|
## Rules: Definitions: Prefixed with "rs_"
|
|
## Global Variables: Prefixed with "g"
|
|
## Local Variables: Prefixed with "l"
|
|
## Iteration Variables: Prefixed with "i"
|
|
## Arguments: Prefixed with "p"
|
|
##
|
|
"""
|
|
|
|
from pyfbsdk import *
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
import webbrowser
|
|
|
|
import RS.Globals as glo
|
|
import RS.Config
|
|
|
|
from RS.Utils.Scene.Component import SetProperties
|
|
from RS.Utils.Scene import Constraint
|
|
|
|
###############################################################################################################
|
|
##* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
##
|
|
## Description: The yellow Rockstar Banner that should be used at the top of EVERY tool
|
|
##
|
|
##* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
###############################################################################################################
|
|
def rs_OnEmailClickCallback(pControl, pEvent):
|
|
|
|
if gCc == None:
|
|
os.startfile("mailto:" + gEmailTo + "&Subject= "+ gToolName+ "&Body=" +gToolPath)
|
|
else:
|
|
os.startfile("mailto:" + gEmailTo + "?CC=" + gCc + "&Subject= "+ gToolName+ "&Body=" +gToolPath)
|
|
|
|
def rs_OnInfoClickCallback(pControl, pEvent):
|
|
|
|
if gWikiLink == None:
|
|
FBMessageBox("Information", "Sorry, there is no Wiki page for this UI...YET :)", "Ok")
|
|
else:
|
|
lNew = 2
|
|
webbrowser.open(gWikiLink)
|
|
|
|
def rs_CreateBanner( parentLyt, lToolName, lEmailTo, lToolPath, lCc=None, lWikiLink=None):
|
|
|
|
# HouseKeeping
|
|
global gToolName
|
|
gToolName = lToolName
|
|
|
|
global gEmailTo
|
|
gEmailTo = lEmailTo
|
|
|
|
global gToolPath
|
|
gToolPath = lToolPath
|
|
|
|
global gCc
|
|
gCc = lCc
|
|
|
|
global gWikiLink
|
|
gWikiLink = lWikiLink
|
|
|
|
# Rockstar Logo
|
|
titlex = FBAddRegionParam(0,FBAttachType.kFBAttachLeft,"")
|
|
titley = FBAddRegionParam(0,FBAttachType.kFBAttachTop,"")
|
|
titlew = FBAddRegionParam(30,FBAttachType.kFBAttachNone,None)
|
|
titleh = FBAddRegionParam(30,FBAttachType.kFBAttachNone,None)
|
|
parentLyt.AddRegion("Title","Title", titlex,titley,titlew,titleh)
|
|
|
|
titleImg = FBImageContainer()
|
|
titleImg.Filename = "{0}\\YellowRockstarLogo_30x30.jpg".format( RS.Config.Script.Path.ToolImages )
|
|
parentLyt.SetControl("Title",titleImg)
|
|
|
|
# Spacer
|
|
spacerx = FBAddRegionParam(30,FBAttachType.kFBAttachLeft,"")
|
|
spacery = FBAddRegionParam(0,FBAttachType.kFBAttachTop,"")
|
|
spacerw = FBAddRegionParam(-60,FBAttachType.kFBAttachRight,"")
|
|
spacerh = FBAddRegionParam(30,FBAttachType.kFBAttachNone,None)
|
|
parentLyt.AddRegion("Spacer","Spacer", spacerx,spacery,spacerw,spacerh)
|
|
|
|
spacerImg = FBImageContainer()
|
|
spacerImg.Filename = "{0}\\YellowSpacerBanner.jpg".format( RS.Config.Script.Path.ToolImages )
|
|
parentLyt.SetControl("Spacer",spacerImg)
|
|
|
|
# Send an email about the tool logo
|
|
lEmailX = FBAddRegionParam(-60,FBAttachType.kFBAttachRight,"")
|
|
lEmailY = FBAddRegionParam(0,FBAttachType.kFBAttachTop,"")
|
|
lEmailW = FBAddRegionParam(30,FBAttachType.kFBAttachNone,None)
|
|
lEmailH = FBAddRegionParam(30,FBAttachType.kFBAttachNone,None)
|
|
parentLyt.AddRegion( "lEmail","lEmail", lEmailX, lEmailY, lEmailW, lEmailH )
|
|
|
|
lEmailContainer = FBVisualContainer()
|
|
parentLyt.SetControl( "lEmail", lEmailContainer)
|
|
lEmailContainer.OnChange.Add(rs_OnEmailClickCallback)
|
|
|
|
lEmailImage = "{0}\\email_icon_yellow.tif".format( RS.Config.Script.Path.ToolImages )
|
|
lEmailContainer.Items.append("")
|
|
lEmailContainer.ItemIconSet(0, lEmailImage)
|
|
lEmailContainer.ItemWidth = 27
|
|
lEmailContainer.ItemHeight = 44
|
|
lEmailContainer.ItemWrap = True
|
|
|
|
# Find more information on the wiki about the tool logo
|
|
lInfoX = FBAddRegionParam(-30,FBAttachType.kFBAttachRight,"")
|
|
lInfoY = FBAddRegionParam(0,FBAttachType.kFBAttachTop,"")
|
|
lInfoW = FBAddRegionParam(30,FBAttachType.kFBAttachNone,None)
|
|
lInfoH = FBAddRegionParam(30,FBAttachType.kFBAttachNone,None)
|
|
parentLyt.AddRegion( "lInfo","lInfo", lInfoX, lInfoY, lInfoW, lInfoH )
|
|
|
|
lInfoContainer = FBVisualContainer()
|
|
parentLyt.SetControl( "lInfo", lInfoContainer)
|
|
lInfoContainer.OnChange.Add(rs_OnInfoClickCallback)
|
|
|
|
lInfoImage = "{0}\\information_icon_yellow.tif".format( RS.Config.Script.Path.ToolImages )
|
|
lInfoContainer.Items.append("")
|
|
lInfoContainer.ItemIconSet(0, lInfoImage)
|
|
lInfoContainer.ItemWidth = 27
|
|
lInfoContainer.ItemHeight = 44
|
|
lInfoContainer.ItemWrap = True
|
|
return parentLyt
|
|
|
|
|
|
def rs_CreateFolder(pName, pLocation):
|
|
"""
|
|
Creates a folder with a specific name under the given location
|
|
|
|
Arguments:
|
|
pName (string): name for the folder
|
|
pLocation (string or FBComponent): location to put folder under, accepts strings and pyfbsdk classes that
|
|
inherit FBComponent
|
|
|
|
Return:
|
|
FBFolder
|
|
"""
|
|
FBMethodDictionary = {
|
|
"Actor Faces" : "FBActorFace",
|
|
"Actors" : "FBActor",
|
|
"Audio Clips" : "FBAudioClip",
|
|
"Cameras" : "FBCamera",
|
|
"Character Extensions": "FBCharacterExtension",
|
|
"Character Faces": "FBCharacterFace",
|
|
"Character Poses": "FBCharacterPose",
|
|
"Characters" : "FBCharacter",
|
|
"Constraints 1" : "FBConstraintSolver",
|
|
"Constraints" : "FBConstraintSolver",
|
|
"Control Rigs" : "FBControlSet",
|
|
"Lights" : "FBLight",
|
|
"Materials" : "FBMaterial",
|
|
"Notes" : "FBNote",
|
|
"Poses" : "FBPose",
|
|
"Sets" : "FBSet",
|
|
"Shaders" : ["FBShaderManager", "CreateShader(ZShader)"],
|
|
"Takes" : ["FBTake", "CopyTake"],
|
|
"Textures" : "FBTexture",
|
|
"VideoClips" : "FBVideoClipImage",
|
|
"Videos" : "FBVideoClipImage",
|
|
"FBShader" : ["FBShaderManager", "CreateShader(ZShader)"],
|
|
"FBTake" : ["FBTake", "CopyTake"],
|
|
"FBConstraint" : "FBConstraintSolver",
|
|
"FBConstraintRelation": "FBConstraintSolver"
|
|
}
|
|
|
|
fbtype = None
|
|
|
|
# Check if the location passed is a class from the pyfbsdk and if it is to
|
|
# get the name of that class
|
|
if hasattr(pLocation, "__module__") and pLocation.__module__ == "pyfbsdk":
|
|
fbtype = FBMethodDictionary.get(pLocation.__name__, pLocation.__name__)
|
|
|
|
if FBMethodDictionary.get(pLocation, None) is None and fbtype is None:
|
|
return
|
|
|
|
methodList = FBMethodDictionary.get(pLocation, None) or fbtype
|
|
|
|
if not isinstance(methodList, list):
|
|
methodList = [methodList]
|
|
|
|
method = sys.modules["pyfbsdk"]
|
|
for eachMethod in methodList:
|
|
# Regular Expression to extract arguments from methods
|
|
# It returns a list of all strings between
|
|
arguments = re.findall("(?<=\()[0-9a-z._ ]+(?=\))", eachMethod, re.I)
|
|
|
|
eachMethod = eachMethod.split("(")[0]
|
|
method = getattr(method, eachMethod)
|
|
|
|
if eachMethod == methodList[-1] and not arguments:
|
|
arguments = ["ToBeDeleted"]
|
|
|
|
method = method(*arguments)
|
|
|
|
temporaryModel = method
|
|
folder = FBFolder(pName, temporaryModel)
|
|
temporaryModel.FBDelete()
|
|
|
|
return folder
|
|
|
|
###############################################################################################################
|
|
##* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
##
|
|
## Description: Create Folders and Add Component(s) in, if they exist
|
|
##
|
|
##* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
###############################################################################################################
|
|
|
|
|
|
def rs_CreateFolderAddComponents(pComponent, pComponentStr, name="", excludeByTypes=[]):
|
|
if not name:
|
|
name = pComponentStr
|
|
lFolder = None
|
|
for folder in RS.Globals.Folders:
|
|
if folder.Name == name:
|
|
lFolder = folder
|
|
break
|
|
|
|
if lFolder == None:
|
|
lFolder = rs_CreateFolder(name, pComponentStr)
|
|
|
|
for iComp in filter(None, pComponent):
|
|
if not IsPyfbsdkType(iComp, *excludeByTypes):
|
|
|
|
lFolder.ConnectSrc(iComp)
|
|
return lFolder
|
|
|
|
###############################################################################################################
|
|
##* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
##
|
|
## Description: Create Constraint Folder and Add Component(s) in, if they exist. Exclude FBCharacter &
|
|
## FBCharacterSolver url:bugstar:1264071
|
|
##
|
|
##* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
###############################################################################################################
|
|
|
|
def rs_CreateConstraintFolderAddComponents(pComponent, pComponentStr):
|
|
|
|
lFolder = rs_CreateFolderAddComponents(pComponent, "Constraints", name=pComponentStr,
|
|
excludeByTypes=[FBCharacterSolver, FBCharacter])
|
|
FBSystem().Scene.Evaluate()
|
|
|
|
|
|
def IsPyfbsdkType(pComponent, *pyfbsdkTypes):
|
|
"""
|
|
Checks if a model/component from motion builder is of a certain type
|
|
:param pComponent: FBComponent() or any object that inherits FBComponent; object to check type
|
|
:param pyfbsdkTypes:
|
|
:return:
|
|
"""
|
|
pyfbsdk = sys.modules["pyfbsdk"]
|
|
result_list = []
|
|
for eachType in pyfbsdkTypes:
|
|
if isinstance(eachType, basestring):
|
|
eachType = getattr(pyfbsdk, eachType)
|
|
result_list.append(isinstance(pComponent, eachType))
|
|
|
|
return True in result_list
|
|
|
|
|
|
def CreatePath(name, *points, **kwargs):
|
|
"""
|
|
Creates a Path3D object in the scene with keys at the given points
|
|
:param name:
|
|
:param points:
|
|
:return:
|
|
"""
|
|
curve = FBModelPath3D(name)
|
|
curve.Show = True
|
|
EditPath(curve, *points, **kwargs)
|
|
return curve
|
|
|
|
|
|
def EditPath(curve, *points, **kwargs):
|
|
plot = kwargs.get("Plot", False)
|
|
frame = kwargs.get("Frame", FBSystem().LocalTime.GetFrame())
|
|
for index, point in enumerate(points):
|
|
position = FBVector4d(*point)
|
|
try:
|
|
# We call PathKeyGet to check if a key already exists
|
|
# it will error out if it doesn't
|
|
if curve.PathKeyGet(index):
|
|
curve.PathKeySet(index, position)
|
|
|
|
except IndexError:
|
|
curve.Segment_PathKeyAdd(index, position)
|
|
|
|
# Sometimes the first point isn't set
|
|
# So we set it again
|
|
if not index:
|
|
curve.PathKeySet(index, position)
|
|
|
|
if plot:
|
|
property = curve.PropertyList.Find("Point_{}".format(index))
|
|
property.SetAnimated(True)
|
|
animationNode = curve.PropertyList.Find("Point_{}".format(index)).GetAnimationNode()
|
|
animationNode.KeyAdd(FBTime(0, 0, 0, frame), [each for each in position])
|
|
|
|
return curve
|
|
|
|
|
|
def CreateConstraint(type, name="", *references, **properties):
|
|
"""
|
|
DEPRECATED FUCNTION! Use:
|
|
from RS.Utils.Scene import Constraint
|
|
Constraint.CreateConstraint
|
|
"""
|
|
constraintTypes = {
|
|
"Aim" : Constraint.AIM,
|
|
"Expression" : Constraint.EXPRESSION,
|
|
"Multi Referential" : Constraint.MULTI_REFERENTIAL,
|
|
"Parent/Child" : Constraint.PARENT_CHILD,
|
|
"Path" : Constraint.PATH,
|
|
"Path Constraint" : Constraint.PATH,
|
|
"Position" : Constraint.POSITION,
|
|
"Range" : Constraint.RANGE,
|
|
"Relation" : Constraint.RELATION,
|
|
"Rigid Body" : Constraint.RIGID_BODY,
|
|
"3 Points" : Constraint.THREE_POINT,
|
|
"Rotation" : Constraint.ROTATION,
|
|
"Scale" : Constraint.SCALE,
|
|
"Mapping" : Constraint.MAPPING,
|
|
"Chain IK" : Constraint.CHAIN_IK}
|
|
|
|
constraint = Constraint.CreateConstraint(constraintTypes[type])
|
|
if name:
|
|
constraint.Name = name
|
|
|
|
[constraint.ReferenceAdd(index, component) for index, component in enumerate(references) if component != None]
|
|
SetProperties(constraint, **properties)
|
|
|
|
return constraint
|
|
|
|
|
|
""" Legacy Code"""
|
|
###############################################################################################################
|
|
##* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
##
|
|
## Description: Create Null Object and name it. This Null object was introduced to the character asset setup
|
|
## to keep the scenes clean.
|
|
## Requested by Animation
|
|
##
|
|
##* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
###############################################################################################################
|
|
|
|
def rs_CreateNullElement(pName, pXpos, pYpos, pZpos):
|
|
|
|
lNull = FBModelNull(pName)
|
|
lNull.Show = False
|
|
lNull.Translation = FBVector3d(pXpos, pYpos, pZpos)
|
|
|
|
return lNull
|
|
|
|
|