[Closed] Math for skin transfer?
Hi there,
I am writing a script to transfer skin data from one mesh to another, and I hope the script can be used to transfer other data in the future (such as UVs). The math is causing me a headache though, I immediately start bumping into problems that are above my level (which is basic linear algebra).
Here’s what I’m going to do:
-
From a selected skin modifier, the script will save out the weight data as a text file. There will be one file per bone, with the position and weighting for each vertex.
-
For the character that is to receive the new weighting, the file data will be read into arrays, and the script will interpolate new values based on the old data.
Now, the interpolation is what’s causing me a headache. I don’t want to just pick the nearest vertex and use its weight. I want to interpolate it properly, so that I can transfer between different topologies. After doodling on paper and prototyping in maxscript for two days, I see that point cloud interpolations are quite complex to solve. I hope someone here have ideas for a simple and good enough way of solving it.
Searches on Google suggests doing a full delaunay polygonization and go from there, but that’s way beyond what I can do. Right now I’m using Shepards method, but it’s not very accurate with points that are far away from the sampled data.
Does anyone here have some suggestions for a quick and dirty way of doing point cloud interpolation?
Here’s a screengrab of my attempts at interpolation. The data is at left, two rows of five white and black points. In the middle is nearest neighbor, at the right is Shepards method. Notice how Shepards starts to fade to black above the white points, and to white under the black points – something that would have been a major problem had these been skin weights.
why don’t you simply use the skinUitilities for that?
can you post the code for the Shepards method test?
I concour,
Walk through each vertex, (selected or not) formating its id. Walk through its bones with:
skinOps.getVertexWeightCount <skin> <vertex>
appending the id of each bone(s) that the vertex uses to an array:
skinOp.getVertexWeightBoneID <skin> <vertex> <index>
and its weight for that vertex to an array:
skinOp.getVertexWeight <skin> <vertex> <index>
You’ll end up with something like so:
vertex <array of bone ids> <array of weights>
1 #(1,2,3,4) #(.25,.25,.25,.25)
- As for doing it over different topologies id store the vertex position as well, and then do a closest match on the different mesh.
In any system you need a baseline reference to essentially find the skeleton for the mesh area/vertex etc…
If the models look the same but have different topologies – a better approach maybe skin wrap. Basically on the driven mesh find the closest 4 vertexes on the target mesh and find the average.
I’ve actually never seen Skin Utilities before! The “match by face” option was greyed out when I tested it, so copying from one topology to another didn’t work. I just got the default bind on the mesh.
Charles, those were the procedures I were planning on using. But since I mostly will be doing this across very different topologies, it’s the interpolation math itself I need input on. Nearest neighbor will be my failsafe, but I would really really like to have proper interpolation in here.
Getting the average of the four closest vertices will not work, because those can be grouped together on one side of the point, and vertices that should be influencing from the other side will then be ignored.
Matan, here’s part of my interpolation test script that have the code for the Shepard method. Just copy paste this into a script window to see the result.
function testPolate =
(
-- Set up some test values and a small bitmap to play with
testDataCoords = #( point2 5.0 18.0, point2 10.0 15.0, point2 20.0 13.0, point2 30.0 11.0, point2 45.0 11.0, \
point2 5.0 22.0, point2 10.0 22.0, point2 20.0 20.0, point2 30.0 19.0, point2 45.0 18.0 )
testDataValues = #(color 256 256 256, color 256 256 256, color 256 256 256, color 256 256 256, color 256 256 256, \
color 0 0 0, color 0 0 0, color 0 0 0, color 0 0 0, color 0 0 0, color 0 0 0)
-- Shepard's method
testMap3 = bitmap 50 50 hdr:true
for x = 0 to 49 do
(
for y = 0 to 49 do
(
local setColor = color 255 0 0
local thisCoord = point2 x y
local val1 = color 0 0 0
local val2 = 0.0
for c = 1 to testDataCoords.count do
(
local newDist = distance thisCoord testDataCoords[c]
local wi = pow (1 / newDist) 8
val1 = val1 + testDataValues[c] * wi
val2 = val2 + wi
)
setColor = val1 / val2
setPixels testMap3 thisCoord #(setColor*.75)
)
)
-- Draw original pixels on the maps
for c = 1 to testDataCoords.count do
(
setPixels testMap3 testDataCoords[c] #(testDataValues[c])
)
-- Display our result!
display testMap3
)
testPolate()
I’m now trying a new method where I pick the closest vertex in three cone subdivisions around the point, and then doing a linear interpolation between the resulting three vertices. I’ll try that in 2D first, and see if I can extrapolate it out to 3D.
How different are the meshes and their topologies from one another? Are we talking big proportional changes?
The proportions are more or less identical, the first skin weight I’m transferring is to the same set of bones. The topology is very different though, it has been subdivided one level and then remodeled.
I seem to have some success with a hybrid of Shepards method and a subdivision method I have been thinking about.
Instead of searching for the nearest vertices, I’m picking the nearest inside three segments around the point. This result in everything from one to three hit points, which I then use Shepards method to weight. It’s very crude, but I think the results are sufficient. At least it’s better than just using nearest neighbor.
Here are the results from a black/white dataset and a debug coloured dataset:
Use Skin Utilities as suggested or even Skin Wrap and then Convert To Skin.
Converting a Skin Wrap does give quite good results on my test model. As I mentioned earlier, Skin Utilities doesn’t work at all. I’ll stick with Skin Wrap a while (converted though, pure Wrap is too slow), and continue with the script if Skin Wrap doesn’t handle the more complex models.
I was halfway on the final script though, felt like a shame to “give up”
SkinWrap is a life saver, but sometimes it behaves very weird. I found it useless in some situations where you can’t make it work properly (it does not recognize some geometry on the model, and I swear to God the model was OK).
If I were you and had some spare time, Rune, I’d go ahead with the script
Thanks Iker, I will try! I was planning on releasing it when it’s done, that way everybody’s happy