Skip to content

3.0.0-alpha.0 integration - #791

Open
DennisSmolek wants to merge 58 commits into
nextfrom
integration/3.0-alpha
Open

DennisSmolek wants to merge 58 commits into
nextfrom
integration/3.0-alpha

Conversation

@DennisSmolek

Copy link
Copy Markdown
Member

Integration branch for @react-three/rapier 3.0.0-alpha.0. It combines the reviewed 3.0 PRs with the cross-PR wiring they need, so next gets one consistent, green state.

Merged PRs

Merged in this order with git merge --no-ff, so each PR's commits and history are kept:

  1. chore: 3.0 foundation (yarn 4, r3f v10 stack, rollup build, CI, alpha prerelease) #787 feat/3.0-foundation: yarn 4, r3f 10 canary, three 0.186, React 19.2, rollup build with an exports map, CI, changesets alpha prerelease
  2. feat!: step physics in the r3f v10 scheduler's fixed-step phase #788 feat/v10-scheduler-stepping: physics stepping on the r3f v10 scheduler's fixed-step phase
  3. fix: 3.0 bug fixes batch #785 fix/3.0-bugfixes: bug fixes batch
  4. feat: kinematic character controller (useCharacterController + <CharacterController>) #783 feat/character-controller: kinematic character controller
  5. feat: soft bodies (SoftBody, useSoftBody) #786 feat/soft-bodies: <SoftBody> / useSoftBody
  6. docs: pmndrs/docs site, 3.0 migration guide and FAQ #784 docs/3.0-docs-site: pmndrs/docs site, migration guide, FAQ
  7. feat(demo): WebGPU/WebGL toggle, auto-discovered examples, vehicle example, #666 fixes #790 feat/demo-v10-webgpu: demo WebGPU/WebGL toggle, auto-discovered examples, vehicle example (added to the scope after the original plan)

Merging this PR merges all seven. Their head commits are contained in this branch, so GitHub marks each of them as merged when this lands. That only works with a merge commit. Do not squash or rebase, because that would also invalidate the SHA in .git-blame-ignore-revs.

Integration commits (on top of the merges)

  • test(soft-bodies): await create() in the test setup (6f38bcf): follow-up fix for the feat: soft bodies (SoftBody, useSoftBody) #786 merge. The setup raced create() against the mount callback, which leaves overlapping act() scopes. With feat!: step physics in the r3f v10 scheduler's fixed-step phase #788's deferred world teardown, the StrictMode test failed.

  • feat(character-controller): use the (world, dt) step callback contract (c009307):

    • <CharacterController> uses the dt argument of WorldStepCallback.
    • The unreleased getStepDt / world.timestep fallback is removed.
    • The docs and demo examples use (_world, dt), and the vehicle demo passes dt to updateVehicle.
    • The it.todo("is framerate independent with timeStep='vary'") test is now enabled. It covers steady 30 and 144 fps plus a jittery 30/120 fps mix, and checks the per-callback dt. It fails when the controller reads world.timestep.
  • feat(soft-bodies): publish the subpath entry and sync once per frame (37fbf0d):

    • @react-three/rapier/soft-bodies is a real subpath export (exports, files, typesVersions), built to dist/soft-bodies.*.
    • Soft-bodies sources now import core through ../index (before: ../hooks/hooks). The bundle therefore imports useRapier from @react-three/rapier instead of inlining a second rapier context.
    • New internal frameSyncCallbacks on the context. syncMeshes(alpha) calls them after the rigid bodies, and resetInterpolation forwards to them. Returning true keeps a demand frameloop ticking.
    • Soft bodies capture() their particles after every substep and write the geometry once per frame with sync(alpha). Tears are still handled per substep.
    • New test: at alpha 0.5, vertices attached to an interpolated kinematic body keep their simulated offset from the rendered body. The old per-step sync fails it by half a step.
    • New test: the subpath resolves to the same source modules as the main entry.
  • docs: document the merged 3.0 APIs and complete the migration guide (17b8507):

  • style: format with prettier 3 (8d7b56b): whole-repo prettier --write .. Only 8 stragglers changed, because the PRs had already formatted what they touched.

  • chore: ignore the prettier 3 reformat in git blame (e7dc1da): adds .git-blame-ignore-revs.

  • fix: resolve the remaining eslint errors (f828e75). Real bugs:

    • missing keys on array elements (collision-events explosions, compound collider nodes)
    • lookAt(undefined) through ?.…! in the stuttering demo
    • async Promise executors in physics.test that raced create()

    Cleanups: @ts-ignore → @ts-expect-error with a reason, or removed where nothing was suppressed. Function → () => T. prefer-const.

  • ci: make the lint and format job blocking (58a8424): removes continue-on-error, and DEVELOPMENT.md is updated to match.

Conflict resolution

Verification (fresh yarn install --immutable)

  • yarn build ✅ (core now emits index.* and soft-bodies.*)
  • yarn typecheck ✅ and yarn workspace demo typecheck ✅
  • yarn test: 124/124 passed, 3 consecutive runs (no todos left)
  • yarn check:packages (publint --strict + attw --pack) ✅. All resolution modes are green for ., ./soft-bodies and the addons.
  • yarn demo:build ✅
  • yarn lint: 0 errors (140 warnings). yarn format:check ✅
  • Docs: npx @pmndrs/docs@4 build docs builds 23 pages ✅
  • yarn changeset version dry run gives @react-three/rapier 3.0.0-alpha.0 and @react-three/rapier-addons 6.0.0-alpha.0. I reverted it; the release workflow does the bump.
  • Every commit except one passed the local pre-commit hook (typecheck, build, publint/attw, vitest). The exception is merge b0000d4 (feat: soft bodies (SoftBody, useSoftBody) #786), committed with --no-verify: its StrictMode test failure is the breakage fixed in the very next commit (6f38bcf).

Browser smoke test of the integrated demo

I ran the vite dev server and drove headless Chrome through playwright-core with WebGPU enabled (a real WebGPU adapter). It visited every auto-discovered route with ?renderer=webgpu and ?renderer=webgl, collecting console errors, page errors, HTTP errors and screenshots. Some routes also got interactions: keyboard driving, and toggling Debug or Interpolate 5×.

No errors on any route. The only console output was the expected noise:

  • drei's RGBELoader deprecation (both renderers)
  • r3f's "WebGlRenderer usage is deprecated" notice (WebGL)
  • the favicon 404 during the interaction pass

The intermittent WebGPU Destroyed texture "PMREM.cubeUv" did not show up in this run. In the screenshots, the character controller moves and collects coins, the vehicle drives, and the cloth hangs from its bar with debug lines on both renderers.

Example WebGPU WebGL
active-collision-types ok ok
all-colliders ok ok
all-shapes ok ok
api-usage ok ok
attractors ok ok
car ok ok
character-controller (driven: D+W, jump) ok ok
cluster ok ok
colliders ok ok
collision-events ok ok
components ok ok
contact-force-events ok ok
contact-skin ok ok
cradle (Debug toggled 5x) ok ok
damping ok ok
dynamic-type-change ok ok
immutable-props ok ok
instanced-meshes ok ok
instances-meshes-compound ok ok
joints ok ok
kinematics ok ok
locked-transforms ok ok
manual-step ok ok
mesh-collider-test ok ok
one-way-platform ok ok
performance ok ok
plinko (Interpolate toggled 5x) ok ok
rope-joint ok ok
sensors ok ok
snapshot ok ok
soft-bodies/cloth (Debug toggled 5x) ok ok
soft-bodies/jelly (Debug toggled 5x) ok ok
soft-bodies/rope (Debug toggled 5x) ok ok
spring ok ok
stuttering ok ok
transforms ok ok
vehicle (driven: W) ok ok

Known gaps / decisions for the maintainer

  • Addons version: the major changeset takes @react-three/rapier-addons from 5.0.0 to 6.0.0-alpha.0, not 3.0. Keep it, or add a fixed / manual version if addons should track core.

  • Lint warnings: 140 remain (91 no-unused-vars, 49 react-hooks/exhaustive-deps). They are non-fatal and I did not change them, because "fixing" hook deps can change behaviour.

  • Soft bodies:

    • rapier:late re-expresses rigid bodies when an ancestor moves during update. Soft bodies are not included, so a cloth whose parent moves after the sync catches up one frame later. This is documented.
    • feat: soft bodies (SoftBody, useSoftBody) #786's own known gaps still apply: no soft body collision events; volume skins don't follow torn-off pieces; cutSoftBody on trimesh cloth may not disconnect pieces; prop changes after mount are ignored.
  • Docs: two pre-existing anchors (from docs: pmndrs/docs site, 3.0 migration guide and FAQ #784) work on GitHub but not on the built site, because the pmndrs/docs slugger keeps ? and ,:

    • faq#how-do-i-get-the-penetration-depth-of-a-collision
    • basic-usage#moving-things-around-and-applying-forces

    The readme's "3.0 is in development" banner is unchanged.

  • r3f canary pin: r3f stays pinned to 10.0.0-canary.a056612 (from chore: 3.0 foundation (yarn 4, r3f v10 stack, rollup build, CI, alpha prerelease) #787) until alpha.6 ships.

  • Smoke test limits: it was automated and headless. Feel and visual quality on a real GPU, e.g. interpolation smoothness, were not judged by a human.

🤖 Generated with Claude Code

DennisSmolek and others added 30 commits October 10, 2026 12:07
…royed (#780)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rs (#781, #627)

Read positions via getX/getY/getZ so interleaved and normalized attributes
work, convert indices to Uint32Array, and wrap collider creation failures
in a descriptive error.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…564)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- packageManager yarn@4.18.1, nodeLinker node-modules (.yarnrc.yml)
- move root tooling from dependencies to devDependencies
- drop @manypkg/cli (only used by the `ci` script)
- demo/addons depend on workspace packages via workspace:*
- engines.node >=22, ignore .yarn state but keep releases/plugins/patches

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…unt (#711)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Drops the three-stdlib dependency from @react-three/rapier and the addons
package, which also pulled node-only code into some bundlers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds docs scripts (docs, docs:site:build) to package.json.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
)

The InstancedMesh bounding sphere is computed once from the initial
matrices and goes stale as bodies move. Recomputing it each step is
O(instances) per frame, so culling is disabled instead.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…730)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Disabling culling stays the default, but users can pass frustumCulled to
re-enable it, and changes after mount are applied.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Cloth, rope and volumetric bodies built from a three geometry on top of
Rapier 0.21 soft bodies. Position-only vertex welding, sync into the
parent's local space after every step, pins, rigid body attachments,
tear events. Physics drains soft body tear events to subscribers.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- @react-three/fiber + test-renderer 10.0.0-canary.a056612 (scheduler 0.3, fixed step),
  three/@types/three 0.186, react/react-dom 19.2.8, drei 11.0.0-alpha.7 (demo)
- peerDependencies: fiber >=10.0.0-0, react >=19.0 <19.3, three >=0.185.0;
  rapier-addons peers on @react-three/rapier ^3.0.0-0
- three-stdlib ^2.36.1 (2.35 imports LuminanceFormat, removed from three)
- vitest 5 + jsdom 30, typescript 5.9; vitest.config.mts replaces vite.config.mts and
  resolves workspace packages from src (no build needed); root tsconfig uses
  moduleResolution Bundler with matching `paths`
- utils.ts: type-only narrowing for fiber v10's readonly-tuple math types
- tests: await `create()` instead of racing it against mount callbacks (overlapping act
  scopes), expect the extra zero-delta priming frame from advanceFrames, re-baseline the
  physics snapshot (float noise from scheduler ms timing, see PR)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Mirrors react-three-jolt's build: `tsc -p tsconfig.build.json` emits per-file declarations,
rollup bundles each entry to dist/<name>.{mjs,cjs} with esbuild and rolls declarations up
into dist/<name>.{d.ts,d.mts,d.cts}.

- shared config in scripts/rollup-library.mjs; entry points are derived from each
  package's `exports` map, so a subpath entry (e.g. `./soft-bodies` -> src/soft-bodies/index.ts)
  only needs an `exports` + `files` entry. Relative imports of another entry are rewritten to
  the published specifier so core is never inlined twice.
- package.json: `exports` map (import/require with types), `type`, `sideEffects: false`,
  `engines`, narrowed `files`, repository/bugs/homepage/license metadata
- deps, peers and their subpaths are external
- `check:package` (publint --strict + attw --pack) per package, `yarn check:packages` at root
- drop babel config and the preconstruct dev postinstall

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Minimal fixes only; the demo migration proper (WebGPU, routing, perf overlay) is separate work.

- resolve @react-three/rapier(-addons) from workspace sources via vite aliases + tsconfig
  paths (keeps the old `preconstruct dev` behaviour: no build needed, HMR on library code)
- moduleResolution Bundler (exports maps of fiber v10 / drei 11 / three)
- `state.clock` is gone in v10: use `state.elapsed`
- `<Canvas shadows>` -> `gl={{ shadows: true }}` (keeps the default WebGL renderer)
- drei 11: `Text` comes from `@react-three/drei/legacy`; drop unused `useCamera`
- OrbitControls type from three/addons instead of three-stdlib
- SpringExample: stop deep-importing library internals (blocked by the exports map)
- replace r3f-perf (pins drei 9 / an old three-stdlib that breaks on three 0.186) with drei
  <Stats>

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- eslint.config.mjs mirrors react-three-jolt (js + typescript-eslint recommended, react,
  react-hooks rules-of-hooks/exhaustive-deps, jsx-a11y, eslint-config-prettier last);
  automatic JSX runtime everywhere
- prettier 3 with the repo's existing style options moved to .prettierrc, so the deferred
  reformat stays small (6 files differ today)
- `lint`, `format`, `format:check` scripts. The codebase is NOT reformatted or lint-fixed
  here (baseline: 29 errors / 126 warnings); that lands at the end of the 3.0 integration.
- pre-commit hook runs `yarn typecheck` instead of the full CI

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…g, refs (#683, #730, #780)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ci.yml (replaces test.yml): typecheck, test, build + publint/attw, demo build, and a
  non-blocking lint/format job; runs on PRs and pushes to main and next, with concurrency
  cancellation and read-only permissions
- shared .github/actions/setup composite: corepack (yarn 4) + setup-node with yarn cache +
  `yarn install --immutable`
- current action majors: checkout v7, setup-node v7, configure-pages v6,
  upload-pages-artifact v5, deploy-pages v5 (supersedes #733)
- release.yml mirrors react-three-jolt: runs on main and next, owner guard, permissions
  (contents/pull-requests write, id-token for provenance), yarn 4 publish env;
  `changeset:version` / `changeset:publish` scripts (publish builds and tests first)
- @changesets/cli 3; `yarn changeset pre enter alpha` (.changeset/pre.json), so `next`
  releases 3.0.0-alpha.n under the `alpha` dist-tag; `prerelease:enter`/`prerelease:exit`
  scripts replace the canary ones
- changeset for the r3f v10 stack / package format (core + addons, major)
- dependabot: root workspace + github-actions ecosystem, ignore majors of the r3f stack
- .nvmrc v22.22.2 (jsdom 30 needs >=22.22.2), demo `typecheck` script

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
DennisSmolek and others added 28 commits October 10, 2026 12:27
Split Physics stepping into stepWorld(dt), run once per fixed substep
(interpolation snapshot, before-step callbacks, world.step, after-step
callbacks, collision/contact-force/soft-body tear event draining), and
syncMeshes(alpha), run once per rendered frame (sleep/wake events,
interpolated transforms, a single invalidate() while bodies are awake).

With updateLoop="follow" and a scheduler that supports fixed phases
(@pmndrs/scheduler >= 0.3), each world steps in a dedicated fixed phase
`rapier:fixed:<timeStep>:<maxSubSteps>` inserted after `physics`, and
meshes sync in a per-frame `rapier` phase before `update`, with the
phase's overstep as the interpolation alpha. The shared `physics` phase
is never retimed. Without fixed-phase support the same contract runs on
our own accumulator inside a per-frame job. timeStep="vary" steps once
per frame with the clamped delta; updateLoop="independent" keeps its own
rAF loop (whose first frame no longer reports a huge delta).

- New maxSubSteps prop (default 5) bounds catch-up; surplus time is
  dropped (#697).
- paused disables the step job; no time is banked, resume does not burst.
- Manual step() works while paused and syncs meshes immediately.
- updatePriority is now ordering within the phase only (BREAKING).
- useBeforePhysicsStep / useAfterPhysicsStep callbacks receive
  (world, dt); single-argument callbacks keep working.
- Bodies are synced into their CURRENT parent space: the parent inverse
  world matrix is re-read every frame, so moving or rotating an ancestor
  after mount keeps visuals on their bodies, sleeping ones included (#36).
- The world is freed in a microtask after unmount so child cleanups run
  against a live world and StrictMode remounts are safe.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Debug kept replacing its LineSegments geometry with a new BufferGeometry
every frame, and the last one leaked on every toggle. Keep one geometry,
rewrite its attributes in place (grow only when needed, upload only the
used range, draw range for the vertex count) and dispose it on unmount.
Rapier's RGBA colors stay a 4-component attribute, which both the WebGL
(vertexAlphas) and WebGPU (vec4 vertexColor node) renderers read. The
job runs in `update`, after meshes were synced in the `rapier` phase.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Fixed-step determinism, maxSubSteps cap, pause without burst, and
interpolation by overstep, each under both the scheduler fixed phase and
the accumulator fallback; dt in step hooks; vary mode; manual step while
paused; multiple worlds with different timeSteps; demand frameloop
invalidation; independent loop; moving/rotating ancestors (#36); debug
geometry reuse and disposal across toggles (#666).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The foundation re-baseline captured float noise from lerping the old
accumulator alpha; with the scheduler stepping the synced positions are
again bit-identical to the values in next.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Tear-safe sync (skip stale layouts, re-sync after tears, no NaN writes),
skin the original render mesh through a weld map (UVs, hard normals),
own position-only weld (drops three-stdlib), lazy material, ref refresh on
world change, corrected onTear docs, volume tear warning, prev/curr
buffers and alpha for later interpolation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…eMemo

Also apply prettier 3 formatting from the foundation tooling and make
importRapier's binding const.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…rpolation resets, phase reuse)

- Sync bodies parent-first: the state map keeps a scene-depth order,
  rebuilt only when bodies register/unregister, so a nested RigidBody no
  longer lags the body carrying it by a frame.
- Late pass in a `rapier:late` phase right before render: if any parent
  read by the sync moved afterwards (e.g. an ancestor animated in a
  regular useFrame, drei <Float>), re-express the already synced world
  poses in the new parent space. One matrix compare per distinct parent
  when nothing moved (#36).
- Compare the parent object too, not just its space version, so a
  re-parented body picks up its new parent space.
- Take the interpolation snapshot after before-step callbacks (teleports
  there no longer lerp) and forget previous poses after a manual step(),
  setWorld(), and paused/interpolate/timeStep changes, so resuming or
  stepping manually never renders behind the latest pose.
- One fixed phase per world (`rapier:fixed:<n>`), retimed in place with
  setPhaseTimestep and returned to a pool on unmount, instead of a phase
  per (timeStep, maxSubSteps) pair: changing timeStep no longer grows the
  scheduler's phase list, and remounts reuse phases.
- Free the EventQueue together with the world.
- Debug: the lines show the latest step, ahead of interpolated meshes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…reparenting and phase reuse

Ported from the PR review repros (R1, R4, R5, R6, R9, R10); each failed
before the fix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Copy-paste example on Rapier's DynamicRayCastVehicleController using
QueryFilterFlags.EXCLUDE_SENSORS for the wheel rays.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…null ref

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: package.json files keep the foundation's yarn 4 / exports setup with #785's removal of three-stdlib (no remaining importers, incl. demo); demo App.tsx keeps #785's ComponentRef<typeof OrbitControls> typing and drops the now-unused three/addons type import. yarn.lock regenerated.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts:
- Physics.tsx: kept #788's stepping structure (stepWorld/syncMeshes/lateSync); #788 already carried softBodyTearCallbacks and the per-substep drainSoftBodyTearEvents verbatim, so the result equals #788's file (deduplicated the SoftBodyTearCallback type).
- demo/vite.config.mts, tsconfig.json: kept the foundation's generic src aliases/paths (@react-three/rapier/<sub> -> src/<sub>/index.ts), which already resolve soft-bodies; dropped #786's temporary soft-bodies-only alias and paths entry.
- demo/src/App.tsx: registered both the character-controller and soft-* routes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The setup raced ReactThreeTestRenderer.create against the mount callback,
leaving act() scopes overlapping. On r3f v10 that does not flush reliably
(the foundation fixed the other suites the same way), and the StrictMode
test failed once combined with the deferred world teardown of PR 788.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: root package.json scripts keep both the foundation's yarn 4 scripts and the docs/docs:site:build scripts.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts: demo/src/App.tsx takes #790's version (auto-discovered examples via demo/src/examples.ts, renderer toggle, GlShim). The manual character-controller and soft-* route registrations added by #783/#786 are dropped: those examples now register as character-controller and soft-bodies/{cloth,jelly,rope}.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The stepping rewrite passes every before/after-step callback the length of
the step it runs (WorldStepCallback = (world, dt) => void). Use that dt in
<CharacterController> and in the docs/demo examples instead of falling back
to world.timestep, which is only written after the before-step callbacks
and therefore lags one step behind with timeStep="vary".

- Remove the temporary getStepDt helper (never released).
- The vehicle demo passes the step dt to updateVehicle as well.
- Enable the "is framerate independent with timeStep='vary'" test: steady
  30 and 144 fps and a jittery 30/120 fps mix all travel the same distance,
  and every callback receives its frame's delta (fails with world.timestep).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Entry point:
- Wire @react-three/rapier/soft-bodies as a real subpath export (exports,
  files and typesVersions), built by the shared rollup config into
  dist/soft-bodies.{mjs,cjs,d.ts,d.mts,d.cts}. publint and attw are green in
  every resolution mode.
- Soft-bodies sources import core through ../index, so the bundle imports
  useRapier from @react-three/rapier instead of inlining a second copy of
  the rapier context (they reached into ../hooks/hooks before).
- The temporary vite alias / tsconfig paths entry from the soft-bodies PR
  were already replaced by the foundation's generic src aliases in the
  merge; tests now also check that the subpath resolves to the same source
  modules as the main entry.

Per-frame sync:
- <Physics> gets an internal frameSyncCallbacks set on the context. Its
  per-frame sync (syncMeshes) calls each entry with the interpolation alpha
  after writing the rigid bodies, and resetInterpolation forwards to them
  (manual step, setWorld, paused/interpolate/timeStep changes). Returning
  true keeps a demand frameloop ticking.
- Soft bodies now capture() their particle buffers after every substep
  (afterStepCallbacks) and write the geometry once per frame with
  sync(alpha), blending the last two steps like the rigid bodies, so cloth
  attached to an interpolated rigid body no longer separates from it. Tear
  handling stays per substep. Unchanged frames (same step, same alpha) skip
  the geometry rewrite.
- New test: at alpha 0.5 the attached vertices keep the simulated offsets
  from the rendered (interpolated) body (fails by half a step with the old
  per-step sync).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Replace every TODO admonition in docs/ with documentation of the merged
APIs:

- update-loop / physics: the r3f v10 scheduler phases (rapier:fixed:<n>,
  rapier, rapier:late), timeStep and "vary", maxSubSteps (default 5),
  interpolation, paused and manual step, the new updatePriority meaning,
  demand frameloop, independent loop, moving parents and the fallback for
  older schedulers. maxSubSteps is added to the props table.
- hooks: step callbacks receive (world, dt) and run per substep.
- New pages: api/character-controller (CharacterController and
  useCharacterController) and api/soft-bodies (the
  @react-three/rapier/soft-bodies entry point, per-frame interpolated sync,
  tears, known limitations).
- performance, faq, rigid-body, debug (single reused geometry, WebGPU),
  collision-events (events fire per substep), instanced-rigid-bodies
  (frustum culling opt-in).
- migration: completed from every changeset plus the stepping notes, with
  WebGPU notes for Debug and the Attractor debug helper.
- readme: link the two new pages.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Whole-repo `prettier --write .` with the repo's .prettierrc. The merged PRs
already formatted the files they touched, so only the stragglers change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Real bugs:
- demo: elements created in arrays had no key (collision-events explosions,
  compound collider nodes), which React warns about and can mis-reconcile.
- demo: stuttering example called camera.lookAt with a non-null-asserted
  optional chain, which passed undefined when the model was not mounted.
- tests: physics.test used async Promise executors, racing create() against
  the mount callback (the overlapping act() pattern the foundation removed
  elsewhere); they now await create() and then the ready callback.

Cleanups:
- @ts-ignore -> @ts-expect-error with a reason where an error is really
  suppressed; dropped the directives that suppressed nothing, and cast the
  GLTF results through unknown instead.
- useConst: type the initializer as () => T instead of Function.
- prefer-const in the demo and the collider tests.

`yarn lint` now reports 0 errors (warnings remain: unused variables and
exhaustive-deps, which do not fail the build).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The repo is formatted with Prettier 3 and has no ESLint errors, so drop
continue-on-error from the lint job and update DEVELOPMENT.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@changeset-bot

changeset-bot Bot commented Oct 10, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 58a8424

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@react-three/rapier Major
@react-three/rapier-addons Major

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

This branch has not been deployed

No deployments
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.

1 participant