Notifications
Clear all

[Closed] Replace bone object in skin modifier maxscript

i can’t unzip it.

edited. ok. i could…

well. my tool works as expected but you have a problem with how your skins in the scene initialized. it seems like some 3rd party tool was used for skinning.

normaly when you delete skin modifier the mesh has to remain in the same deformation state. also if you check OFF a “Always Deform” checkbox in skin modifier your mesh has to go to ‘bind’ pose, but if check it back to ON it has to go back in deformed state. Your skin meshes behave wrong.

there is a way how to fix it:

right click on Skin Modifier

copy it

collapse stack

paste modifier back (it will screw the mesh. it’s ok)

set “Always Deform” to OFF and back to ON

skin is fixed now

and my tool works right (except some bones that have duplicated names or don’t have a pair)

1 Reply
(@patan77)
Joined: 11 months ago

Posts: 0

Its was skinned by just the regular skin modifier, its probably messing up because it was skinned on CAT when it was not in animation mode or something.

I did the collapse skin fix you said, and now actually my script seems to work(even tho I don’t do anything with the skin weights), though I still cant get yours to really work, you say “except some bones that have duplicated names or don’t have a pair” but there should not be any duplicated names.

(I think your code should work and are far better than my code, but I don’t know, it just don’t want to work fully for me.)

Download updated max scene with skin collapse fix and renamed _new bones.
http://www.patan77.com/download/Skin_CAT_bone_replace_scriptScene_updated_skin_fix.zip

(My really slow code) [works]

	if Replace_Bone != undefined then
	(
		DestroyDialog Replace_Bone
	)
	

	rollout Replace_Bone "Replace Bone"
	(
		global array_skin_objects = #()
		global array_new_bone_objects = #()
		global ary_old_bones_temp = #()
		
		Button btn_select_skin "Add selected skin objects" tooltip:"rightclick to clear"
		Button btn_select_new_bone "Add selected objects to new bones" tooltip:"rightclick to clear"
		Button btn_run_bone_script "RUN script"
		
		
		on btn_select_skin pressed do
		(
			if $ != undefined then
			(
				array_skin_objects = (selection as array)
				Replace_Bone.btn_select_skin.text = "Right click to clear"
				print "added skin objects"
			)
			
		)
		
		
		on btn_select_skin rightclick do
		(
			array_skin_objects = #()
			Replace_Bone.btn_select_skin.text = "Add selected skin objects"
			print "skin objects cleared"
		)
		
		
			on btn_select_new_bone pressed do
		(
			if $ != undefined then
			(
				array_new_bone_objects = (selection as array)
				Replace_Bone.btn_select_new_bone.text = "Right click to clear"
				print "added new bone objects "
			)
		)
		
		
			on btn_select_new_bone rightclick do
		(
			array_new_bone_objects = #()
			Replace_Bone.btn_select_new_bone.text = "Add selected objects to new bones"
			print "bone objects cleared"
		)
		
		
			on btn_run_bone_script pressed do
		(
			if (((array_skin_objects as string) != "#()") and (array_new_bone_objects as string) != "#()" )then
			(
				for k = 1 to array_skin_objects.count do
				(
					if (ClassOf array_skin_objects[k].modifiers[1]) == skin then
					(
						select array_skin_objects[k]
						max modify mode
						modPanel.setCurrentObject array_skin_objects[k].modifiers[1] 		
						ary_old_bones_temp = #()
						
						for i = 1 to (skinOps.getNumberBones $.skin) do
						(
							select (getnodebyname (skinOps.getBoneName $.skin i 0))					
							ary_old_bones_temp = ary_old_bones_temp + (selection as array)			
							select array_skin_objects[k]		
						)

						for j = 1 to ary_old_bones_temp.count  do
						(
							for ja = 1 to ary_old_bones_temp.count  do
							(
								if (skinOps.getBoneName $.skin ja 1) !=  (ary_old_bones_temp[j].name as string) + "_new" then
								
								(
									for ia = 1 to array_new_bone_objects.count do
									(
										if ((array_new_bone_objects[ia].name) as string) == ((skinOps.getBoneName $.skin ja 1) + "_new") then
										(
											skinOps.removebone $.skin ja
											skinOps.addbone $.skin array_new_bone_objects[ia] 0
										)
									)

								)
							)

						)
					)
				)	
				print "run skript"
			)
			else
			(	
				print"add both skin and bone objects before pressing run" 	
			)
		)
	)
	
	createdialog Replace_Bone width:200 height:100

i made a little fix to my code (i am re-baking skin vertices now)… using the updated scene posted above run my script.
To do an x-change just simply press “X-Change” button with Ctrl. The tool will find all skin meshes itself.


/********** Skin Bone X-Change by denisT *****************/

global XChangeSkinDialogPos = unsupplied
try (destroydialog XChangeSkinDialog) catch()

rollout XChangeSkinDialog "X-Change Skin Bones" width:200
(
	button xchange_bt "X-Change" width:190 align:#right offset:[8,0] tooltip:"X-Change Skin Bones
  + Ctrl	- Fix All Skins"
	label xchange_lb "nodes xchanged:" align:#left across:2
	label xchange_nm "0" align:#left offset:[0,0]
	
	fn getSkin node = if isvalidnode node do
	(
		local sk
		for m in node.modifiers while sk == undefined where iskindof m Skin do sk = m
		sk
	)
	fn collectSkinBones sk = 
	(
		for b in (refs.dependson sk) where isvalidnode b collect b
	)	
	fn xchangeSkinBone sk source target remove:off add:on = 
	(
		local result = off
		
		if sk == modpanel.getcurrentobject() and iskindof sk Skin do
		(
			bones = collectSkinBones sk
			if (sid = finditem bones source) > 0 do
			(
				if finditem bones target == 0 and add do
				(
					skinops.addbone sk target 1
					bones = collectSkinBones sk
					sid = finditem bones source
				)
				if (tid = finditem bones target) > 0 do
				(
					verts = #{1..skinops.getnumbervertices sk}
					skinops.selectvertices sk verts
					skinops.bakeselectedverts sk
					
					for v in verts do
					(
						bb = #()
						ww = #()
						for k=1 to skinops.getvertexweightcount sk v do
						(
							append bb (skinops.getvertexweightboneid sk v k)
							append ww (skinops.getvertexweight sk v k)
						)
						if (k = finditem bb sid) > 0 do 
						(
							bb[k] = tid
							skinops.replacevertexweights sk v tid 1.0
							skinops.setvertexweights sk v bb ww
							
							result = on
						)
					)
					if remove do skinops.removebone sk sid 
				)
			)
		)
		result
	)
	fn getTargetPair node = 
	(
		getnodebyname (node.name + "_new") 
	)
	fn xchangeSkin node = if (sk = getSkin node) == undefined then 0 else
	(
		select node
		modpanel.setcurrentobject sk
		verts = #{1..skinops.getnumbervertices sk}
		skinops.selectvertices sk verts
		skinops.bakeselectedverts sk

		sk.always_deform = off
		sk.always_deform = on

		count = 0
		bones = collectSkinBones sk
		for source in bones where (target = getTargetPair source) != undefined do
		(
			if (act = xchangeSkinBone sk source target remove:on add:on) do count += 1 
		)
		count
	)

	on xchange_bt pressed do undo "X-Change" on 
	(
		count = 0
		max modify mode
		if keyboard.controlpressed then
		(
			for node in geometry where getSkin node != undefined do count += xchangeSkin node
		)
		else 
		(
			node = selection[1]
			if getSkin node != undefined do count += xchangeSkin node
		)
		xchange_nm.text = count as string
		ok
	)
	
	on XChangeSkinDialog close do
	(
		XChangeSkinDialogPos = getdialogpos XChangeSkinDialog
	)
)

createdialog XChangeSkinDialog pos:XChangeSkinDialogPos
5 Replies
(@patan77)
Joined: 11 months ago

Posts: 0

Cool, but there is a “bug” I think at line ~68 with

if remove do skinops.removebone sk sid 

, because if you run the script “as is” with the scene, and check the list of bones after the script have run, it don’t say _new on all, while you run the code you can see it adds all the new bones correctly, but then it deletes the “old” bones incorrectly and deletes some that’s called _new, I think its cause of when you remove a bone in the list it auto sort it self by name and then the index “sid” of the next bone that its going to remove isn’t the correct bone anymore.

But I’m not 100% sure that’s the issue.

(@denist)
Joined: 11 months ago

Posts: 0

since max 2011 skin modifier and skinops messed up with bone IDs. i completely rewrote this and don’t use built-in anymore.

but i’ve tested the code i posted. it looks like working right.
btw… what version of max do you use? since max 2014 autodesk fixed some bugs and almost solved skin bone id issues

(@patan77)
Joined: 11 months ago

Posts: 0

I’m using 3ds max 2013.
So if you run your script with the “updated simple test scene ” that have the _new names and after you have run it, delete the CAT objects in the scene, then the skin still works?

(@denist)
Joined: 11 months ago

Posts: 0

i’m with max 2014. my code works right for me. you have screwed bone id system in max 2013. i’ve a workaround. but i will not be able to post it sooner than next week

(@patan77)
Joined: 11 months ago

Posts: 0

Ok, I see.
Will be interesting to see how you workaround it, but its not rush, I did just run my script from UPDATE #2 on my main scene, and it looks like it did the job.

Btw, thanks for the help.

UPDATE: my “slow code” optimized a little bit: [~3x faster than original code]

if Replace_Bone != undefined then
	(
		DestroyDialog Replace_Bone
	)
	

	rollout Replace_Bone "Replace Bone"
	(
		global array_skin_objects = #()
		global array_new_bone_objects = #()
		global ary_old_bones_temp = #()
		
		Button btn_select_skin "Add selected skin objects" tooltip:"rightclick to clear"
		Button btn_select_new_bone "Add selected objects to new bones" tooltip:"rightclick to clear"
		Button btn_run_bone_script "RUN script"
		
		
		on btn_select_skin pressed do
		(
			if $ != undefined then
			(
				array_skin_objects = (selection as array)
				Replace_Bone.btn_select_skin.text = "Right click to clear"
				print "added skin objects"
			)
			
		)
		
		
		on btn_select_skin rightclick do
		(
			array_skin_objects = #()
			Replace_Bone.btn_select_skin.text = "Add selected skin objects"
			print "skin objects cleared"
		)
		
		
			on btn_select_new_bone pressed do
		(
			if $ != undefined then
			(
				array_new_bone_objects = (selection as array)
				Replace_Bone.btn_select_new_bone.text = "Right click to clear"
				print "added new bone objects "
			)
		)
		
		
			on btn_select_new_bone rightclick do
		(
			array_new_bone_objects = #()
			Replace_Bone.btn_select_new_bone.text = "Add selected objects to new bones"
			print "bone objects cleared"
		)
		
		
			on btn_run_bone_script pressed do
		(
			if (((array_skin_objects as string) != "#()") and (array_new_bone_objects as string) != "#()" )then
			(
				for k = 1 to array_skin_objects.count do
				(
					if (ClassOf array_skin_objects[k].modifiers[1]) == skin then
					(
						select array_skin_objects[k]
						max modify mode
						modPanel.setCurrentObject array_skin_objects[k].modifiers[1] 		
						ary_old_bones_temp = #()
						ary_old_bones_temp = (ary_old_bones_temp + (getnodebyname (skinOps.getBoneName $.skin 1 0)))
							
						for j = 1 to ary_old_bones_temp.count  do
						(
							for ja = 1 to ary_old_bones_temp.count  do
							(
								if (skinOps.getBoneName $.skin ja 1) !=  (ary_old_bones_temp[j].name as string) + "_new" then
								
								(
									for ia = 1 to array_new_bone_objects.count do
									(
										if ((array_new_bone_objects[ia].name) as string) == ((skinOps.getBoneName $.skin ja 1) + "_new") then
										(
											skinOps.removebone $.skin ja
											skinOps.addbone $.skin array_new_bone_objects[ia] 0
										)
									)

								)
							)

						)
					)
				)	
				print "Done"
			)
			else
			(	
				print"add both skin and bone objects before pressing run" 	
			)
		)
	)
	
	createdialog Replace_Bone width:200 height:100

EDIT:
UPDATE #2: my “slow code” optimized a little bit more : [~5x faster than original code]

if Replace_Bone != undefined then
	(
		DestroyDialog Replace_Bone
	)
	

	rollout Replace_Bone "Replace Bone"
	(
		global array_skin_objects = #()
		global array_new_bone_objects = #()
		global ary_old_bones_temp = #()
		
		Button btn_select_skin "Add selected skin objects" tooltip:"rightclick to clear"
		Button btn_select_new_bone "Add selected objects to new bones" tooltip:"rightclick to clear"
		Button btn_run_bone_script "RUN script"
		
		
		on btn_select_skin pressed do
		(
			if $ != undefined then
			(
				array_skin_objects = (selection as array)
				Replace_Bone.btn_select_skin.text = "Right click to clear"
				print "added skin objects"
			)
			
		)
		
		
		on btn_select_skin rightclick do
		(
			array_skin_objects = #()
			Replace_Bone.btn_select_skin.text = "Add selected skin objects"
			print "skin objects cleared"
		)
		
		
			on btn_select_new_bone pressed do
		(
			if $ != undefined then
			(
				array_new_bone_objects = (selection as array)
				Replace_Bone.btn_select_new_bone.text = "Right click to clear"
				print "added new bone objects "
			)
		)
		
		
			on btn_select_new_bone rightclick do
		(
			array_new_bone_objects = #()
			Replace_Bone.btn_select_new_bone.text = "Add selected objects to new bones"
			print "bone objects cleared"
		)
		
		
			on btn_run_bone_script pressed do
		(
			if (((array_skin_objects as string) != "#()") and (array_new_bone_objects as string) != "#()" )then
			(
				for k = 1 to array_skin_objects.count do
				(
					if (ClassOf array_skin_objects[k].modifiers[1]) == skin then
					(
						select array_skin_objects[k]
						max modify mode
						modPanel.setCurrentObject array_skin_objects[k].modifiers[1] 		
						ary_old_bones_temp = #()
						ary_old_bones_temp = (ary_old_bones_temp + (getnodebyname (skinOps.getBoneName $.skin 1 0)))
					
						i3 = false
						i2 = 0
						temp_count_01 = (skinOps.getNumberBones $.skin)
						while i3 == false do
							(
								i2 = ( i2 + 1 )
								if  (skinOps.getBoneName $.skin i2  1 ) !=  ((trimRight (skinOps.getBoneName $.skin i2 1 ) "_new") + "_new") then
								(
									temp_count_01 = (temp_count_01 - 1)
									temp_name = (skinOps.getBoneName $.skin i2 1) 
									skinOps.removebone $.skin i2
									skinOps.addbone  $.skin (getNodebyName (temp_name + "_new")) 0
									i2 = 0 
								)
								if temp_count_01 < 1 then
								(
									i3 = true
								)
							)
						
					)
				)	
				print "run skript"
			)
			else
			(	
				print"add both skin and bone objects before pressing run" 	
			)
		)
	)
	
	createdialog Replace_Bone width:200 height:100
Page 2 / 2