Files
2025-09-29 00:52:08 +02:00

485 lines
12 KiB
Plaintext
Executable File

--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