[Closed] HELP: Problems applying skin weights via Maxscript fn
Guys, I really could use some help here… this is driving me crazy. My Maxscript dotXSI 3.x file importer is just about complete– the only problem I am having is properly applying the skin weights in the skin modifier.
From the dotXSI file I have read in all of the SI_Envelope templates for a mesh object (that stores skin weights for a mesh against a single bone… thus multiple templates for one mesh object) and stored the data within a custom attribute on the mesh object itself.
This custom attribute is given by:
xsiSkinDataCA = attributes xsiSkinData
(
parameters xsiMeshParams
(
boneList type:#nodeTab tabSizeVariable:true
skinWeights type:#floatTab tabSizeVariable:true
)
)
The trick to storing ALL weight values for ALL bones for a single mesh object is done by:
append meshObj.boneList boneObj --this is growing iteratively
--store the vertex skin weights for the bones affecting this mesh
for n = 1 to numWeightedVertices do (
vertIndex = ( meshObj.boneList.count * meshObj.numverts ) - meshObj.numverts + n
vertexID = readValue inFile as integer + 1
weight = ( readValue inFile as float )/100.0
.
.
.
insertItem weight meshObj.skinWeights ( ( meshObj.boneList.count * meshObj.numverts ) - meshObj.numverts + vertexID )
)
.
.
.
A few lines later in the code block above I add each current boneObj to the skin modifier on the current mesh as I loop thru growing the custom attribute boneList… but I do not apply any skin weights yet. I do that in a function later on… at first I wrote this “SetSkinWeights” function to loop thru all mesh vertices with an inner loop iterating over each bone in the boneList and using:
skinOps.SetVertexWeights (crrMesh.skin) v b weight
…and this looked almost correct– except some vertices were messed up. Further investigation seemed to reveal that the first bone getting added to the skin modifier was getting set to a value of 1.0 which then messed up later applying of skin weights. I tried to fix this… but could never get it to work. So then I read on the forums and tried to use “skinOps.ReplaceVertexWeights” where I specify the boneList for a given vertex along with an array of bone weights for that vertex… but this seems to produce worse results.
My “SetSkinWeights” function currently looks like (…at the point it is called, the skin modifier is already applied and all bones added to the skin modifier for all meshes):
fn SetSkinWeights = (
meshCollection = #()
meshCollection = for o in objects where classOf o == Editable_Mesh collect o
for m = 1 to meshCollection.count do (
crrMesh = meshCollection[m]
--here I need to repackage the skinWeights array to individual arrays for each bone
select crrMesh
max modify mode
for v = 1 to crrMesh.numverts do (
tempWeightArray = #()
for b = 1 to crrMesh.boneList.count do (
vertIndex = ( b * crrMesh.numverts ) - crrMesh.numverts + v
insertItem ( crrMesh.skinWeights[vertIndex] ) tempWeightArray b
)
skinOps.ReplaceVertexWeights (crrMesh.skin) v (crrMesh.boneList) tempWeightArray
)
skinOps.removeZeroWeights crrMesh.skin
update crrMesh
)
)
I know that I have the skin weight data stored correctly in the custom attribute… it’s driving me crazy how to get it properly applied onto the model. Any help would be greatly appreciated.
It’s hard to tell without a test case or a sample input/output data, but it might be that the vertex weights are un-normalized in the input data and I think the Skin modifier will have them normalized by default.
For example it could happened that your input data for 3 bones is:
#(0.698908, 0.441149, 0.26502)
And the result is:
#(0.497151, 0.314103, 0.188746)
If that is the case check for skinOps.unNormalizeVertex() and skinOps.isUnNormalizeVertex()
I don’t think that is the case… I also tried to unNormalize the vertices but it didn’t help. What is happening is the first bone added to the skin modifier is getting weights on vertices where it should not have any effect at all. This is happening automatically when the first bone is added– it isn’t anything I am doing in the script. Though apparently I’m failing to do something correct. :banghead:
This works on my end, but I will assume I am doing something wrong or different than what you need.
If you have a test scene I could try to help you further.
(
fn BuildTestScene =
(
delete objects
node = converttomesh (cylinder heightsegs:10 capsegs:1 sides:10 height:100 radius:5 isSelected:on)
bones = for j = 0 to 4 collect BoneSys.createBone [0,0,25*j] [0,0,25*(j+1)] [0,0,1]
for j = 2 to bones.count do bones[j].parent = bones[j-1]
modPanel.addModToSelection (Skin()) ui:on
for j = 1 to 5 do skinOps.addBone node.skin ($bone*)[j] 1
completeredraw()
)
fn ModifyVertexWeights node:$ normalizeVerts:true =
(
inputw = #()
outputw = #()
b = 1
for v = 1 to node.numverts do
(
skinOps.unNormalizeVertex node.skin v (not normalizeVerts)
weights = #(random 0.0 1.0, random 0.0 1.0)
skinOps.ReplaceVertexWeights node.skin v #(b, b+1) weights
w1 = skinOps.GetVertexWeight node.skin v 1
w2 = skinOps.GetVertexWeight node.skin v 2
append inputw #(v-1, weights)
append outputw #(v-1, #(w1,w2))
if mod v 30 == 0 do b += 1
)
for j = 1 to inputw.count do format "v:% in:% out:%
" inputw[j][1] inputw[j][2] outputw[j][2]
)
BuildTestScene()
ModifyVertexWeights normalizeVerts:false
)
Thanks… I’ve attached an imported file– the weights are messed up as discussed but the mesh is correct on Frame 0 (skin pose).
I added another parameter to custom attribute to sum all weight elements to verify they equal to 1.0. The custom attribute is given by:
xsiSkinDataCA = attributes xsiSkinData
(
parameters xsiMeshParams
(
boneList type:#nodeTab tabSizeVariable:true
vertWeightSum type:#floatTab tabSizeVariable:true
skinWeights type:#floatTab tabSizeVariable:true
)
)
After import I verified the summed weights for the torso object using:
for v = 1 to $torso.numverts do (
print $torso.vertWeightSum[v]
)
Before calling the SetSkinWeights function… I’ve already added the skin modifier and bones to the mesh objects.
The SetSkinWeights function is given by:
fn SetSkinWeights = (
meshCollection = #()
meshCollection = for o in objects where classOf o == Editable_Mesh collect o
for m = 1 to meshCollection.count do (
crrMesh = meshCollection[m]
--here I need to repackage the skinWeights array to individual arrays for each bone
select crrMesh
max modify mode
for v = 1 to crrMesh.numverts do (
tempWeightArray = #()
for b = 1 to crrMesh.boneList.count do (
vertIndex = ( b * crrMesh.numverts ) - crrMesh.numverts + v
insertItem ( crrMesh.skinWeights[vertIndex] ) tempWeightArray b
)
--sum all the weights for the vertex to check if equal to one
crrMesh.vertWeightSum[v] = SumElements tempWeightArray
--assign vertex weights
skinOps.ReplaceVertexWeights (crrMesh.skin) v (crrMesh.boneList) tempWeightArray
)
skinOps.removeZeroWeights crrMesh.skin
update crrMesh
)
)
So all the skin data necessary is present in the custom attribute on each mesh. Thanks for your help!
Thank you for the file and function, it is much easier to test with it. I believe the following code is working well.
In the scene you have, all weights appear to be already normalized so the 'normalizeVerts' parameter can be safely removed from the function.
You could modify the script a little to have another function to verify if all the weights where set correctly after running the SetSkinWeights() function.
(
fn SetSkinWeights nodes:#() normalizeVerts:false =
(
max modify mode
local so_UnNormalizeVertex = skinOps.UnNormalizeVertex
local so_ReplaceVertexWeights = skinOps.ReplaceVertexWeights
local so_RemoveZeroWeights = skinOps.RemoveZeroWeights
for node in nodes where classOf node == Editable_Mesh do
(
select node
local skinMod = node.skin
local numVerts = node.numverts
local numBones = node.boneList.count
local skinWeights = node.skinWeights
for v = 1 to numVerts do
(
so_unNormalizeVertex skinMod v (not normalizeVerts)
weights = for b = 1 to numBones collect
(
vertIndex = (b * numverts) - numVerts + v
skinWeights[vertIndex]
)
so_ReplaceVertexWeights skinMod v (#{1..numBones} as array) weights
)
redrawViews()
so_RemoveZeroWeights skinMod
)
)
SetSkinWeights nodes:geometry
)
That doesn’t work for me. In 3ds Max 8 your new function leaves much of the mesh unweighted– actually it assigns a weight of zero for the bones it should be weighted against. I’m not sure what’s happening.
Do you have any newer version of Max to test it, so we are on the same page?
The oldest version I have is 9 and it does seem to work well. All the weights are assigned as they are in the CA and also I can see the skin in the animation is automatically fixed, especially in the left and right torsos.
I’ve added a debug option to the function so you can see what’s going on. On my end the ‘in’ and ‘out’ arrays are the same, which means all the values were set correctly.
(
fn SetSkinWeights nodes:#() normalizeVerts:false debug:true =
(
max modify mode
local so_UnNormalizeVertex = skinOps.UnNormalizeVertex
local so_ReplaceVertexWeights = skinOps.ReplaceVertexWeights
local so_GetVertexWeight = skinOps.GetVertexWeight
local so_GetVertexWeightCount = skinOps.GetVertexWeightCount
local so_RemoveZeroWeights = skinOps.RemoveZeroWeights
for node in nodes where classOf node == Editable_Mesh do
(
select node
local skinMod = node.skin
local numVerts = node.numverts
local numBones = node.boneList.count
local skinWeights = node.skinWeights
for v = 1 to numVerts do
(
so_UnNormalizeVertex skinMod v (not normalizeVerts)
weights = for b = 1 to numBones collect
(
vertIndex = (b * numverts) - numVerts + v
skinWeights[vertIndex]
)
so_ReplaceVertexWeights skinMod v (#{1..numBones} as array) weights
if debug do
(
weights2 = for b = 1 to numBones collect so_GetVertexWeight skinMod v b
format "node:%
in:%
out:%
" node weights weights2
)
)
redrawViews()
so_RemoveZeroWeights skinMod
)
)
SetSkinWeights nodes:geometry
)
I am sorry I can’t test it on Max 8. Perhaps there is a bug in that version.
Thank you Michael.
In Max 8 try this:
SetSkinWeights nodes:geometry normalizeVerts:true debug:false
or this if you use the previous script:
SetSkinWeights nodes:geometry normalizeVerts:true
… and let me know if it worked.