0
$\begingroup$

I distribute points (I'll call them P) on the faces of a mesh and then instantiate objects (scales) at these points, in order to cover the surface with scales:

enter image description here

However, the original mesh is animated via an armature, and I have trouble assigning working vertex weights for the scales. The scales instances themselves should not be deformed, but move rigidly with the point P on the surface they are attached to.

What I've tried:

  • I can copy the vertex weights with a DataTransfer modifier from the original mesh to the scales. However, this does not know about which face the point P was originally placed on, so I loose that information and the result is a mess. Also, it potentially deforms the scale:

enter image description here

  • I tried storing the index of the nearest vertex (of the original mesh) for each of the distributed surface points P in an attribute. When I create scale instances, those "inherit" this attribute, which allows me to later write a script which copies the vertex weights from the vertex with this index to the entire scale instance. This results in rigid movement, but it (obviously) looks like the scale was attached to the bones that the nearest vertex is attached to, when really it should be attached to a point on the surface (which I think is an interpolation between the face corner vertices' weights)?.
  • My current idea is to store the position of the generated distributed point P and the index of the face it was placed on. Then I could determine the vertex weights of each of the corner vertices of the face and probably interpolate them depending on the point position.

Is there a function to calculate this interpolation? I think when subdividing/cutting a mesh then weights for the new vertices are calculated automatically - and I think that is the function I would need? Or is there a different, better way to achieve this?

Edit: I should have noted that I want to export the result for use in a game engine, so correct vertex weights is really the only feasible option I have.

Side note: As far as I can tell, working with a large (and varying) number of vertex groups is currently not easy in geometry nodes, so I would prefer using the geometry nodes only to create the scale instances and store necessary information and then write a python script that calculates the vertex groups/weights.

$\endgroup$

1 Answer 1

2
$\begingroup$

The best idea here is to step away from the idea of armature deforming a bunch of realized instanced scales, and instead, deform the surface that you are using to instance your scales. This might be via an armature modifier; it might be via a surface deform modifier targeting your rendering mesh; it might even be something else.

There may be some reasons that this doesn't seem to work for you, but I'd say, maybe we can suggest some ways to make it work in those circumstances. Deforming the instancing surface is so, so much better of an idea than deforming the instances that it is worth some work to make it happen. Deforming the instancing mesh is the most non-destructive solution, the most reusable solution. And it is hugely more performant, both in terms of time and memory.

Let me explain how I'd weight this geo if it was not generated by GN. I would make a shapekey for the scale mesh that was each mesh island shrunk down to a single point-- select all, scale about individual origins to 0. Then, with this shapekey pinned, I would data transfer vertex groups on nearest face interpolated mode (not nearest vertex.) The modifier gets applied, then the shapekey gets deleted.

What makes for "rigid" deformation is simply that the entire rigid bit has identical weights. Scaling to 0 ensures that every vertex of any particular mesh island is reading weights from a single point on the source mesh. Reading from the nearest face interpolated lets it follow the actual mesh surface, rather than acting in chunks that follow the vertices.

However, this does not know about which face the point P was originally placed on, so I loose that information and the result is a mess.

Data transfer from a non-rendering copy that isn't deformed.

Also, it potentially deforms the scale:

Shrink to a zero point before data transferring, then restore position afterwards. GN can do this fairly easily-- you're already storing the instancing position, which is your zero point; you can save position to an arbitrary attribute and restore position from it after data transfer.

Is there a function to calculate this interpolation?

You want barycentric weights. Given point p on a face and the positions of the three points a, b, and c making up the vertices of that face, the following provides barycentric coordinates u, v, and w.

Vector v0 = b - a, v1 = c - a, v2 = p - a;
float d00 = Dot(v0, v0);
float d01 = Dot(v0, v1);
float d11 = Dot(v1, v1);
float d20 = Dot(v2, v0);
float d21 = Dot(v2, v1);
float denom = d00 * d11 - d01 * d01;
v = (d11 * d20 - d01 * d21) / denom;
w = (d00 * d21 - d01 * d20) / denom;
u = 1.0f - v - w;

From https://gamedev.stackexchange.com/questions/23743/whats-the-most-efficient-way-to-find-barycentric-coordinates .

Your desired weights are the weighted average: u * weight(a) + v * weight(b) + w * weight(c) = weight(p).

There is one other potential solution to the rigidity problem here, if it's easier for you in some way: try a high strength, high iteration (1.0 strength, 200 iteration) corrective smooth modifier. It usually restores the "rigidity" of deformed objects. At least, well enough. And, it's very easy to try.

$\endgroup$
2
  • $\begingroup$ Thanks! I should have mentioned that I want to export the result for use in a game engine, so I really need to use a vertex weight solution. That means that first deforming the surface, then distributing points and instancing won't work (also, I think this can produce a pop-in-effect). I think what I was missing was the barycentric coordinates. I tried it, but it's not working so far (looks like scales are attached to the wrong point atm.), will keep trying... $\endgroup$ Mar 24 at 9:37
  • $\begingroup$ Yeah, it works with barycentric coordinates: 1. Store the scale center point and the face index of the face it was generated on (I use Samples Nearest node set to Face and send that to a Store Named Attribute node) 2. find the barycentric coords for the center point within this face using mathutils.interpolate.poly_3d_calc 3. interpolate the weights for all the groups any of the face corner verts belong to 4. copy weights to all of the scale's vertices. Thanks again! $\endgroup$ Mar 24 at 16:17

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .