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