3DS Max - Maxscript - Task-Automation tool for skin

Hello guys, i have been working on a tool for our animation team. they asked me to help them automate some task that have to be preformed for each character in our game.
The mesh Skin has multiple Ref.Frames, and the goal is to merge all the data to Ref.Frame 0. this is to properly export to our game engine.
here is the manual way of doing it which works 100%.
0. Set time line to frame 0

  1. select mesh
  2. select skin modifier
  3. open weigh-table
  4. copy all vertex values and paste them back into the same places (this is as i was explained forces the Manual Vertex Weighting, in case something was done using Envelopes, the Column M gets “X” marked for all.
  5. Right-click -Copy Skin Modifier,
  6. Collapse Skin Modifier
  7. Paste Previously copied Skin
  8. Skin-Advanced parameters - Set Ref.Frame to 0
  9. Toggle Always Deform on/off (to update the view port)
  10. DONE.- ready for export.

Don’t ask why these are the steps, i had been trying to find out but it was a little challenging, considering lots of people were involved in the process, and i have just joined the team.

What i wrote:

clearlistener()

sliderTime = 0f

theModel = $

vertArray = #()
vertWeightArray = #()
boneIdArray = #()

------------------------------------------------------------
--Create TMP model and append to TMP Layer
tmpModel = copy theModel
tmpModel.name = "tmp_" + theModel.name
tmpLayer = LayerManager.newLayer()
tmpLayer.setname "tmp_Layer"
tmpLayer.addnode tmpModel
----------------------------------------------------------

for vert = 1 to (skinOps.GetNumberVertices tmpModel.modifiers[#skin])  do
	(
		--format ("Vertex % 
")vert
		append vertArray vert
		
 		for bon = 1 to (skinOps.GetVertexWeightCount tmpModel.modifiers[#skin] vert) do
			(	
				boneID = (skinOps.GetVertexWeightBoneID tmpModel.modifiers[#skin] vert bon)
				append boneIdArray boneID
				
				boneName = (skinOps.GetBoneName tmpModel.modifiers[#skin] boneID 0)
				
				VertWeight = (skinOps.GetVertexWeight tmpModel.modifiers[#skin] vert bon)
				append vertWeightArray VertWeight
				
				--skinOps.SetVertexWeights theModel.modifiers[#skin] vert boneID VertWeight	
				format ("Vertex: % | boneID: % | Name: % | Weight: %
")vert boneID boneName VertWeight 
			)
 		format("
")
	)

maxOps.CollapseNodeTo theModel theModel.modifiers.count on
addModifier theModel tmpModel.modifiers[#skin]
	
modPanel.setCurrentObject theModel.modifiers[#Skin]
subobjectlevel = 1

for vert=1 to vertArray.count do (
	
	for bon = 1 to boneIdArray.count do (
	
		skinOps.SetVertexWeights theModel.modifiers[#skin] vert boneIdArray[bon] vertWeightArray[bon]	
		)
	)
theModel.modifiers[#skin].ref_frame = 0
theModel.modifiers[#skin].always_deform = False
theModel.modifiers[#skin].always_deform = True
subobjectlevel = 0

As the result - my new skin after application, doesn’t match to what the manual way is doing, however the steps are all the same, and i can’t seem to figure out what is the problem, if anyone has a tip, i would really appreciate this, thanks!

ok, i think i was able to figure this out, and it is functioning like i need it.

so there were few things that were confusing, and i think i will point them out here, so if anyone is running into a problem similar to mine, this would at least be like a start up…

So the main problem that i had was that after i create a temp-model that holds original skin (copy function) and tried syncing skin-data from it to my original model, after collapsing its skin, vertecies were not properly weighted, not all of them. just those that had 1 bone affecting all others got one bone, and set it to 1 instead of what the values were.

after some research and trouble shooting i figured out that :
in order to apply weighting correctly, i had to manually select the SKIN-Modfifer, set mode to modify panel

max modify mode
modPanel.setCurrentObject skinMod

select a bone

 skinOps.SelectBone targetSkin bnID

get the envelope data transferred from the original skin (from the temp-model)

skinOps.setStartPoint targetSkin bnID (skinOps.getStartPoint sourceSkin bnID)
skinOps.setEndPoint targetSkin bnID (skinOps.getEndPoint sourceSkin bnID)
skinOps.setOuterRadius targetSkin bnID 2 (skinOps.getOuterRadius sourceSkin bnID 2)
skinOps.setInnerRadius targetSkin bnID 2 (skinOps.getInnerRadius sourceSkin bnID 2)
skinOps.setOuterRadius targetSkin bnID 1 (skinOps.getOuterRadius sourceSkin bnID 1)
skinOps.setInnerRadius targetSkin bnID 1 (skinOps.getInnerRadius sourceSkin bnID 1)

and only after that i could properly relocate the weights by selecting a corresponding vertex and applying the weight

skinOps.SelectVertices targetSkin vtx
skinOps.setVertexWeights targetSkin vtx bnID vtxWeight

one other, very important element that i had misinterpreted, is the boneID, i thought, that once the bone is created, and receives its identification within 3dmax, this is how the bone will be called throughout its life, but i was wrong. and in my case, calling a boneID 2, for example, from my TMP model, and trying to locate the same boneID 2 of my original mesh were not referring to the same bone (thus causing my vertecies to get weights from incorrect bones). the BoneID depends on the order in which the bones are presented in the listbox in the SKinModifier.
so what i ended up doing, was scanning all the bones that are applied to the tmp-skin, getting those bone ID (which are regular INTs) and sorting those in array. therefore i was able to scan ALL-Bones in my scene, find the correct one by its name and used the boneID-Array to assemble them in identical order, once i did that - verts were mapped correctly.
here is the sorting that i made:

fn listCurrentBones obj skinMod = (
	setCurrent obj skinMod
	boneIdArray = #()
	currentBonesArray = #()
	boneArray = getAllBones()
	
	for vert = 1 to (skinOps.GetNumberVertices skinMod)  do
		(
			for bn = 1 to (skinOps.GetVertexWeightCount skinMod vert) do
				(	
					bnID = (skinOps.GetVertexWeightBoneID skinMod vert bn)
					bnName = (skinOps.GetBoneName skinMod bnID 0)
					appendIfUnique boneIdArray boneID
				)
		)
	boneIdArray = sort boneIdArray
		
	for bnID in boneIdArray do (
		for theBone in boneArray do (
			if (skinOps.GetBoneName currentSkin bnID 0 == theBone.name) then (
				append currentBonesArray theBone
				)
			)
		)
	
	return currentBonesArray
)

i hope some of you will find this useful and will save some headaches. I f