Message 1 of 25
Help weight transfer script
- Mark as New
- Bookmark
- Subscribe
- Mute
- Subscribe to RSS Feed
- Permalink
- Report
Edit. Solved.
Edit. Solved.
@davinwest72 wrote:Version gmax, which is exactly like 3dsmax4.2 ...
Are you a time traveler? An ancient artifact hunter? 😲
Discreet 3dsmax 4
| Magma | 2000 | Windows 98, Windows ME, Windows 2000[6] |
or maybe 3D Studio? 😂
Well... Do you want to make it good or easy? Good skin wrapping is a pretty complicated algorithm. What kind of meshes do you want to wrap?
Edit. Solved.
Is there no way to convert the data to a more recent version of MAX and, using something like OBJ, import it back in after skin wrapping?
I'm also concerned about the MAXScript version... at the beginning of the 2000s it was pretty far from up to date, skinning support was very limited, and I'm not sure if skinops have been introduced yet.
Skin wrapping based on the distance between vertices is very coarse. A much better result is given by a "face-based" algorithm. Both requires a lot of time and memory for large meshes, and something like a Kd-tree search engine is needed to speed it up... This is quite a heavy task for older versions assuming MAXScript implementation.
Edit. Solved.
I've been writing scripts for MAX for 25 years now... I'm not saying that what you're trying to do is completely impossible, but I see good reasons to look for alternative solutions.
Edit. Solved.
local boneIndices = for k = 1 to k - 1 where boneWeights[k] > 0 collect k
this is wrong for sure...
What is the typical number of vertices in the source and target meshes?
Edit. Solved.
@davinwest72 wrote:I will reconfigure the line. The source mesh typically has about 400 verts. Target mesh will match or sometimes double that amount.
These are small meshes. You can do it the way you do it. There's no need for any clever "acceleration".
Edit. Solved.
I understand that you're interested in learning how to write scripts. Unfortunately, I don't have the time to explain everything in detail, but I can show you two basic functions that I used in one of my tools. If you take the time to understand them, you'll be able to advance your learning on your own.
It's worth mentioning that your current code isn't too far off from working properly.
So, are you ready to learn these functions?
Edit. Solved.
fn copySkinData node =
(
sk = node.modifiers[skin]
if iskindof sk Skin do
(
max modify mode
modpanel.setcurrentobject sk
num = skinops.getnumberbones sk
bones = for k=1 to num collect
(
skinops.getbonename sk k 0
)
weights = for k=1 to skinops.getnumbervertices sk collect
(
num = skinops.getvertexweightcount sk k
_bones = #()
_names = #()
_weights = #()
for i=1 to num do
(
id = skinops.getvertexweightboneid sk k i
name = skinops.getbonename sk id 0
weight = skinops.getvertexweight sk k i
append _bones id
append _names name
append _weights weight
)
#(k, _bones, _names, _weights)
)
#(bones, weights)
)
)
fn pasteSkinData node data =
(
sk = node.modifiers[skin]
if iskindof sk Skin do
(
max modify mode
modpanel.setcurrentobject sk
bones = data[1]
num = bones.count
for k=1 to num do
(
if isvalidnode (bone = getnodebyname bones[k]) do
(
skinops.addbone sk bone 1
)
)
skinOps.Invalidate sk 1
weights = data[2]
num = weights.count
for k=1 to num do
(
_d = weights[k]
_bones = for b in _d[3] collect (finditem bones b)
skinops.replacevertexweights sk k _bones _d[4]
)
#(skinops.getnumberbones sk, skinops.getnumbervertices sk)
)
)
If you create a map for the source and target vertices, these two functions will suffice for the skin transfer.
Edit. Solved.
fn mapNearestVerts source target =
(
fn findNearest p verts =
(
dist = distance p verts[1]
n = 1
for k=2 to verts.count where (d = distance p verts[k]) < dist do
(
dist = d
n = k
)
n
)
source_mesh = snapshotasmesh source
target_mesh = snapshotasmesh target
source_map = #()
numv = source_map.count = source_mesh.numverts
target_verts = for v=1 to target_mesh.numverts collect (getvert target_mesh v)
for v=1 to numv do source_map[v] = findNearest (getvert source_mesh v) target_verts
source_map
)
delete objects
s = geoSphere name:#source radius:25 segs:12 wirecolor:green
t = sphere name:#target radius:25 segs:32 wirecolor:orange
source_map =
(
format "source:% target:%\n" (s.mesh.numverts) (t.mesh.numverts)
t0 = timestamp()
h0 = heapfree
mm = mapNearestVerts s t
format "\ttime:% heap:% count:%\n" (timestamp() - t0) (h0 - heapfree) source_map.count
mm
)
This example shows that we are good with source and target meshes of size ~1.5K and ~0.5K vertices.
You can play around with the number of segments to see how it affects performance
If we copy from high-res to low-res, no problem here.... but mostly we need to copy from low-res to high-res. In this case, there is a problem because we can't do it well without averaging several near vertices.