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

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