Skip to content

investigate(springbone): inside-collider diagnostic + substep ordering#300

Draft
arkavo-com wants to merge 2 commits into
mainfrom
issue/237-inside-collider-substep-ordering
Draft

investigate(springbone): inside-collider diagnostic + substep ordering#300
arkavo-com wants to merge 2 commits into
mainfrom
issue/237-inside-collider-substep-ordering

Conversation

@arkavo-com

Copy link
Copy Markdown
Contributor

Summary

  • Adds VMK#237 inside-collider diagnostic harness (per-bone last-write buffer the sphere/capsule kernels populate with (shape_type, angleLimit, distance, boundary, penetration, group_matched), opt-in via renderer.setInsideColliderDiagnosticsEnabled(true)). Sentinel shape_type values distinguish "never ran" / "kernel-entered no match" / "outside-branch with inside-flag=0" / "inside-sphere fired" / "inside-capsule fired" so future investigations can pin parameter-propagation bugs without re-instrumenting.
  • Substep ordering: sphere + capsule collision now interleave with the distance constraint iteration loop (SpringBoneComputeSystem.swift's XPBD loop). Inside-* containment is re-applied after every distance pass instead of only once at the end; plane stays outside the loop.
  • New regression test testInsideSphereAngleLimitReachesShader documents that BoneParams.angleLimit reaches the shader correctly (1.047 rad / 60° read by every non-root bone).

Finding

The conformance bucket collapse for springbone_extended_isphere_* variants is not a VMK propagation bug. The diagnostic confirms parser / GPU buffer build / shader branch / group filter / per-joint angleLimit are all wired correctly. Chain joints stay at distance 0.0046 / 0.0506 / 0.1005 m from the sphere centre, well inside the 0.20 m and 0.40 m boundaries at pmed / ploose, so inside-sphere collision correctly never engages; only ptight (r=0.10) is tight enough to clip the chain. Angle-limit values 30°/60°/90° collapse because the chain's actual swing never exceeds 30° from bind.

vrm-conformance suite addressed it on their side by reducing inside-sphere/capsule placement radii from [0.10, 0.20, 0.40] to [0.04, 0.06, 0.08]. Bucket count 7 → 11 (+57%), non-zero penetration across all 18 swing variants.

Behaviour change

Sphere and capsule collision dispatches now run once per distance-constraint iteration instead of once at the end of the substep. For models that previously sat inside their colliders without distance constraint forcing them out, behaviour is unchanged. For models where distance constraint can pull a joint back outside a containment surface, the joint is now pushed back in within the same substep instead of carrying the violation forward. Plane behaviour unchanged.

Performance: constraintIterations=4, substeps=4 → 16 dispatches/frame for sphere+capsule combined vs. 4 before. Each dispatch is one bone-count grid (≪ 100 threads typical), trivially cheap.

Test plan

  • swift test --filter SpringBone --disable-sandbox — 170 tests, 0 unexpected failures (1 expected failure caught by XCTExpectFailure)
  • swift test --filter SpringBoneExtendedColliderBehaviorTests --disable-sandbox — 7 tests, including the new diagnostic test
  • swift build clean
  • make shaders-macos clean
  • AvatarSample_A.png regenerated, no visible artifact (88-byte delta from prior render)

Closes #237

🤖 Generated with Claude Code

arkavo-com and others added 2 commits May 24, 2026 19:38
#237)

Two related changes against VMK#237 (extended-collider conformance bucket
collapse) plus a finding that reframes the bug.

* **Diagnostic harness** (`SpringBoneCollision.metal`,
  `SpringBoneBuffers.swift`, `SpringBoneComputeSystem.swift`,
  `VRMRenderer.swift`): per-bone last-write buffer the sphere/capsule
  kernels populate with `(shape_type, angleLimit, distance, boundary,
  penetration, group_matched)` when opted in via
  `renderer.setInsideColliderDiagnosticsEnabled(true)`. Sentinel
  shape_type values distinguish "never ran" / "kernel-entered no match"
  / "outside-branch with inside-flag=0" / "inside-sphere fired" /
  "inside-capsule fired" so future investigations can pin parameter-
  propagation bugs without re-instrumenting.
* **Substep ordering**: sphere + capsule collision now interleave with
  the distance constraint iteration (`SpringBoneComputeSystem.swift`'s
  XPBD loop). Inside-* containment is re-applied after every distance
  pass instead of only once at the end; plane stays outside the loop
  (only outside-collision semantics). Defensive improvement for any
  scenario where distance constraint can pull a contained joint back
  outside the surface.

**Finding** (`SpringBoneExtendedColliderBehaviorTests` new test
`testInsideSphereAngleLimitReachesShader`): the diagnostic confirms
`BoneParams.angleLimit` reaches the shader correctly (1.047 rad / 60°
read by every non-root bone). The conformance bucket collapse for
`springbone_extended_isphere_*` variants is **not** a propagation bug —
chain joints stay at distance 0.0046 / 0.0506 / 0.1005 m from the
sphere centre, well inside the 0.20 m and 0.40 m boundaries at pmed /
ploose. Inside-sphere collision correctly never engages; only ptight
(r=0.10) clips the chain (its distinct bucket). Angle-limit values
30°/60°/90° collapse because the chain's actual swing never exceeds
30° from bind, so none of the cones bite. Fixture-engineering
outcome, not a VMK bug.

Suite: 170 tests, 0 unexpected failures (added one diagnostic test).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Substep ordering change interleaves sphere/capsule collision with the
distance-constraint iteration loop — verifying the static T-pose render
is byte-near-identical (88-byte delta vs prior PNG, no visible artifact).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@arkavo-com arkavo-com marked this pull request as draft May 29, 2026 21:05
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

VRMC_springBone_extended_collider parsed but applied inconsistently — chaotic per-variant clustering, doesn't track swept axes

1 participant