Files
gtav-src/tools_ng/wildwest/script/3dsMax/Characters/Rigging/ambientFacial/connect1DSliderToAmbientJoystick.ms
T
2025-09-29 00:52:08 +02:00

580 lines
20 KiB
Plaintext
Executable File

--tool to query animations and then generate face fx 1d sliders
-- Load the common maxscript functions
--include "rockstar/export/settings.ms" -- This is SLOW!
filein "rockstar/export/settings.ms" -- This is fast
-- Figure out the project
theProjectRoot = RsConfigGetProjRootDir()
theProject = RSConfigGetProjectName()
theWildWest = RsConfigGetWildWestDir()
theProjectConfig = RsConfigGetProjBinConfigDir()
-- Load common functions
filein (theWildWest + "script/max/Rockstar_North/character/includes/FN_common.ms")
filein (theWildWest + "script/max/Rockstar_North/character/includes/FN_bone_tagger.ms")
filein (theWildWest + "script/max/Rockstar_North/character/includes/FN_Rigging.ms")
-- filein (theProjectRoot + "tools/dcc/current/max2010/scripts/pipeline/util/xml.ms")
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
facialBoneArray = #(
"FB_L_Brow_Out",
"FB_Brow_Centre",
"FB_L_Lid_Upper",
"FB_L_Eye",
"FB_L_CheekBone",
"FB_UpperLipRoot",
"FB_UpperLip",
"FB_L_Lip_Top",
"FB_R_Lip_Top",
"FB_L_Lip_Corner",
"FB_Jaw",
"FB_LowerLipRoot",
"FB_LowerLip",
"FB_L_Lip_Bot",
"FB_R_Lip_Bot",
"FB_Tongue",
"FB_R_Lid_Upper",
"FB_R_Eye",
"FB_R_CheekBone",
"FB_R_Brow_Out",
"FB_R_Lip_Corner"
)
ambientArray = #( --array of ambient joysticks
"Tongue",
"L_Blink",
"LookAT_Activator",
"L_Cheek",
"LowerLip",
"UpperLip_Curl",
"LowerLip_Curl",
"UpperLip",
"L_Mouth",
"Jaw",
"Mouth",
"R_Eye",
"L_Eye",
"R_Mouth",
"C_Brow",
"Tongue_In_Out",
"R_Cheek",
"R_Brow",
"L_Brow",
"R_Blink"
)
poseArray= #( --facefx 1d slider joystick Name and frame at which pose is derived and position for joystick
-- #("Neutral", 0, [0.0,0,0]),
#("open", 10, [0.1,0,0]),
#("W", 20, [0.2,0,0]),
#("ShCh", 30, [0.3,0,0]),
#("PBM", 40, [0.4,0,0]),
#("FV", 50, [0.5,0,0]),
#("wide", 60, [0.6,0,0]),
#("tBack", 70, [0.7,0,0]),
#("tTeeth", 80, [0.8,0,0]),
#("tRoof", 90, [0.9,0,0]),
#("Blink_Left", 100, [0,0,-0.2]),
#("Blink_Right", 110, [0.1,0,-0.3]),
#("Eyebrow_Raise_Left", 120, [0.2,0,-0.2]),
#("Eyebrow_Raise_Right", 130, [0.3,0,-0.3]),
#("Squint_Left", 140, [0.4,0,-0.2]),
#("Squint_Right", 150, [0.5,0,-0.3]),
-- #("Head_Pitch_Pos", 200),
-- #("Head_Yaw_Pos", 210),
-- #("Head_Roll_Pos", 220),
-- #("LeftEye_Pitch_Pos", 230, [0.6,0,-0.2]),
-- #("LeftEye_Yaw_Pos", 240, [0.7,0,-0.3]),
-- #("RightEye_Pitch_Pos", 250, [0.8,0,-0.2]),
-- #("RightEye_Yaw_Pos", 260, [0.9,0,-0.3]),
-- // Negative Rotations below
-- #("Head_Pitch_Neg", 270),
-- #("Head_Yaw_Neg", 280),
-- #("Head_Roll_Neg", 290),
-- #("LeftEye_Pitch_Neg", 300, [0.0,0,-0.4]),
-- #("LeftEye_Yaw_Neg", 310, [0.1,0,-0.5]),
-- #("RightEye_Pitch_Neg", 320, [0.2,0,-0.4]),
-- #("RightEye_Yaw_Neg", 330, [0.3,0,-0.5]),
-- #("AU1-Inner_Brow_Raiser_Left", 400, [0.4,0,-0.4]),
-- #("AU1-Inner_Brow_Raiser_Right", 410, [0.5,0,-0.5]),
-- #("AU2-Outer_Brow_Raiser_Left", 420, [0.6,0,-0.4]),
-- #("AU2-Outer_Brow_Raiser_Right", 430, [0.7,0,-0.5]),
-- #("AU4-Brow_Lowerer", 440, [0.8,0,-0.4]),
-- #("AU10-Upper_Lip_Raiser", 450, [0.9,0,-0.5]),
-- #("AU15-Lip_Corner_Depressor_Left", 460, [0,0,-0.6]),
-- #("AU15-Lip_Corner_Depressor_Right", 470, [0.1,0,-0.7]),
-- #("AU16-Lower_Lip_Depressor", 480, [0.2,0,-0.6]),
-- #("AU20-Lip_Stretcher", 490, [0.3,0,-0.7]),
-- #("AU23-Lip_Tightener", 500, [0.4,0,-0.6]),
-- #("AU26-Jaw_Drop", 510, [0.5,0,-0.7]),
-- #("AU29-Jaw_Sideways", 530, [0.6,0,-0.6]),
#("Cheek_Up_Left", 600, [0.7,0,-0.7]),
#("Cheek_Up_Right", 610, [0.8,0,-0.6]),
#("Snarl_Left", 620, [0.9,0,-0.7]),
#("Snarl_Right", 630, [0,0,-0.8])
)
dupJoystickArray = #( --array of ambient joysticks which we need to disconnect after facefx joystick creation
-- "Tongue",
"L_Blink",
-- "LookAT_Activator",
-- "L_Cheek",
-- "LowerLip",
-- "UpperLip_Curl",
-- "LowerLip_Curl",
-- "UpperLip",
-- "L_Mouth",
-- "Jaw",
-- "Mouth",
-- "R_Eye",
-- "L_Eye",
-- "R_Mouth",
-- "C_Brow",
-- "Tongue_In_Out",
-- "R_Cheek",
-- "R_Brow",
-- "L_Brow",
"R_Blink"
)
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
fn disconnectAmbientExpr joystickName = --this will disconnect expressions for specified joystickName
(
-- for o in objects do
for o in objects do
(
if (substring o.name 1 3 ) == "FB_" do
(
if classOf o.position.controller == position_list do
(
for p = 1 to o.position.controller.count do
(
if o.position.controller[p].name == joystickName do
(
o.position.controller.delete p
print ("Disconnected "+joystickName+" position from "+o.name)
)
)
)
if classOf o.rotation.controller == rotation_list do
(
for r = 1 to o.rotation.controller.count do
(
if o.rotation.controller[r].name == joystickName do
(
o.rotation.controller.delete r
print ("Disconnected "+joystickName+" rotation from "+o.name)
)
)
)
if classOf o.scale.controller == scale_list do
(
for s = 1 to o.scale.controller.count do
(
if o.scale.controller[s].name == joystickName do
(
o.scale.controller.delete s
print ("Disconnected "+joystickName+" scale from "+o.name)
)
)
)
)
)
)
fn create1DSlider sliderCount mySliderName sliderPos =
(
-- call the joystick creation function with the UI data
jstemp = mySliderName as string
-- jstemp = edtSliderName.text as string
jsname = replacespace jstemp
jstyle = 2
jpos = sliderPos -- the default position on screen. This can overridden later, perhaps by mouse click.
-- Create a joystick slider and select it
seljoy = createJoystick ("FFX_CTRL_"+jsname) jstyle jpos 1 -- This can also be copied and pasted to a script to auto build a controller
)
fn create1DExpr facialBone contType fbAxis poseName ambientName scalarNames scalarTargets jsMultiplier scalarToChangeIndex theexpr =
(
if contType == "Position" then
(
contTypeB = "Position"
)
else
(
contTypeB = "Euler"
)
ambientJoystickName = (substring ambientName 6 30 )
--first off add a new controller to the face bone and name it to match the pose
contCount = ("$"+facialBone.name+"."+contType+".controller.count" )
contCount = execute contCount
-- print ("contCount = "+(contCount as string))
newCont = ("$"+facialBone.name+"."+contType+".Available.controller = "+contTypeB+"_XYZ ()")
newCnt = execute newCont
newControllerNameStr = ("FFX_"+poseName+"_"+ambientName)
newCont = ("$"+facialBone.name+"."+contType+".controller"+".setName "+((contCount + 1) as string)+" "+"\""+newControllerNameStr +"\"")
newCont = execute newCont
--now we can add a float expression to the relevant controller axis
fcStr = ( "$"+facialBone.name+"."+contType+".controller."+newControllerNameStr+"."+fbAxis+"_"+contType+".controller = Float_Expression() ")
newFcStr = execute fcStr
--now we can start to edit that expression
--first need to find the original driving scalar and then swap that to be the faceFX joystick with its multiplier
originalScalarName = undefined
replaceMentScalarName = undefined
for i = 1 to scalarNames.count do --this should loop through the array of scalars and their controllers and create a scalar for each
(
if i == scalarToChangeIndex then
(
originalScalarName = scalarNames[i]
replaceMentScalarName =( "("+"("+poseName+" / "+(jsMultiplier as string)+")"+" * 1)")
--ok we're going to point this scalar at the Y movement of the appropriate faceFX joystick
ffxJoystick = getNodeByName ("FFX_CTRL_"+poseName)
SVN = poseName --name of the scalar
scalContStr = ("$"+ffxJoystick.name+".position.controller.Zero_Pos_XYZ.controller.Y_Position.controller") --actually controller the scale points to
scalCont = execute scalContStr
)
else
(
SVN = scalarNames[i] --name of the scalar
scalContStr = (scalarTargets[i] as string) --actually controller the scale points to
scalCont = execute scalContStr
)
newfcStr.AddScalarTarget SVN scalCont --add scalar pointing to the controller of the scalar object
)
--now we need to edit 'theexpr' and replace the scalarNames[i] with the new 'scalarNames[i]' plus the jsMultiplier
--so we need to test the 'theExpr' string and replace originalScalarName with newScalarName
originalScalarNameLength = originalScalarName.count
replaceThisString = theExpr
for r = 1 to 3 do --need to loop through the expression string 3 times because the original scalar name is in the expression 3 times
(
newExprStrStart = findString replaceThisString originalScalarName
replaceThisString = replace replaceThisString newExprStrStart originalScalarNameLength replaceMentScalarName
)
-- print "--------------------------"
-- print "--------------------------"
-- print ("origninal expression:\r\n")
-- print theExpr
-- print "--------------------------"
-- print ("new expression:\r\n")
-- print replaceThisString
-- print "--------------------------"
-- print "--------------------------"
newfcStr.SetExpression replaceThisString
)
-- fn readExpression controller joystickAxis facialBone poseName axisUsed transType =
fn readExpression controller joystickAxis facialBone jsAx fbAx contType poseName ambientName jsMultiplier=
(
joystickAxis = exprformaxobject joystickAxis
scalarNames=#()
scalarTargets=#()
-- print ("Controller = "+controller)
theExprStr = ((controller as string)+".getExpression()")
theexpr = execute theExprStr
theScalarStr = ((controller as string)+".NumScalars()")
theScalarCount = execute theScalarStr
if theScalarCount != 4 do --need to remember there are always 4 for ticks, frames, secs and normalised-time
(
for i = 5 to theScalarCount do
(
thisScalarNameStr = ((controller as string)+".GetScalarName "+(i as string))
thisScalarName = execute thisScalarNameStr
appendIfUnique scalarNames thisScalarName
)
)
for i = 1 to scalarNames.count do
(
thisScalarTgtStr = ((controller as string)+".GetScalarTarget "+"\""+scalarNames[i]+"\""+" asController:true")
thisScalarTgt = execute thisScalarTgtStr
-- print ("Scalar value = "+(thisScalarTgt.value as string))
thisScalarTgtCont = exprformaxobject thisScalarTgt
appendIfUnique scalarTargets (thisScalarTgtCont as string)
-- print ("appending "+(thisScalarTgtCont as string)+" to controller array...")
)
--now we need to check if any of the scalaras match the joystick axis.
--if they do then we can do a create expression.
for i = 1 to scalarTargets.count do
(
-- print ("testing "+(scalarTargets[i] as string)+" against "+(joystickAxis as string))
if scalarTargets[i] == joystickAxis do
(
-- print ("WOOHOO "+(scalarTargets[i] as string)+" matched "+(joystickAxis as string)+"!")
--ok we now know that this jsAxis matches this expression.
--so we need to tell the expression creator what axis to add a controller to on what bone
-- print "\r\n"
-- print ("***************** Running create1dExpr on "+facialBone.name+", faceboneAxis "+fbax+", controller type "+conttype+", for pose "+poseName+" *******************")
-- print "\r\n"
scalarToChangeIndex = i
create1DExpr facialBone contType fbAx poseName ambientName scalarNames scalarTargets jsMultiplier scalarToChangeIndex theexpr
)
)
)
fn passDataToReadExpr ambientName jsAxis boneNumber jsAx poseName jsMultiplier =
(
for fb = 1 to facialBoneArray.count do
(
facialBone = getNodeByName (facialBoneArray[fb]+boneNumber)
posCount = facialBone.position.controller.count
for cC = 1 to posCount do
(
contType = "Position"
contName = facialBone.position.controller.getName cC
if contName == (ambientName) do
(
-- print ("found a "+ambientName+" Position controller on "+facialBone.name)
--ok so now we need to query if the x y or z pos controller are float expressions
--ok so now we need to query if the x y or z pos controller are float expressions
if (facialBone.position.controller[cC].X_position.controller as string) == "Controller:Float_Expression" do
(
--now we need to query if the jsAxis is the driver in the expression
-- print ("X ROT was an EXPR")
controller = ("$"+facialBone.name+".position.controller["+(cC as string)+"].X_position.controller")
fbAx = "X"
readExpression controller jsAxis facialBone jsAx fbAx contType poseName ambientName jsMultiplier
)
if (facialBone.position.controller[cC].Y_position.controller as string) == "Controller:Float_Expression" do
(
-- print ("Y ROT was an EXPR")
-- controller = facialBone.position.controller[cC].Y_position.controller
controller = ("$"+facialBone.name+".position.controller["+(cC as string)+"].Y_position.controller")
fbAx = "Y"
readExpression controller jsAxis facialBone jsAx fbAx contType poseName ambientName jsMultiplier
)
if (facialBone.position.controller[cC].Z_position.controller as string) == "Controller:Float_Expression" do
(
-- print ("Z ROT was an EXPR")
-- controller = facialBone.position.controller[cC].Z_position.controller
controller = ("$"+facialBone.name+".position.controller["+(cC as string)+"].Z_position.controller")
fbAx = "Z"
readExpression controller jsAxis facialBone jsAx fbAx contType poseName ambientName jsMultiplier
)
)
)
rotCount = facialBone.rotation.controller.count
for cC = 1 to rotCount do
(
contType = "Rotation"
contName = facialBone.rotation.controller.getName cC
if contName == (ambientName) do
(
-- print ("found a "+ambientName+" Rotation controller on "+facialBone.name)
--ok so now we need to query if the x y or z pos controller are float expressions
if (facialBone.rotation.controller[cC].X_rotation.controller as string) == "Controller:Float_Expression" do
(
--now we need to query if the jsAxis is the driver in the expression
-- print ("X ROT was an EXPR")
controller = ("$"+facialBone.name+".rotation.controller["+(cC as string)+"].X_Rotation.controller")
fbAx = "X"
readExpression controller jsAxis facialBone jsAx fbAx contType poseName ambientName jsMultiplier
)
if (facialBone.rotation.controller[cC].Y_rotation.controller as string) == "Controller:Float_Expression" do
(
-- print ("Y ROT was an EXPR")
-- controller = facialBone.rotation.controller[cC].Y_Rotation.controller
controller = ("$"+facialBone.name+".rotation.controller["+(cC as string)+"].Y_Rotation.controller")
fbAx = "Y"
readExpression controller jsAxis facialBone jsAx fbAx contType poseName ambientName jsMultiplier
)
if (facialBone.rotation.controller[cC].Z_rotation.controller as string) == "Controller:Float_Expression" do
(
-- print ("Z ROT was an EXPR")
-- controller = facialBone.rotation.controller[cC].Z_Rotation.controller
controller = ("$"+facialBone.name+".rotation.controller["+(cC as string)+"].Z_Rotation.controller")
fbAx = "Z"
readExpression controller jsAxis facialBone jsAx fbAx contType poseName ambientName jsMultiplier
)
)
)
)
)
fn parsePoses boneNumber =
(
clearListener()
for i = 1 to poseArray.count do
(
--first set frame to the 2nd element of this item ie frame number
poseFrame = posearray[i][2]
poseName = poseArray[i][1]
sliderTime = poseFrame
--now for each joystick query their x and y positions
for js = 1 to ambientArray.count do
(
thisJoystick = getNodeByName ("CTRL_"+ambientArray[js])
jsPos = in coordsys parent thisJoystick.position
myX = jsPos[1]
newX = ( 1 / myX )
-- jsXMultiplier = myX * newX as integer
jsXMultiplier = newX as float
myY = jsPos[2]
newY = ( 1 / myY )
-- jsYMultiplier = myY * newY as integer
jsYMultiplier = newY as float
if myX != 0 do
(
-- print ("jsXMultiplier ="+(jsXMultiplier as string)+" for "+thisJoystick.name+" with pose "+poseName)
-- print ("derived by myX ( 1 / "+(myX as string)+")"+" gives "+(jsXMultiplier as string))
--ok the joystick has moved in the x position
-- print ("Need to create X expr on "+ambientArray[js]+" for pose "+poseName)
jsAxis = thisJoystick.position.controller.zero_pos_xyz.x_position.controller
jsAx = "X"
passDataToReadExpr ambientArray[js] jsAxis boneNumber jsAx poseName jsXMultiplier
)
if myY != 0 do
(
-- print ("jsYMultiplier ="+(jsYMultiplier as string)+" for "+thisJoystick.name+" with pose "+poseName)
-- print ("derived by myY ( 1 / "+(myY as string)+")"+" gives "+(jsYMultiplier as string))
-- print ("Need to create Y expr on "+ambientArray[js]+" for pose "+poseName)
jsAxis = thisJoystick.position.controller.zero_pos_xyz.Y_position.controller
jsAx = "Y"
passDataToReadExpr ambientArray[js] jsAxis boneNumber jsAx poseName jsYMultiplier
)
)
sliderTime = 0f
)
)
fn connect1dToAmbientJoysticks =
(
--firstly make the 1d joysticks
for sl = 1 to poseArray.count do
-- for sl = 1 to 2 do
(
create1DSlider 1 poseArray[sl][1] poseArray[sl][3]
)
--now create the facefx text parent obj
faceFXText = text size:0.25 kerning:0 leading:0 transform:(matrix3 [1,0,0] [0,0,1] [0,-1,0] [0.215069,0,-0.0633061]) isSelected:on
faceFXText.text = "FaceFX"
faceFXText.render_displayRenderMesh = true
faceFXText.thickness = 0.01
faceFXText.sides = 3
faceFXText.name = "FaceFX"
in coordsys world faceFXText.pos = [0.45,0,0.1]
--now link all the ffx rectangles to the faceFX object
for o in objects where (substring o.name 1 8) == "FFX_RECT" do
(
o.parent = faceFXText
)
faceFXText.scale = [0.3,0.3,0.3]
in coordsys world faceFXText.pos = [0.5,0.0,1.8]
--setup done.
--now we need to find all the facial bones in the scene
faceBoneIterations = #()
for o in objects do
(
if substring o.name 1 8 == "FaceRoot" do
(
--ok we've found a faceroot node and therefore a rig so we need to find the number of it
rigNumber = (substring o.name 14 4)
appendIfUnique faceBoneIterations rigNumber
)
)
--start the clever stuff!
for fNo = 1 to faceBoneIterations.count do
(
--boneNumber is the suffix numbers on the bones
--thisBoneNumber = "_000"
thisBoneNumber = faceBoneIterations[fNo]
parsePoses thisBoneNumber
)
)
-- generate1dStruct()
clearListener()
connect1dToAmbientJoysticks()
for i = 1 to dupJoystickArray.count do
(
disconnectAmbientExpr dupJoystickArray[i]
)
print ("FaceFx creation complete.")
-- select $FB_R_Lip_Corner_000