SoA world: joints, force/torque lanes, capsules and boxes #2

Merged
nibu merged 4 commits from nibu/soa-joints into main 2026-11-10 17:14:36 +00:00
Member

Stacked on #1 (base nibu/soa-world; retarget to main after #1 merges). Do not merge before #1. Rung 1 of the Bad Day Simulator ladder, Ymir half. Follow-up nibu-body:follow_up:ymir-soa-lodur-needs, items 1-3 and the body layout; items 4 (contact facts) and 5 (public surface in Ymir.Core) are not done.

Added (ABI 2; docs/soa-world.md has the contract)

  • One body descriptor, ymir_box3d_soa_add_body, replaces add_sphere/add_static_box: sphere, capsule (segment along local Y), dynamic box, static box; rotation, density, friction, restitution, rolling resistance, parent and limb index lanes (set at creation, not read by Box3D).
  • Joints: ymir_box3d_soa_add_joint, revolute, spherical, weld, from a world anchor and axis (creation pose is zero), optional limits. Joint state lanes: angle (Box3D's), world orientation of frame A, relative rotation (w >= 0), relative angular velocity in frame A.
  • Per-body force and torque write lanes, mutable through the view; step applies them to dynamic bodies and clears them (one step of intent). A non-finite value refuses the whole step before any body is touched.
  • 10 new tests (16 total, all pass, Linux x64, Debug native build): hinge angle vs relative-rotation lanes, off-axis hinge, limit and weld, spherical joint, exact force/torque acceleration and clearing, NaN refusal, capsule/box rest heights, refusals, bit-identical repeat of a torque-driven jointed body.

Found while testing: creating Box3D worlds from two threads at once aborts the host (b3CreateWorld, SIGILL). xunit ran two test classes in parallel; the test assembly is now serial. Not a Ymir fix, a constraint to know.

Not measured: Windows build, Unity consumption (unrun); cross-platform and cross-process bit-identity (unclaimed; repeat runs are in-process). Joint motors and springs are not exposed: Lodur drives joints from torque lanes, which limits stable stiffness (see Lodur's docs/first-slice.md). Box3DSoaWorld is still internal; InternalsVisibleTo lists Lodur.Body.Ymir and Lodur.Tests until a public surface exists.

Consumer: GameCult/Lodur nibu/first-slice.

Merge nothing.

🤖 Generated with Claude Code

Stacked on #1 (base `nibu/soa-world`; retarget to `main` after #1 merges). Do not merge before #1. Rung 1 of the Bad Day Simulator ladder, Ymir half. Follow-up `nibu-body:follow_up:ymir-soa-lodur-needs`, items 1-3 and the body layout; items 4 (contact facts) and 5 (public surface in `Ymir.Core`) are not done. **Added** (ABI 2; `docs/soa-world.md` has the contract) - One body descriptor, `ymir_box3d_soa_add_body`, replaces `add_sphere`/`add_static_box`: sphere, capsule (segment along local Y), dynamic box, static box; rotation, density, friction, restitution, rolling resistance, **parent and limb index lanes** (set at creation, not read by Box3D). - Joints: `ymir_box3d_soa_add_joint`, revolute, spherical, weld, from a world anchor and axis (creation pose is zero), optional limits. **Joint state lanes**: angle (Box3D's), world orientation of frame A, relative rotation (w >= 0), relative angular velocity in frame A. - **Per-body force and torque write lanes**, mutable through the view; `step` applies them to dynamic bodies and clears them (one step of intent). A non-finite value refuses the whole step before any body is touched. - 10 new tests (16 total, all pass, Linux x64, Debug native build): hinge angle vs relative-rotation lanes, off-axis hinge, limit and weld, spherical joint, exact force/torque acceleration and clearing, NaN refusal, capsule/box rest heights, refusals, bit-identical repeat of a torque-driven jointed body. **Found while testing**: creating Box3D worlds from two threads at once aborts the host (`b3CreateWorld`, SIGILL). xunit ran two test classes in parallel; the test assembly is now serial. Not a Ymir fix, a constraint to know. **Not measured**: Windows build, Unity consumption (unrun); cross-platform and cross-process bit-identity (unclaimed; repeat runs are in-process). Joint motors and springs are not exposed: Lodur drives joints from torque lanes, which limits stable stiffness (see Lodur's `docs/first-slice.md`). `Box3DSoaWorld` is still internal; `InternalsVisibleTo` lists `Lodur.Body.Ymir` and `Lodur.Tests` until a public surface exists. Consumer: GameCult/Lodur `nibu/first-slice`. Merge nothing. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
ABI 2. One body descriptor replaces add_sphere/add_static_box (kinds: sphere, capsule, box, static box).
Revolute, spherical and weld joints created from a world anchor and axis, with joint state lanes (angle,
frame A orientation, relative rotation, relative angular velocity). Per-body force and torque write lanes,
applied before the step then cleared; non-finite values refuse the step. Parent and limb index lanes.
Tests run serially: concurrent b3CreateWorld aborts the host.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
A bare sphere or capsule rolls on without it, so a fallen head never settles.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
nibu changed target branch from nibu/soa-world to main 2026-11-10 17:14:04 +00:00
nibu changed title from Draft: SoA world: joints, force/torque lanes, capsules and boxes (stacked on #1) to SoA world: joints, force/torque lanes, capsules and boxes 2026-11-10 17:14:35 +00:00
Author
Member

Steward check before merge (nibu, 2026-11-10). Retargeted from nibu/soa-world to main after #1 merged; 0 conflicts.

  • dotnet test tests/Ymir.Box3D.Soa.Tests at the head: 16/16 pass. tests/Ymir.Box3D.Parity: 20/20 pass.
  • Falsification: making the force/torque lane clear after the step a no-op (lane[k][i] = lane[k][i]) fails 1 test ("lanes act before the step and clear after"), so the clear is pinned.
  • Merged.
Steward check before merge (nibu, 2026-11-10). Retargeted from `nibu/soa-world` to main after #1 merged; 0 conflicts. - `dotnet test tests/Ymir.Box3D.Soa.Tests` at the head: 16/16 pass. `tests/Ymir.Box3D.Parity`: 20/20 pass. - Falsification: making the force/torque lane clear after the step a no-op (`lane[k][i] = lane[k][i]`) fails 1 test ("lanes act before the step and clear after"), so the clear is pinned. - Merged.
nibu merged commit 7ba31c30fc into main 2026-11-10 17:14:36 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
GameCult/Ymir!2
No description provided.