245 lines
8.1 KiB
Plaintext
Executable File
245 lines
8.1 KiB
Plaintext
Executable File
struct sGTAVFacialConverter
|
|
(
|
|
---------------------------------------------------------------------------
|
|
-- LOCALS
|
|
---------------------------------------------------------------------------
|
|
solverAssetPath = (RsConfigGetProjRootDir core:true) + "art/animation/resources/characters/solver_assets/",
|
|
assetsPath = (RsConfigGetAssetsDir core:true) + "anim/expressions/",
|
|
outputPath = undefined,
|
|
tempPath = this.solverAssetPath + "temp/",
|
|
|
|
scriptRoot = (RsConfigGetWildwestDir() + "script/3dsMax/Characters/Setup/GTAVFacialConverter/"),
|
|
|
|
convertBat = this.scriptRoot + "Bin/convertexpressions.bat",
|
|
makeExprBat = this.scriptRoot + "Bin/makeexpressions.bat",
|
|
|
|
idXML = this.scriptRoot + "Config/ExpressionIDs.xml",
|
|
idTable = DotNetObject "System.Collections.HashTable",
|
|
clampFile = this.scriptRoot + "Config/scalesToClamp.xml",
|
|
scalesToClamp = #(),
|
|
|
|
pedName = undefined,
|
|
expressionList = #(),
|
|
fileList = #(),
|
|
skelset = undefined,
|
|
|
|
---------------------------------------------------------------------------
|
|
-- FUNCTIONS
|
|
---------------------------------------------------------------------------
|
|
|
|
---------------------------------------------------------------------------
|
|
-- make sure save paths exist, otherwise create them
|
|
fn ValidateSavePath =
|
|
(
|
|
this.outputPath = this.solverAssetPath + this.pedName + "/"
|
|
|
|
local pathsToValidate = #( this.outputPath, (this.outputPath + "skeleton/"), (this.outputPath + "expressions/") )
|
|
|
|
for p in pathsToValidate do
|
|
(
|
|
if( not DoesFileExist p )then MakeDir p all:true
|
|
)
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- attempt to get the expressions for this ped
|
|
fn GetPedExpressions =
|
|
(
|
|
if( this.pedName != undefined )then
|
|
(
|
|
local exprPath = this.assetsPath + this.pedName + "/components/"
|
|
if( DoesFileExist exprPath )then
|
|
(
|
|
local dirFiles = GetFiles (exprPath + "*.xml")
|
|
|
|
for x in dirFiles do
|
|
(
|
|
local fName = GetFileNameFile x
|
|
if( MatchPattern fName pattern:"head_000_r" or MatchPattern fName pattern:"teef_000_u" )then AppendIfUnique this.expressionList x
|
|
)
|
|
)
|
|
)
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- replace expression IDs that are incorrect due to the ConvertExpressions process
|
|
fn FixExprIDs x =
|
|
(
|
|
local xml = RSTA_XML_IO xmlFile:x
|
|
|
|
local exprsToClamp = #()
|
|
|
|
local objIter = (xml.root.SelectNodes "//object").GetEnumerator()
|
|
while objIter.MoveNext() do
|
|
(
|
|
local id = objIter.current.GetAttribute "id"
|
|
local track = objIter.current.GetAttribute "track"
|
|
|
|
if( this.idTable.item[id] != undefined )then
|
|
(
|
|
objIter.current.SetAttribute "id" this.idTable.item[id]
|
|
id = this.idTable.item[id]
|
|
)
|
|
|
|
if( track == "facialControl" )then
|
|
(
|
|
objIter.current.SetAttribute "track" "facialTranslation"
|
|
objIter.current.SetAttribute "type" "vector3"
|
|
objIter.current.SetAttribute "component" "x"
|
|
)
|
|
|
|
if( FindItem this.scalesToClamp id != 0 and track == "boneScale" )then
|
|
(
|
|
local tarName = objIter.current.GetAttribute "name"
|
|
Append exprsToClamp tarName
|
|
)
|
|
)
|
|
|
|
local exprIter = (xml.root.SelectNodes "//expression").GetEnumerator()
|
|
while exprIter.MoveNext() do
|
|
(
|
|
local target = exprIter.current.GetAttribute "target"
|
|
|
|
if( FindItem exprsToClamp target != 0 )then exprIter.current.SetAttribute "string" "vec( 1, 1, 1 )"
|
|
)
|
|
|
|
xml.Save saveWarning:false
|
|
xml.Dispose()
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- run an external process
|
|
fn ExternalProcess xml bat =
|
|
(
|
|
-- local msgList = #()
|
|
-- local errList = #()
|
|
|
|
local cmd = ( xml as string )
|
|
|
|
local convertProcess = RsProcess()
|
|
convertProcess.RunProcess bat cmd workingDir:(RsConfigGetProjRootDir()) --messageList:&msgList errorList:&errList
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- convert expressions to new format and them output the .expr file
|
|
fn ProcessExpressions =
|
|
(
|
|
for xml in this.expressionList do
|
|
(
|
|
local targetName = GetFileNameFile xml
|
|
local targetFile = (this.outputPath + "expressions/" + targetName + ".xml")
|
|
|
|
local convertSuccess = this.ExternalProcess xml this.convertBat
|
|
if( convertSuccess != 0 )then Format "Failed in ConvertExpressions for %\n" xml
|
|
else
|
|
(
|
|
if( DoesFileExist targetFile )then
|
|
(
|
|
gRsPerforce.Edit targetFile
|
|
DeleteFile targetFile
|
|
CopyFile (this.tempPath + "expr.xml") targetFile
|
|
)
|
|
else
|
|
(
|
|
CopyFile (this.tempPath + "expr.xml") targetFile
|
|
gRsPerforce.Add_Or_Edit targetFile
|
|
)
|
|
|
|
this.FixExprIDs targetFile
|
|
|
|
local makeExprSuccess = this.ExternalProcess targetFile this.makeExprBat
|
|
if( makeExprSuccess != 0 )then Format "Failed in MakeExpressions for %\n" targetFile
|
|
)
|
|
)
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- build a table of the hashes for all valid facial bones and controllers
|
|
fn CollectHashIDTable =
|
|
(
|
|
local xml = RSTA_XML_IO xmlFile:this.idXML
|
|
local objIter = (xml.root.SelectNodes "//object").GetEnumerator()
|
|
while objIter.MoveNext() do
|
|
(
|
|
this.idTable.Add (objIter.current.GetAttribute "id") (objIter.current.GetAttribute "name")
|
|
)
|
|
|
|
xml.Dispose()
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- collect a list of scale controllers that can be clamped to [1,1,1]
|
|
fn CollectScalesToClamp =
|
|
(
|
|
local xml = RSTA_XML_IO xmlFile:this.clampFile
|
|
local objectIter = (xml.root.SelectNodes "//object").GetEnumerator()
|
|
while objectIter.MoveNext() do Append this.scalesToClamp (objectIter.current.GetAttribute "name")
|
|
xml.Dispose()
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- align a bone to another and freeze transform
|
|
fn AlignAndFreeze b target =
|
|
(
|
|
b.transform = target.transform
|
|
ClearSelection()
|
|
Select b
|
|
FreezeTransform()
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- reparent and freeze bones
|
|
fn ParentAndFreeze b bParent =
|
|
(
|
|
b.parent = bParent
|
|
ClearSelection()
|
|
Select b
|
|
FreezeTransform()
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- parent the on-face controls into the skeleton for export
|
|
fn SetUpFacialOffCtrls =
|
|
(
|
|
local offRoot = $'facialRoot_C_OFF'
|
|
local offJaw = $'jaw_C_OFF'
|
|
local facialRoot = $'FACIAL_facialRoot'
|
|
|
|
if( offRoot != undefined and facialRoot != undefined and offJaw != undefined)then
|
|
(
|
|
local offRootChildren = for c in offRoot.children collect c
|
|
for c in offRootChildren do c.parent = undefined
|
|
|
|
local offJawChildren = for c in offJaw.children collect c
|
|
for c in offJawChildren do c.parent = undefined
|
|
|
|
this.AlignAndFreeze offJaw $'FACIAL_jaw'
|
|
this.AlignAndFreeze offRoot $'FACIAL_facialRoot'
|
|
|
|
for c in offJawChildren do this.ParentAndFreeze c offJaw
|
|
for c in offRootChildren do this.ParentAndFreeze c offRoot
|
|
|
|
local skelRoot = $'SKEL_ROOT'
|
|
if( skelRoot != undefined )then offRoot.parent = skelRoot
|
|
)
|
|
else Format "Missing one of the Facial root nodes: %, %, \n" "FACIAL_facialRoot" "facialRoot_C_OFF" "jaw_C_OFF"
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- remove the on-face controls from the skeleton after export
|
|
fn CleanUpFacialOffCtrls =
|
|
(
|
|
local offRoot = $'facialRoot_C_OFF'
|
|
if( offRoot != undefined )then offRoot.parent = undefined
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- export the skeleton
|
|
fn ExportSkeleton =
|
|
(
|
|
this.SetUpFacialOffCtrls()
|
|
|
|
local skelExporter = sGTAVSkeletonExporter pedName:this.pedName
|
|
skelExporter.Init()
|
|
skelExporter.WriteSkelset this.solverAssetPath this.outputPath
|
|
skelExporter.WriteSkeleton()
|
|
|
|
this.CleanUpFacialOffCtrls()
|
|
),
|
|
---------------------------------------------------------------------------
|
|
-- initialize the clamp and ID table pvariables
|
|
fn Init =
|
|
(
|
|
this.CollectHashIDTable()
|
|
this.CollectScalesToClamp()
|
|
)
|
|
) |