arkavo-org / arkavo-org/VRMMetalKit
SpringBone collider evaluation order: VMK uses one global authored-order buffer; UniVRM/three-vrm use per-spring group-major lists
- Dominant language
- Swift
- Stars
- 6
- Forks
- 2
- Avg merge
- 18h 51m
- Merged PRs (30d)
- 26
Description
Surfaced while investigating #415. That issue asked for per-group collider application; measuring it showed the repetition itself is inert in this solver, but isolated a different divergence that is not currently covered by the suite.
## The divergence
UniVRM and three-vrm evaluate a **per-spring, group-major** collider list — `spring.colliderGroups` in order, each group's `colliders` in order:
- `UniVRM` `FastSpringBoneBufferFactory.cs:47-48` — `spring.ColliderGroups.SelectMany(group => group.Colliders)` builds a per-spring span
- `UniVRM` `UpdateFastSpringBoneJob.cs:100-119` — the job walks that span in order, updating `nextTail` on each hit
- `three-vrm` `VRMSpringBoneJoint.ts:284-299` — nested `for cg` / `for c` in the same order
VMK uploads **one global collider buffer in authored collider order** and filters it per bone with a 32-bit group mask (`SpringBoneComputeSystem.populateSpringBoneData`, `collideWithSphereFiltered` in `SpringBoneCollision.metal`). Every spring sees the same order.
Collision resolution is order-dependent: the loop mutates the joint position in place, so pushing out of collider A and then B does not generally land where B-then-A lands. Wherever two co-reachable colliders overlap, the two orders disagree.
## Measured
`Tests/VRMMetalKitTests/SpringBoneMultiGroupColliderTests.swift` (`testGroupMajorOrderingIsWhatMakesRepetitionObservable`) runs one scene — two overlapping spheres, `X` reachable via two groups, `Y` via one — under three buffer layouts with identical geometric content:
| upload | order | max joint delta after 90 frames |
| --- | --- | --- |
| A — today | `X(0b11), Y(0b01)` | — |
| B — collider-major | `X(0b01), X(0b10), Y(0b01)` | 9.3e-09 m |
| C — group-major (UniVRM's order) | `X(0b01), Y(0b01), X(0b10)` | **5.63e-02 m** |
B and C differ only in the position of the repeated entry. 5.6 cm on a 0.1 m bone chain.
## Exposure
Overlap between co-reachable colliders is not a corner case. Counting pairs of colliders reachable from a common spring whose bounds intersect in the rest pose:
| asset | colliders | springs | co-reachable pairs | overlapping | springs with >=1 overlapping pair |
| --- | --- | --- | --- | --- | --- |
| AvatarSample_U 1.0 | 28 | 68 | 13785 | 1135 (8.2%) | 68 / 68 |
| AvatarSample_A 1.0 | 22 | 12 | 2772 | 204 (7.4%) | 12 / 12 |
| AvatarSample_K 1.0 | 22 | 36 | 8316 | 612 (7.4%) | 36 / 36 |
| vroid_default_F 1.0 | 28 | 44 | 6891 | 685 (9.9%) | 44 / 44 |
Every spring on every avatar sampled has at least one overlapping co-reachable pair. Overlap is necessary but not sufficient — a joint has to reach the overlap region for order to matter — so this bounds exposure, it does not measure output delta on these assets.
## What would settle it
A conformance discriminator with **two overlapping colliders in different groups**, referenced by one spring, with the authored order deliberately disagreeing with the group-major order. That measures the divergence directly against the oracle and tells us whether it is worth restructuring. The single-collider multi-group probe in #415 cannot: with one collider the two orders coincide.
## Options, if it turns out to matter
1. **Per-spring collider spans in group-major order.** Faithful to the references. Costs the global-buffer + mask-filter design: colliders would be duplicated per referencing spring, the buffer grows with total membership rather than collider count, and the sphere-array indices that the public `sphereColliderRadiusOverrides` API is keyed on would shift.
2. **Reorder the global buffer** to the group-major order implied by the first referencing spring. Cheap, no API break, but only correct when springs agree on relative order — it does not fix the general case.
3. **Document as a methodology difference** and leave it, if the discriminator shows the real-asset delta is below the oracle's own nondeterminism floor.
No work started on any of these — filing so the measurement is not lost.
Contributor guide
Research direction
Start with Tests/VRMMetalKitTests/SpringBoneMultiGroupColliderTests.swift and run testGroupMajorOrderingIsWhatMakesRepetitionObservable. Build the proposed conformance discriminator using two overlapping colliders in different groups referenced by one spring, with authored and group-major orders differing. Compare the result against the referenced UniVRM/three-vrm ordering and determine whether the real-asset difference exceeds the nondeterminism floor before choosing an option.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- computer-graphics, testing-qa
- Issue type
- Bug
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100