Problem. A glTF mesh could lose additional skin mappings and weight sets, while legacy reimport could reassign asset IDs. Implementation. Per-skin imports, weight reduction and conservative legacy matching. Verification. The affected suites passed 301 tests, with fixes merged upstream.
Pulsar's glTF importer kept only the first skin used by a mesh and ignored vertex weights after the first attribute set. I updated the import path across Solid3D, Helio and Pulsar-Native. The changes are merged upstream.
The skin belongs to the node
In glTF, two nodes can reference the same mesh but use different skins. Each skin has its own ordered joint table and inverse-bind matrices. A joint index in a vertex is an index into that table, so reusing the first skin's table can bind the vertex to the wrong bone.
{ "mesh": 0, "skin": 0 }
{ "mesh": 0, "skin": 1 }
Helio now extracts a rig for each used skin. Nodes sharing a mesh under the same skin reuse geometry. A node with no skin still produces a static asset. Pulsar writes a skeleton, skeletal mesh and animation set for each rig.
Reading the remaining weights
The decoder reads every paired JOINTS_n and WEIGHTS_n set, validates their types and counts, and combines duplicate joint references. It keeps the strongest four influences, breaks ties by joint index and normalizes the retained weights with an f64 accumulator.
The runtime still stores four influences per vertex. This change handles richer source files at import time; it does not increase that runtime limit or implement GPU skinning. A warning reports how many vertices in each source mesh lost influences.
A reimport bug caught during review
New import records store a source-skin index beside each output. Reimport uses it to keep the skeleton, mesh and animation IDs associated with the same rig, including when files have been moved or deleted.
Old records had no skin index. The initial migration assigned those outputs to the currently first-used skin. I reproduced a case where an old import used skin 1, then a source edit made skin 0 first-used. Migration gave skin 0 the old skin-1 IDs.
The fix matches the saved joint palette and inverse binds before writing any assets. If the mesh is missing, it can use a surviving skeleton. It requires a unique match. When the saved evidence is missing or ambiguous, the import stops and asks for the previous source and outputs to be restored. It leaves the existing assets intact.
Validation and integration
A small glTF fixture covers two same-name skins sharing a two-primitive mesh, different joint palettes and inverse binds, repeated instances, an unskinned node and nine influences across three attribute sets. Tests check CPU skinning results, serialization, weight reduction and reimport identity.
The importer, conversion and skeletal-asset suites passed 301 tests. Scoped strict Clippy, affected-package all-target checks and all 17 dependency-pin checks passed. Maintainer tristanpoland merged the decoder and conversion changes before the final engine pins.
At handoff, workspace CI still had unrelated formatting and Graphy guard failures. The duplication detector's jscpd phase also remained unverified.
Merged changes
- Solid3D #62 · glTF decoding
- Helio #367 · per-skin scene conversion
- Pulsar-Native #1550 · linked asset import
- Pulsar-Native #1551 · legacy migration fix
- Helio #368 and Pulsar-Native #1553 · final dependency pins