arkavo-org / arkavo-org/VRMMetalKit
SpringBone: synthetic colliders model each segment as one radius; the body surface varies 3-4x
- Dominant language
- Swift
- Stars
- 6
- Forks
- 2
- Avg merge
- 18h 51m
- Merged PRs (30d)
- 26
Description
## Problem
Synthetic collider augmentation models each body segment as **one primitive with one radius**, anchored on a bone axis. For much of the body the skin surface is not a tube around the skeleton, so no single radius is correct: pick the high percentile and the collider engulfs and ejects cloth outward; pick the low one and cloth sinks through.
This is the shared root of several shipped defects, not a one-off.
## Evidence
How far the skin sits from each bone's own axis on `AvatarSample_A` (dominant-weight verts, radial distance from the bone segment, verts projected within the segment):
| segment | p05 | p50 | p95 | p95/p05 |
|---|---|---|---|---|
| spine→chest | 79 mm | 84 mm | 100 mm | **1.26** |
| neck→head | 28 | 33 | 47 | 1.67 |
| shoulder→upperArm | 24 | 33 | 50 | 2.08 |
| upperLeg→lowerLeg | 35 | 57 | 98 | 2.82 |
| upperChest→neck | 40 | 78 | 113 | 2.83 |
| chest→upperChest | 44 | 75 | 131 | **3.00** |
| upperArm→lowerArm | 21 | 39 | 85 | **4.06** |
| lowerArm→hand | 12 | 27 | 51 | **4.23** |
Where the ratio is ~1.3 a capsule is faithful. At 3–4× there is no good radius.
## Consequences already observed
- **Décolletage funnel (#309 `testLookUp` regression).** The mesh-fitted shoulder sphere sat at 1.76–1.88× the authored collider on the same bone, its medial edge reaching x=±0.036 and wedging front hair into the uncovered midline gap. Fixed in #380 by excluding medial verts, but the underlying single-radius fit remains.
- **An attempted neck collider needed a 108 mm radius** to reach a pocket 80.6 mm off the neck axis — against an authored neck collider of 43.1 mm and a mesh-measured neck of p95 47.2 mm. Every test passed; the avatar's side hair splayed horizontally. Reverted (tag `neck-attempt-archive`).
- **Hand sphere is 3.02–3.80× the authored hand collider** on the two fixtures (86–91 mm synthesized against 24–29 mm authored). Shipping since #321. Currently pinned in `ColliderDimensionAudit.knownOversized`.
## Why sizing alone does not close it
`SpringBoneBoneGeometry.limbCapsule` computes `max(authoredHint, length * radiusFraction)` — the authored radius is a **floor and never a ceiling**, so an overshooting fraction wins silently on every rig. #380 adds `ColliderDimensionAudit` to bound this (synthetic ≤ 1.25× authored, with known offenders pinned). That stops the drift getting worse. It does not make a constant-radius primitive able to represent a surface that varies 3–4×.
## Coverage gaps, separately
Of 54 humanoid bones on `AvatarSample_A`, 14 carry a collider. Of the 40 without, 34 are finger joints, eyes and toes — correctly skipped, since a collider there is pure XPBD cost per joint per substep. The gaps that matter are **hips, chest, both shoulders, both feet**. `spine` and `upperChest` are covered while `chest` is not, which is precisely the décolletage band.
## Proposed direction
1. **Treat authored colliders as the source of truth.** The audit shows they are accurate (neck 43.1 mm vs mesh p95 47.2 mm; head, upperChest similar). The three synthetic families that already derive their size from an authored radius — `headSkullSphere` (1.0 × rHead), `headBrowCapsule` (0.5 × rHead), `breastTwinSphere` (a copy) — land at exactly 1.00× / 0.50× / 1.00× on both fixtures. That is the grounded pattern.
2. **Synthesize only into measured gaps**, not over regions the author already covered.
3. **Where p95/p05 exceeds ~2, fit a chain of small spheres along the bone** instead of one capsule. VRM's capsule is `offset + radius + tail` with a single radius and no taper, so multiple primitives are the only in-spec way to follow a varying surface.
4. Retire the `*Fraction` constants in `SpringBoneColliderAugmentor.Ratios` as each family becomes measurement-derived; delete the corresponding `knownOversized` entries, which the guard already fails as stale once a ratio drops under the ceiling.
## Acceptance
- `ColliderDimensionAudit.knownOversized` is empty, with the general 1.25× assertion guarding every synthetic collider that shares a bone with an authored one.
- The #309 / #321 / #377 suites stay green.
- Visual sanity render per the project convention — this defect class is invisible to the suites. The reverted neck attempt passed every test and was obviously wrong on screen.
## Reproducing the measurements
The spread table came from a scratch probe (dominant-weight verts per bone, radial distance from the bone segment). It was not committed; `ColliderDimensionAudit` in #380 contains the vertex/skinning access pattern needed to rebuild it.
Contributor guide
Research direction
Start with SpringBoneColliderAugmentor.Ratios, SpringBoneBoneGeometry.limbCapsule, and ColliderDimensionAudit, then review the #309, #321, and #377 suites. Rebuild the measurements using the vertex/skinning access pattern in ColliderDimensionAudit and determine which uncovered regions need coverage. Done means the audit has no knownOversized entries, the general 1.25× assertion passes, the named suites remain green, and a visual sanity render is checked.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- swift
- Domain
- computer-graphics
- Issue type
- Feature
- Difficulty
- 5/5
- Estimated time
- Over a week
- Activity status
- Quiet
- Clarity
- Mostly clear
- Newbie friendliness
- 35/100