613 lines
19 KiB
Plaintext
Executable File
613 lines
19 KiB
Plaintext
Executable File
FileIn (RsConfigGetWildWestDir() + "script/3dsMax/_config_files/Wildwest_header.ms")
|
|
FileIn (RsConfigGetWildWestDir() + "script/3dsMax/General_tools/SkinnedObject.ms")
|
|
FileIn "StatedAnimCombine.ms"
|
|
|
|
struct _StatedAnimCore_
|
|
(
|
|
/*---------------------------------------------------
|
|
Relevant attribute idx(s)
|
|
*/---------------------------------------------------
|
|
StatedAnimGroupIdx = GetAttrIndex "Gta Object" "groupName",
|
|
StatedAnimStateIdx = GetAttrIndex "Gta Object" "animState",
|
|
HasAnimIdx = GetAttrIndex "Gta Object" "Has Anim",
|
|
AuthoredOrientsIdx = GetAttrIndex "Gta Object" "Authored Orients",
|
|
TXDIdx = GetAttrIndex "Gta Object" "TXD",
|
|
|
|
StateAnimation = #Animation,
|
|
StateStart = #Start,
|
|
StateEnd = #End,
|
|
|
|
MaximumBoneCount = 127,
|
|
MaximumVertCount = 64000,
|
|
|
|
/*---------------------------------------------------
|
|
Returns true if the object is valid for inclusion in the creation of a StatedAnim
|
|
*/---------------------------------------------------
|
|
fn IsValidObject Obj =
|
|
(
|
|
case (ClassOf Obj) of
|
|
(
|
|
Editable_Poly: return True
|
|
Editable_Mesh: return True
|
|
PolyMeshObject: return True
|
|
default: return False
|
|
)
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Converts a SelectionSet to an normal array. So we can do join and append operations.
|
|
*/---------------------------------------------------
|
|
fn SelectionSetToArray InputSelectionSet =
|
|
(
|
|
ReturnArray = #()
|
|
for Obj in InputSelectionSet do
|
|
(
|
|
Append ReturnArray Obj
|
|
)
|
|
return ReturnArray
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Get all node dependants of all the nodes of the passed array.
|
|
*/---------------------------------------------------
|
|
fn GetNodeDependencies InputNode =
|
|
(
|
|
--Collect any direct dependencies
|
|
ReturnDependencyArray = for Dependant in (Refs.DependsOn InputNode) where IsValidNode Dependant collect Dependant
|
|
|
|
--We also collect any dependencies of the modifier stack
|
|
for SubModifier in InputNode.Modifiers do
|
|
(
|
|
SubDependencyArray = for Dependant in (Refs.DependsOn SubModifier) where IsValidNode Dependant collect Dependant
|
|
Join ReturnDependencyArray SubDependencyArray
|
|
)
|
|
|
|
--Return info
|
|
return ReturnDependencyArray
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Deletes any objects that are not needed for stated anim we are working with. Also returns the full array of nodes left in scene.
|
|
*/---------------------------------------------------
|
|
fn RemoveNonDependantNodes NodeArray =
|
|
(
|
|
--Get all nodes that we are dependant on
|
|
DependencyNodeArray = #()
|
|
for SubNode in NodeArray do
|
|
(
|
|
SubNodeDependencyArray = GetNodeDependencies SubNode
|
|
Join DependencyNodeArray SubNodeDependencyArray
|
|
)
|
|
--Add these nodes to the list passed in to create a full list of nodes needed
|
|
FullNodeArray = MakeUniqueArray (Join DependencyNodeArray NodeArray)
|
|
|
|
--Delete any objects that don't appear on the list
|
|
ObjectArray = Objects as Array
|
|
for Obj in ObjectArray do
|
|
(
|
|
if( FindItem FullNodeArray Obj == 0 ) then
|
|
(
|
|
if IsValidNode Obj then Delete Obj
|
|
)
|
|
)
|
|
|
|
return FullNodeArray
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Deletes any existing anim proxies for the passed StatedAnimGroup
|
|
*/---------------------------------------------------
|
|
fn DeleteOldProxy =
|
|
(
|
|
ProxyObjects = for Obj in Objects where (ClassOf Obj == StatedAnimProxy) collect Obj
|
|
|
|
for Obj in ProxyObjects do
|
|
(
|
|
Delete Obj
|
|
)
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Creates an animation proxy for the selected statedanim
|
|
*/---------------------------------------------------
|
|
fn CreateProxy StatedAnimGroup =
|
|
(
|
|
--Delete any old proxies for this anim group
|
|
DeleteOldProxy()
|
|
|
|
SuspendEditing()
|
|
|
|
--Create our new anim proxy objects
|
|
NewProxy = StatedAnimProxy ShowEnd:False
|
|
StartAnimProxy
|
|
EndAnimProxy
|
|
|
|
--Collect anim objects
|
|
AnimObjects = for StatedAnimObj in This.GetGroupObjectArray StatedAnimGroup where This.GetState StatedAnimObj == "Animation" collect StatedAnimObj
|
|
|
|
--Get snapshot of the anim objects at the start and end
|
|
EndFrame = AnimationRange.End.Frame as integer
|
|
StartFrame = AnimationRange.Start.Frame as integer
|
|
at Time StartFrame
|
|
(
|
|
StartAnimProxy = RSMesh_BasicSnapshotFromArray AnimObjects RefTransform:AnimObjects[1].Transform
|
|
)
|
|
at Time EndFrame
|
|
(
|
|
EndAnimProxy = RSMesh_BasicSnapshotFromArray AnimObjects RefTransform:AnimObjects[1].Transform
|
|
)
|
|
|
|
--We use the transform from the first animation object although all animation objects should be the same
|
|
NewProxy.Name = StatedAnimGroup + "_anim"
|
|
NewProxy.NodeTab = #(StartAnimProxy, EndAnimProxy)
|
|
NewProxy.UpdateMyMesh()
|
|
NewProxy.Transform = AnimObjects[1].Transform
|
|
|
|
Delete StartAnimProxy
|
|
Delete EndAnimProxy
|
|
|
|
--Setup animation start/end frame
|
|
AnimStartIdx = GetAttrindex "RsStatedAnim" "Animation Start"
|
|
AnimEndIdx = GetAttrindex "RsStatedAnim" "Animation End"
|
|
|
|
SetAttr NewProxy AnimStartIdx StartFrame
|
|
SetAttr NewProxy AnimEndIdx EndFrame
|
|
|
|
ResumeEditing()
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Gets the root object of the selection set. Basicaly the first instance of a node called "__ROOT__"". This root object will be our pivot for creating our StatedAnim.
|
|
*/---------------------------------------------------
|
|
fn GetSelectionSetRoot InputSelectionSet =
|
|
(
|
|
for Obj in InputSelectionSet do
|
|
(
|
|
if Obj.Name == "__ROOT__" then return Obj
|
|
)
|
|
|
|
return Undefined
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Returns an array of valid selection sets.
|
|
*/---------------------------------------------------
|
|
fn GetValidSelectionSetArray =
|
|
(
|
|
ValidSelectionSetArray = #()
|
|
|
|
for SelectionSet in SelectionSets do
|
|
(
|
|
StringNET = DotNetObject "System.String" SelectionSet.Name
|
|
--Check the name has the destruction prefix first
|
|
if ( StringNET.StartsWith "DES_" ) then
|
|
( -- Check to see if the selection set has a root object
|
|
if( GetSelectionSetRoot SelectionSet != Undefined ) then
|
|
(
|
|
--We have a selection set that we can create StatedAnimation from
|
|
Append ValidSelectionSetArray SelectionSet
|
|
)
|
|
)
|
|
)
|
|
|
|
return ValidSelectionSetArray
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Get valid objects from passed target selection set name
|
|
*/---------------------------------------------------
|
|
fn GetTargetObjectArray TargetSelectionSetName =
|
|
(
|
|
TargetSelectionSet = SelectionSets[TargetSelectionSetName]
|
|
|
|
TargetObjectArray = for Obj in TargetSelectionSet where IsValidObject Obj collect Obj
|
|
return TargetObjectArray
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Sets up the visibility bounds of the passed object from start to end
|
|
*/---------------------------------------------------
|
|
fn SetupVisibilityBounds Obj =
|
|
(
|
|
EndFrame = AnimationRange.End.Frame as integer
|
|
StartFrame = AnimationRange.Start.Frame as integer
|
|
|
|
--Get the bounds of the object at the start and end of the animation
|
|
StartBounds
|
|
at Time StartFrame
|
|
(
|
|
StartBounds = NodeGetBoundingBox Obj (Matrix3 1)
|
|
)
|
|
|
|
EndBounds
|
|
at Time EndFrame
|
|
(
|
|
EndBounds = NodeGetBoundingBox Obj (Matrix3 1)
|
|
)
|
|
|
|
--Get the full bounds of the animation from start to finish
|
|
FullBounds = #([0,0,0],[0,0,0])
|
|
|
|
if StartBounds[1].x < EndBounds[1].x then FullBounds[1].x = StartBounds[1].x else FullBounds[1].x = EndBounds[1].x
|
|
if StartBounds[1].y < EndBounds[1].y then FullBounds[1].y = StartBounds[1].y else FullBounds[1].y = EndBounds[1].y
|
|
if StartBounds[1].z < EndBounds[1].z then FullBounds[1].z = StartBounds[1].z else FullBounds[1].z = EndBounds[1].z
|
|
|
|
if StartBounds[2].x > EndBounds[2].x then FullBounds[2].x = StartBounds[2].x else FullBounds[2].x = EndBounds[2].x
|
|
if StartBounds[2].y > EndBounds[2].y then FullBounds[2].y = StartBounds[2].y else FullBounds[2].y = EndBounds[2].y
|
|
if StartBounds[2].z > EndBounds[2].z then FullBounds[2].z = StartBounds[2].z else FullBounds[2].z = EndBounds[2].z
|
|
|
|
--Setup a new RsVisibilityBound object with the collected bounds.
|
|
ObjRsVisBound = RsVisibilityBound Name:(Obj.Name + "_BOUNDS") Parent:Obj AutoUpdate:False UpdateAnim:False WireColor:Red
|
|
ObjRsVisBound.BoxMin = FullBounds[1] - Obj.Pivot
|
|
ObjRsVisBound.BoxMax = FullBounds[2] - Obj.Pivot
|
|
ObjRsVisBound.Transform = Obj.Transform
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Sets up the attributes for the object to be correct for a stated anim
|
|
*/---------------------------------------------------
|
|
fn SetupAttributes Obj StatedAnimName =
|
|
(
|
|
SetAttr Obj StatedAnimGroupIdx StatedAnimName
|
|
SetAttr Obj TXDIdx StatedAnimName
|
|
SetAttr Obj StatedAnimStateIdx "Animation"
|
|
SetAttr Obj HasAnimIdx True
|
|
SetAttr Obj AuthoredOrientsIdx True
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Takes the passed object and makes a SkinnedObject from it
|
|
*/---------------------------------------------------
|
|
fn CreateSkinnedObject Obj =
|
|
(
|
|
ReturnSkinnedObject = SkinnedObject()
|
|
|
|
--If the object has skinning then processes it as such otherwise we will assume that the object has some animation
|
|
if( HasSkinning Obj ) then
|
|
(
|
|
ResumeEditing()
|
|
ReturnSkinnedObject.CreateFromSkinnedGeometry Obj
|
|
SuspendEditing()
|
|
return ReturnSkinnedObject
|
|
)
|
|
else
|
|
(
|
|
ReturnSkinnedObject.CreateFromAnimatedGeometry Obj
|
|
return ReturnSkinnedObject
|
|
)
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Returns the anim state attribute of the passed object or undefined if invalid object or attribute not set
|
|
*/---------------------------------------------------
|
|
fn GetState Obj =
|
|
(
|
|
try
|
|
(
|
|
StateIdx = GetAttrIndex (GetAttrClass Obj) "animState"
|
|
Return (GetAttr Obj StateIdx)
|
|
)
|
|
catch
|
|
(
|
|
Return Undefined
|
|
)
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Sets the anim state of the passed object. Should only be passed the named values defined at top; #Animation #Start #End
|
|
*/---------------------------------------------------
|
|
fn SetState Obj State =
|
|
(
|
|
try
|
|
(
|
|
StateIdx = GetAttrIndex (GetAttrClass Obj) "animState"
|
|
State = State as String
|
|
SetAttr Obj StateIdx State
|
|
)
|
|
catch()
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Returns the anim group attribute of the passed object or undefined if invalid object or attribute not set
|
|
*/---------------------------------------------------
|
|
fn GetGroup Obj =
|
|
(
|
|
try
|
|
(
|
|
GroupIdx = GetAttrIndex (GetAttrClass Obj) "groupName"
|
|
return (GetAttr Obj GroupIdx)
|
|
)
|
|
catch
|
|
(
|
|
return Undefined
|
|
)
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Sets the anim group of the passed object.
|
|
*/---------------------------------------------------
|
|
fn SetGroup Obj GroupName =
|
|
(
|
|
try
|
|
(
|
|
StateIdx = GetAttrIndex (GetAttrClass Obj) "groupName"
|
|
SetAttr Obj StateIdx GroupName
|
|
)
|
|
catch()
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Returns an array of all the anim groups in the scene
|
|
*/---------------------------------------------------
|
|
fn GetGroupArray =
|
|
(
|
|
GroupArray = #()
|
|
for Obj in Objects do
|
|
(
|
|
CurrentGroup = GetGroup Obj
|
|
if CurrentGroup != Undefined and CurrentGroup != "DEFAULT" then
|
|
(
|
|
AppendIfUnique GroupArray CurrentGroup
|
|
)
|
|
)
|
|
return GroupArray
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Returns an array of objects that belong to a specific group
|
|
*/---------------------------------------------------
|
|
fn GetGroupObjectArray GroupName =
|
|
(
|
|
if(GroupName == Undefined) then return #()
|
|
GroupObjectArray = for Obj in Objects where GetGroup Obj == GroupName collect Obj
|
|
return GroupObjectArray
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Sets the redraw state of the command panel
|
|
*/---------------------------------------------------
|
|
fn SetCommandPanelRedraw State =
|
|
(
|
|
Args = 0
|
|
if( State == on ) then Args = 1
|
|
|
|
WM_SETREDRAW=0xB
|
|
CommandHWND = (Windows.GetChildHWND #Max "Command Panel")
|
|
Windows.Sendmessage (CommandHWND[1]) WM_SETREDRAW Args 0
|
|
),
|
|
|
|
/*---------------------------------------------------
|
|
Creates a stated animation by using the selection set
|
|
*/---------------------------------------------------
|
|
fn CreateStatedAnim TargetSelectionSet ProgressFN =
|
|
(
|
|
ResumeEditing()
|
|
|
|
--ClearNodeSelection redraw:False
|
|
SourceNodeArray = Objects as Array -- RemoveNonDependantNodes (SelectionSetToArray TargetSelectionSet)
|
|
|
|
--Log the time whole operation takes
|
|
StartTime = TimeStamp()
|
|
StatedAnimName = TargetSelectionSet.Name
|
|
Root = GetSelectionSetRoot TargetSelectionSet
|
|
|
|
--Close the material editor as this can really slow things down
|
|
MatEditor.Close()
|
|
|
|
--Set the slider position to be the beginning of the timeline as our datum
|
|
SliderTime = AnimationRange.Start
|
|
SkinnedObjectArray = #()
|
|
|
|
--Disable redraw and undo and begin the process of creating the stated animation
|
|
with Redraw off, undo off
|
|
(
|
|
--Progress variables
|
|
ProgressValue = 0.0
|
|
ProgressStatus = ""
|
|
|
|
--Suspend modifier panel refreshes while we collect information and group objects
|
|
SuspendEditing()
|
|
|
|
--Process each of the objects creating a skinned object for each.
|
|
Format "Collecting data.\n"
|
|
Format "-----------------------------------------\n"
|
|
SkinnedObjectArray = #()
|
|
ProgressIterator = 100.0 / TargetSelectionSet.Count
|
|
for Obj in TargetSelectionSet do
|
|
(
|
|
--Set progress
|
|
ProgressValue += ProgressIterator
|
|
ProgressStatus = "Collecting data: " + Obj.Name
|
|
ProgressFN ProgressValue ProgressStatus
|
|
|
|
if( IsValidObject Obj ) then
|
|
(
|
|
CurrentSkinnedObject = CreateSkinnedObject Obj
|
|
Append SkinnedObjectArray CurrentSkinnedObject
|
|
)
|
|
)
|
|
|
|
Format "Grouping objects. Phase 1.\n"
|
|
Format "-----------------------------------------\n"
|
|
--Group the skinned objects into sets
|
|
ProgressValue = 0.0
|
|
ProgressIterator = 100.0 / SkinnedObjectArray.Count
|
|
ProgressStatus = "Grouping objects: Phase 1..."
|
|
|
|
--Create our organiser and add an SOData object for each SkinnedObject
|
|
SOOrganiser = DotNetObject "StatedAnim.SOOrganiser"
|
|
for SubSkinnedObject in SkinnedObjectArray do
|
|
(
|
|
ProgressValue += ProgressIterator
|
|
ProgressFN ProgressValue ProgressStatus
|
|
|
|
--Create our SOData object. Collects face, vert, bone and material information
|
|
CurrentSOData = SOData.CollectData SubSkinnedObject
|
|
SOOrganiser.SODataList.Add CurrentSOData
|
|
)
|
|
|
|
(DotNetClass "StatedAnim.SOOrganiser").Write @"X:\gta5\tools\wildwest\script\3dsMax\VFX\Test.xml" SOOrganiser
|
|
--Process what we have collected
|
|
SODataArray = SOOrganiser.Process()
|
|
|
|
Format "Grouping objects. Phase 2...\n"
|
|
Format "-----------------------------------------\n"
|
|
ProgressValue = 0.0
|
|
ProgressIterator = 100.0 / SODataArray.Count
|
|
ProgressStatus = "Grouping objects. Phase 2..."
|
|
|
|
--Now we have grouped our SkinnedObjects up we must merge them together
|
|
RootSkinnedObjectArray = #()
|
|
for SOData in SODataArray do
|
|
(
|
|
--Set progress
|
|
ProgressValue += ProgressIterator
|
|
ProgressFN ProgressValue ProgressStatus
|
|
|
|
--Create our root SkinnedObject for this SODataSet
|
|
RootSkinnedObject = SkinnedObject()
|
|
RootSkinnedObject.SetTransform Root
|
|
RootSkinnedObject.SetName( UniqueName (StatedAnimName + "_") NumDigits:2 )
|
|
|
|
--Create our new material for this SkinnedObject
|
|
NewMaterial = MultiMaterial NumSubs:1 Name:RootSkinnedObject.Name
|
|
NewMaterial[1] = CreateInvalidMaterial()
|
|
RootSkinnedObject.SetMaterial NewMaterial
|
|
|
|
for CurrentGUID in SOData.SourceGUIDArray do
|
|
(
|
|
--Get the relevant skinned object by GUID
|
|
MatchingSkinnedObject
|
|
for SubSkinnedObject in SkinnedObjectArray do
|
|
(
|
|
if( SubSkinnedObject.GUID == CurrentGUID ) then
|
|
(
|
|
MatchingSkinnedObject = SubSkinnedObject
|
|
exit
|
|
)
|
|
)
|
|
|
|
--Check to make sure the SkinnedObject we are about to add has some data
|
|
if( MatchingSkinnedObject.GetVertCount() != 0 ) then
|
|
(
|
|
RootSkinnedObject.MergeSkinnedObject MatchingSkinnedObject
|
|
)
|
|
else
|
|
(
|
|
RootSkinnedObject.Destroy()
|
|
)
|
|
|
|
)
|
|
|
|
--Add this RootSkinnedObject to the array and move on
|
|
Append RootSkinnedObjectArray RootSkinnedObject
|
|
)
|
|
|
|
Format "Merging objects.\n"
|
|
Format "-----------------------------------------\n"
|
|
--Skin each of the created SkinnedObjects
|
|
ProgressValue = 0.0
|
|
ProgressIterator = 100.0 / RootSkinnedObjectArray.Count
|
|
ProgressStatus = "Merging objects..."
|
|
for SubSkinnedObject in RootSkinnedObjectArray do
|
|
(
|
|
ProgressValue += ProgressIterator
|
|
ProgressFN ProgressValue ProgressStatus
|
|
|
|
SubSkinnedObject.CollapseMeshCache()
|
|
SubSkinnedObject.MeshNode.Material = MultiMat_Clone SubSkinnedObject.MeshNode.Material
|
|
)
|
|
|
|
--Resume editing for skinning phase.
|
|
ResumeEditing()
|
|
|
|
Format "Skinning objects.\n"
|
|
Format "-----------------------------------------\n"
|
|
--Skin each of the created SkinnedObjects
|
|
ProgressValue = 0.0
|
|
ProgressIterator = 100.0 / RootSkinnedObjectArray.Count
|
|
for SubSkinnedObject in RootSkinnedObjectArray do
|
|
(
|
|
ProgressValue += ProgressIterator
|
|
ProgressStatus = "Skinning: " + SubSkinnedObject.Name
|
|
ProgressFN ProgressValue ProgressStatus
|
|
|
|
SubSkinnedObject.SkinObject()
|
|
)
|
|
|
|
ClearSelection()
|
|
|
|
Format "Copying ParticleHelpers and Lights.\n"
|
|
Format "-----------------------------------------\n"
|
|
ClonedNodeList = #()
|
|
for SubSkinnedObject in RootSkinnedObjectArray do
|
|
(
|
|
for SubBone in SubSkinnedObject.BoneHierarchy.BoneArray do
|
|
(
|
|
for ChildNode in SubBone.ChildNodeArray do
|
|
(
|
|
if ( SuperClassOf ChildNode == Light ) or ( ClassOf ChildNode == RsRageParticleHelper ) then
|
|
(
|
|
if( FindItem ClonedNodeList ChildNode == 0 ) then
|
|
(
|
|
Append ClonedNodeList ChildNode
|
|
ChildNodeCopy = Copy ChildNode
|
|
ChildNodeCopy.Parent = SubBone.Node
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
|
|
Format "Setting up visibility bounds and attributes.\n"
|
|
Format "-----------------------------------------\n"
|
|
for SubSkinnedObject in RootSkinnedObjectArray do
|
|
(
|
|
SetupAttributes SubSkinnedObject.MeshNode StatedAnimName
|
|
SetupVisibilityBounds SubSkinnedObject.MeshNode
|
|
)
|
|
|
|
Format "Removing source nodes.\n"
|
|
Format "-----------------------------------------\n"
|
|
|
|
--Collect our objects we want to not delete
|
|
NoDeleteArray = #()
|
|
if( SelectionSets["No_Delete"] != undefined ) then
|
|
(
|
|
NoDeleteArray = for Obj in SelectionSets["No_Delete"] collect Obj
|
|
)
|
|
|
|
NodeDeleteArray = #()
|
|
for Obj in SourceNodeArray do
|
|
(
|
|
if( IsValidNode Obj ) and ( FindItem NoDeleteArray Obj == 0 ) then
|
|
(
|
|
Append NodeDeleteArray Obj
|
|
)
|
|
)
|
|
Delete NodeDeleteArray
|
|
|
|
Format "Creating proxy object.\n"
|
|
Format "-----------------------------------------\n"
|
|
CreateProxy StatedAnimName
|
|
)
|
|
SetCommandPanelRedraw on
|
|
|
|
EndTime = TimeStamp()
|
|
Format "Time (In milliseconds): %\n" (EndTime - StartTime)
|
|
)
|
|
)
|
|
|
|
StatedAnimCore = _StatedAnimCore_()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|