glTF-Blender-IO icon indicating copy to clipboard operation
glTF-Blender-IO copied to clipboard

Export Vertex Groups

Open canadaduane opened this issue 3 years ago • 29 comments

Is your feature request related to a problem? Please describe. In a three.js game, we would like to be able to tell the "hair vertices" from the "skin vertices" of an avatar (as well as clothing, shoes, etc.) so that we can modify the colors of the vertices in-game. To accomplish this, we'd like to be able to see the vertex groups as they exist in Blender; however, it appears vertex groups are not currently exported.

Describe the solution you'd like When an object's vertices are grouped via Object Data Properties -> Add Vertex Group, we would like to see these vertex groups in the exported glTF file:

image

Describe alternatives you've considered Multiple UV maps. Dead end due to the way three.js uses UV maps.

Additional context This file has a vertex group called "vertexgroup" and when exported as a glTF, the vertex group is not present:

vertexgroup.blend.zip

canadaduane avatar Oct 05 '20 23:10 canadaduane

How do you want to see it in the glTF?

scurest avatar Oct 06 '20 01:10 scurest

I'm open to just about anything, but one way that seems congruent with a previous blender/three.js exporter is to export as an array attribute:

https://github.com/repsac/io_three/blob/56b2f35b7d56aab6df131c7a3aecb43cda915ca3/addons/io_three/exporter/geometry.py#L409

(extra_vertex_groups defined here: https://github.com/repsac/io_three/blob/master/addons/io_three/exporter/api/mesh.py#L182)

(See also a long ago discussion at https://stackoverflow.com/questions/38451346/blender-buffergeometry-access-vertices-from-vertex-group)

It was a long time ago though (5 years). Does that seem reasonable today?

canadaduane avatar Oct 06 '20 13:10 canadaduane

This branch should export a SCALAR attribute array for every non-skinning vertex group.

scurest avatar Oct 06 '20 14:10 scurest

Wow, that was quick! I'm unfamiliar with testing glTF-Blender-IO but I will give it a shot and report back. Thanks!

canadaduane avatar Oct 06 '20 15:10 canadaduane

Thanks again for this addition @scurest!

After looking at the output, I think the export could be used in its current state. However, is it exporting data in a format that uses more memory than it needs to?

For example, here is a blender file with two exported vertex groups. Each vertex group is comprised of 4 selected vertices (at least, visually in Blender) so I would expect to see 4 vertices * 3 dimensions = 12 values in each group. On the output, however, there are 24 data points, which matches the total number of vertices in the mesh (8 vertices * 3 dimensions = 24). twovertexgroups.blend.zip

Here, I'm using Don McCurdy's gltfviewer to see the glTF, with the JS console open on the right showing "_group" (the first vertex group). Vertices in this group are highlighted RED: gltf-export1

Here is the second vertex group, "_group2", also with vertices highlighted RED: gltf-export2

It looks like there is redundant data in three ways:

  1. It appears we're using a Float32Array for what amounts to binary set inclusion: 0s for "not in the vertex group" and 1s for "in the vertex group".
  2. The set inclusivity data (binary 1 or 0) is repeated for each XYZ dimension.
  3. The entire mesh's vertices are being mapped--could we perhaps employ just a list of indices of vertices in the group? This would amount to 4 data points in my case.

Would it make sense to compress this data/remove redundancies in some way?

canadaduane avatar Oct 06 '20 17:10 canadaduane

could we perhaps employ just a list of indices of vertices in the group

There's nothing like that in glTF but you can modify the exporter to stuff the indices into extensions/extras or something.

scurest avatar Oct 06 '20 17:10 scurest

There's nothing like that in glTF but you can modifier the exporter to stuff the indices into extensions/extras or something.

Ok, well, I think it would be more useful to people if it's more or less the "standard" way.

What do you think about reducing redundant data (numbers 1 and 2 in the list above)?

Thanks again for your help--I wouldn't have known where to even start :)

canadaduane avatar Oct 06 '20 21:10 canadaduane

As long as the data is stored as an attribute array, I think it's already as small as possible based on the fact all attribute arrays in a primitive have to have to same count, and the elements must align to four byte values.

re 1: The _Group array contains the weight each vertex was assigned in Group, so it isn't just set inclusion. But even if you wanted to store something smaller than a float, the alignment requirements (accessor.byteOffset and bufferView.byteStride must be multiples of 4) make it so you'd have to pad to 4 bytes anyway.

re 2: I don't understand what you mean by "repeated for each XYZ dimension". If you mean there are 24 vertices in glTF, rather than the 8 you see in Blender, that's because we need three verts (with three different normals) for each single corner of the cube. If you turn off export of normals/UVs you'll get the same 8 verts as Blender. It doesn't have anything to do with vertex groups.

scurest avatar Oct 06 '20 22:10 scurest

I see. Thank you for the clarifications! It looks like it's working as intended & to spec.

canadaduane avatar Oct 06 '20 23:10 canadaduane

@scurest am I understanding correctly that if the mesh has three vertex groups, A, B, and C, each with optional weights, the branch would write custom vertex attributes to the mesh named _A, _B, and _C, each with values derived from the weights (or zero if the vertex is not in that group)? If so, then for my $0.02, I closed https://github.com/KhronosGroup/glTF-Blender-IO/pull/807 because I thought the approach you've taken here would be better, but I never found time to implement it.

I think it should probably be behind an export option, but would you want to open a PR?

donmccurdy avatar Oct 08 '20 02:10 donmccurdy

@donmccurdy That's correct. I'm not interested in doing a PR for this.

scurest avatar Oct 08 '20 14:10 scurest

Looking for this!

Queatz avatar Jan 02 '21 23:01 Queatz

@Queatz could you say a bit more about how you'd like to use vertex groups in an exported glTF? Other software does not have a concept of vertex groups, and the only concrete proposal here is https://github.com/KhronosGroup/glTF-Blender-IO/issues/1232#issuecomment-704311864, which would preserve the weights from vertex groups, but not really preserve the groups themselves.

donmccurdy avatar Jan 09 '21 22:01 donmccurdy

@donmccurdy I want to use Vertex Groups to paint where grass will be spawned on my terrain. I ended up making it work using Vertex Colors, however it would still be nice to be able to use Vertex Groups since that's how it's normally done (in Blender at least.)

Queatz avatar Jan 09 '21 22:01 Queatz

Ok, thanks — I believe @scurest's suggestion https://github.com/KhronosGroup/glTF-Blender-IO/compare/master...scurest:custom-data would handle that case well.

donmccurdy avatar Jan 09 '21 22:01 donmccurdy

(Sorta OT)

It occurred to me if the custom data accessors are mostly zero it would be better to write a sparse accessor, so I pushed a commit to the custom-data branch that calculates when a sparse accessor would be smaller and writes that instead. I haven't tested it extensively though.

@canadaduane This can serve as a partial response to your question about "employing just a list of indices of vertices in the group".

scurest avatar Jan 10 '21 20:01 scurest

Nice! Yes, that would improve our use case as well (needing access to vertices that make up the "hair" part of a character, for example, which is a small number of vertices compared to all vertices in the character).

On Sun, Jan 10, 2021 at 1:45 PM scurest [email protected] wrote:

(Sorta OT)

It occurred to me if the custom data accessors are mostly zero it would be better to write a sparse accessor, so I pushed a commit to the custom-data https://github.com/scurest/glTF-Blender-IO/tree/custom-data branch that calculates when a sparse accessor would be smaller and writes that instead. I haven't tested it extensively though.

@canadaduane https://github.com/canadaduane This can serve as a partial response to your question about "employing just a list of indices of vertices in the group".

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/KhronosGroup/glTF-Blender-IO/issues/1232#issuecomment-757541183, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAABAP4PQI4PAXFJI4VOYDSZIGV3ANCNFSM4SFJQH3Q .

canadaduane avatar Jan 10 '21 21:01 canadaduane

I have @donmccurdy's GLTF viewer working with vertex groups:

image

Code at https://github.com/canadaduane/three-gltf-viewer/commit/27354979166da0d68b7f19593ffcd4acd34653bd

This capability is exactly what we were looking for. Thanks @scurest!

canadaduane avatar Mar 12 '21 19:03 canadaduane

@scurest I'm curious how difficult it would be to export Face Maps as well. I recently learned about this feature (Blender 2.8+, apparently). It's even more ideal than vertex groups for our use case, since it means we would not need to double up on certain vertices to get a "clean edge" when coloring based on vertex.

canadaduane avatar Mar 12 '21 22:03 canadaduane

Here is a branch that should export facemaps.

  • For the purpose of exporting a vertex attribute, the facemap index is turned from a property of polys to a property of their corners: the corner of a face assigned to the ith facemap is assigned i. Then it's treated like any other per-corner data (UVs, vertex colors, normals).
  • Meshes with facemaps will have a _FACEMAPS attribute (scalar, float32) in glTF, holding the facemap index or -1 for "not assigned to any facemap".
  • Nodes have an extras property face_maps holding an array of the facemap names. (Note that this is stored on the glTF node, not the glTF mesh. That's because the names are also stored on the Blender object, not the Blender mesh.)

scurest avatar Mar 13 '21 01:03 scurest

Thanks @scurest! I combined both of your excellent contributions, so that one may export both vertex groups & facemaps if desired:

https://github.com/canadaduane/glTF-Blender-IO/tree/custom-data

NOTE: I changed the _FACEMAPS attribute to FACEMAPS so that the underscore prefix can continue to have special meaning, i.e. to flag mesh attributes as named vertex groups.

canadaduane avatar Mar 13 '21 06:03 canadaduane

I changed the _FACEMAPS attribute to FACEMAPS so that the underscore prefix can continue to have special meaning, i.e. to flag mesh attributes as named vertex groups.

The underscore is because the spec requires that "application-specific semantics must start with an underscore, e.g., _TEMPERATURE". The validator will reject an attribute named FACEMAPS.

scurest avatar Mar 13 '21 06:03 scurest

Oh, hmmm. That makes sense, but then someone could name a vertex group as "facemap" and it would potentially wipe out the legit "_facemap" attribute, right?

Maybe we need a special prefix, such as "vg*", for vertex groups...

On Fri, Mar 12, 2021, 11:48 PM scurest @.***> wrote:

I changed the _FACEMAPS attribute to FACEMAPS so that the underscore prefix can continue to have special meaning, i.e. to flag mesh attributes as named vertex groups.

The underscore is because the spec requires that "application-specific semantics must start with an underscore, e.g., _TEMPERATURE". The validator will reject an attribute named FACEMAPS.

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/KhronosGroup/glTF-Blender-IO/issues/1232#issuecomment-797878706, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAAABANJ6TVDZ37WADYXTSDTDL4EHANCNFSM4SFJQH3Q .

canadaduane avatar Mar 13 '21 14:03 canadaduane

I added the _vg_* prefix and restored the _ prefix to _facemaps. (Thanks!)

canadaduane avatar Mar 13 '21 18:03 canadaduane

are there plans to merge this into the official exporter?

fknfilewalker avatar Mar 29 '21 15:03 fknfilewalker

We'd be happy to review PRs for either Face Maps or Vertex Groups — ideally with added unit tests since this is a less common case and it would be easy to miss regressions.

donmccurdy avatar Mar 29 '21 18:03 donmccurdy

Facemaps are now longer part of Blender (removed for 4.0) See https://projects.blender.org/blender/blender/commit/46cf09327001331c77bcd54ceab73404a1733172

julienduroure avatar Jun 09 '23 17:06 julienduroure