arkavo-org / arkavo-org/VRMMetalKit

SpringBone: synthetic colliders model each segment as one radius; the body surface varies 3-4x

Open
#381 1 comment 0 reactions 0 assignees View on GitHub
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

Open the contributing 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.