[Closed] Replace bone object in skin modifier maxscript
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)
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
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.
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
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?
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
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