Files
2025-09-29 00:52:08 +02:00

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()
)
)