--remapSkinning.ms --March 2013 --Matt Rennie --tool to remap skinning from bones in initialBonesToRemove array to bones which remain present in the skin modifier --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- escapeEnable = true -- arrayOfBones = #( --list of all bones in skin modifier this can eventually be queried via skin mod -- $A, -- $B, -- $C, -- $D, -- $E, -- $F, -- $G -- ) boneNames = #() initialBonesToRemove = # --initial list of bones to remove ( $F, $C, $E ) bonesToRemove = #() --dynamically generated array of all bones which need to be removed (if we were to remove bone C then its children will need removing also boneMapping = #( --dynamically generated 2d array. id1 = initial bone, id2 = bone to remap to ) newVertData = #() --dynamically generated array if final vert data. Id1 = vertId, id2 = bone index, id3 = weightValue --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- fn reNomalize vertexId skinMod = ( skinOps.unNormalizeVertex skinMod vertexID false ) --if the vert has already been weighted tyhen the denormalizing will nacker up so we have to be a bit funky here! fn deNormalize vertexID skinMod = ( skinOps.unNormalizeVertex skinMod vertexID false skinOps.SetVertexWeights skinMod vertexID 1 1.0 skinOps.unNormalizeVertex skinMod vertexID true skinOps.SetVertexWeights skinMod vertexID 1 0.0 ) fn applyReMappedSkinning obj vertData = ( skinMod = obj.modifiers[#Skin] vertCount = undefined if classof obj == Editable_mesh then ( vertCount = meshop.getNumVerts obj.baseobject ) else ( vertCount = polyOp.getNumVerts obj.baseobject ) --unnormalize weights for vertexID = 1 to vertCount do ( --now set all weights to 1 on the 1st bone skinOps.SetVertexWeights skinMod vertexId 1 1 deNormalize vertexID skinMod --now set the weights to 0 skinOps.setVertexWeights skinMod vertexId 1 0 weightCount = skinOps.GetVertexWeightCount skinMod vertexId --now loop via the weightCount for wt = 1 to weightCount do ( for vtw = 1 to vertData.count do ( if vertData[vtw][1] == vertexId do ( --now we need to find vertexId in the newVertData array boneArray = #() WeightArray = #() for val = 1 to newVertData.count do ( if newVertData[val][1] == vertexId do ( append boneArray newVertData[val][2] append weightArray newVertData[val][3] ) ) if weightArray.count != 0 do ( skinOps.SetVertexWeights skinMod vertexId boneArray weightArray ) ) ) ) --now renormalize the weights reNomalize vertexId skinMod ) print "Reskin complete." ) fn compareSubArrays first second count = ( result = true if first.count != second.count then ( result = false ) else ( for i = 1 to first.count do ( if first[i] != second[i] do ( result = false ) ) ) result ) fn conCatVertData vertData = ( newVertData = #() for vert = 1 to vertData.count do ( --we need to find any duplicates of index 1 and index 2 currV = vertData[vert][1] currentBoneId = vertData[vert][2] for i = 1 to vertData.count do ( if i != vert do ( thisV = vertData[i][1] thisBoneId = vertData[i][2] if currV == thisV do ( if thisBoneId == currentBoneId do ( --ok we need to concat this data currentWeight = vertData[vert][3] newWeight = (vertData[i][3] + currentWeight) tmpArr = #(thisV, currentBoneId, newWeight) --print ("Appending "+(tmpArr as string)) appendIfUnique newVertData tmpArr ) ) ) ) ) -- for i = 1 to newVertData.count do -- ( -- print ("i:"+(i as string)+" ! "+(newVertData[i][1] as string)+", "+(newVertData[i][2] as string)+", "+(newVertData[i][3] as string)) -- ) --print ("newVertData.count:"+(newVertData.count as string)) --this is a hacky way of ensuring we dont get array duplicates for nv = 1 to newVertData.count do ( for dV = newVertData.count to nv+1 by -1 do ( if dV != nv do --check that we're not the same array element ( test = (compareSubArrays newVertData[nv] newVertData[dV] 3) --print test if test do ( deleteItem newVertData dv --format "Deleting %\n" dv ) ) ) ) --print ("newVertData.count: "+(newVertData.count as string)) -- break() --now we've concatted everythign we now need to parse through the vertData and copy over any items which dont have the first 2 indexes matching the newVertData for vert = 1 to vertData.count do ( currV = vertData[vert][1] currB = vertData[vert][2] --print ("currV: "+(currV as string)) --print ("currB: "+(currB as string)) newVertDataCount = newVertData.count -- for newV = 1 to newVertDataCount do -- ( -- if currV != newVertData[newV][1] do -- ( -- if currB != newVertData[newV][2] do -- ( -- tmpArr = #(currV, currB, vertData[vert][3]) -- appendIfUnique newVertData tmpArr -- ) -- ) -- ) for newV = 1 to newVertDataCount do ( test = (compareSubArrays newVertData[newV] VertData[vert] 2) if test do ( appendIfUnique newVertData vertData[vert] ) ) --print ("newvertData.count: "+(newvertData.count as string)) ) ) fn reMapSkinning obj = ( skinMod = obj.modifiers[#Skin] vertCount = undefined if classof obj == Editable_mesh then ( vertCount = meshop.getNumVerts obj.baseobject ) else ( vertCount = polyOp.getNumVerts obj.baseobject ) -- weightCount = skinOps.GetVertexWeightCount skinMod --now loop through each vert in the mesh and query the weight vertData = #() --multi dimensional array of vertIndex, boneIndex, boneWeight for vert = 1 to vertCount do ( weightCount = skinOps.GetVertexWeightCount skinMod vert --now loop via the weightCount for wt = 1 to weightCount do ( boneItem = skinOps.GetVertexWeightBoneID skinMod vert wt --gets the index of the bone wt boneWeight = skinOps.GetVertexWeight skinMod vert wt --gets the weight value applied to bone wt tmpArr = #(vert, boneItem, boneWeight) append vertData tmpArr ) ) --now we have a full list of verts & their weighting, we need to look through boneNames and then also the bones to remove dynamic array and for any matches, --we then need to find a match for that in the id 2 of vertData and remap that weight for vert = 1 to vertData.count do ( --now find the index of the current boneItem in index 2 currentVertBoneIndex = vertData[vert][2] foundMatch = undefined --need to find the index in the main bone array that matches boneMapping[currentVertBoneIndex][2] foundmatch = findItem boneNames boneMapping[currentVertBoneIndex][2] if foundMatch == undefined do ( break()) --now we have found the node we need to rpelace with we will change the value in vertData[vert][2] to match if vertData[vert][2] != foundMatch do ( fromBone = boneNames[vertData[vert][2]] toBone = boneNames[foundMatch] print ("For vert "+(currentVertBoneIndex as string)+" replacing "+(fromBone.name as string)+" with "+(toBone.name as string)) vertData[vert][2] = foundMatch ) ) print ("vertData.count: "+(vertData.count as string)) conCatVertData vertData applyReMappedSkinning obj vertData ) fn buildBonesToRemove bonesToLose = ( --go through the initialBonesToRemove array and add all of their children for b in initialBonesToRemove do ( boneChildren = b.children appendIfUnique bonesToRemove b for c in boneChildren do ( appendIfUnique bonesToRemove c ) ) format ("Initial removal nodes:"+"\n") for i in initialBonesToRemove do ( format (i.name+"\n") ) format ("Total removal nodes:"+"\n") for b in bonesToRemove do ( format (b.name+"\n") ) format ("---------------"+"\n") ) fn boneRemap = ( --first off build the total list of all nodes we will need to remove buildBonesToRemove bonesToLose for i in boneNames do ( parBone = i.parent if parBone != undefined then ( --ok now we have a parent bone we need to see if i is in bonesToRemove foundToRemove = finditem bonesToRemove i if foundToRemove != 0 then ( --ok so we need to remove bone 'i' --now wee need to iterate up through its parents until we find a bone that is not to be removed isValid = false boneToUse = parBone while isValid == false do ( parValid = findItem bonesToRemove parBone if parValid != 0 then --ok this parent is one we need to remove so we need to go higher ( parBone = parBone.parent isValid = false ) else ( --ok we can stick weights to this bone boneToUse = parBone isValid = true ) ) tmpArr = #(i, boneToUse) print ((i.name)+" mapped to "+(boneToUse.name)+" <-- CHANGE") append boneMapping tmpArr ) else ( tmpArr = #(i, i) print ((i.name)+" mapped to "+(i.name)) append boneMapping tmpArr ) ) else ( tmpArr = #(i, i) print ((i.name)+" mapped to "+(i.name)) append boneMapping tmpArr ) ) ) fn skinXfer obj = ( skinMod = obj.modifiers[#Skin] --first off build a list of all the bones in the skin modifier totalSkinBones = skinOps.GetNumberBones skinMod boneNames = #() boneNamesTmp = #() select obj max modify mode modPanel.setCurrentObject $.modifiers[#Skin] for i = 1 to totalSkinBones do ( thisBone = skinops.GetBoneName skinMod i 1 append boneNamesTMP thisBone ) --now remap from bone names to bone objects for i in boneNamesTmp do ( tb = getNodeByName i append boneNames tb ) --now do the bone remapping boneRemap() -- for f = 1 to boneMapping.count do -- ( -- print ((f as string)+": "+(boneMapping[f] as string)) -- ) --now we have the bone remapping done we need to transfer the skinning reMapSkinning obj ) if ((skinReMapGUI != undefined) and (skinReMapGUI.isDisplayed)) do (destroyDialog skinReMapGUI) ------------------------------------------------------------------------------------------------------------------------- rollout skinReMapGUI "SkinRemap" ( button btnSkinRemap "Remap Skinning on selected" on btnSkinRemap pressed do ( sela = selection as array if sela.count == 1 then ( skinXfer $ ) else ( messagebox ("Please run with only one mesh selected.") beep:true ) ) ) CreateDialog skinReMapGUI