Skip to content

[Feature] Drive external physics backends via an EngineExtension coordinator - #1129

Merged
untoldengine merged 1 commit into
untoldengine:developfrom
miolabs:feature/physics_coordinator-upstream
Aug 5, 2026
Merged

[Feature] Drive external physics backends via an EngineExtension coordinator#1129
untoldengine merged 1 commit into
untoldengine:developfrom
miolabs:feature/physics_coordinator-upstream

Conversation

@miogds

@miogds miogds commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Summary

PR B of the narrowed phase-1 plan from discussion #1116 (follows #1123), reworked per your review guidance to sit entirely on the #1127 EngineExtension infrastructure — no runFrame seam, no engine-core changes. Rebased on current develop (post the repo-wide format commit); make lint passes clean on the branch. With no backend installed, nothing is even scheduled: the coordinator is only registered while a backend plugin is installed, so runtime behavior is unchanged by construction. The built-in integrator is untouched and keeps running legacy PhysicsComponents/KineticComponent entities in all cases (the coexistence rule from the plan).

  • Sources/UntoldEngine/Physics/PhysicsCoordinator.swiftPhysicsCoordinator conforming to EngineExtension (id: com.untoldengine.physics.coordinator). Its fixedUpdate runs once per fixed substep right after updatePhysicsSystem, and per substep: (1) diffs the RigidBodyComponent+ColliderComponent+LocalTransformComponent query result against the backend's known-body set → didAddBody/didRemoveBody (entity destruction needs no extra hook — component cleanup drops the entity out of the query and the diff catches it); (2) batch-writes kinematic targets; (3) backend.step; (4) batch-reads active transforms back into LocalTransformComponent for dynamic bodies, marking transformDirty/anyTransformDirty exactly like the built-in integrator; (5) drains events into an inert sink (delivery/USC wiring is PR C).
  • PhysicsBackendRegistry now registers the coordinator with EngineExtensionRegistry.shared.register(_:) on successful install (also pushing the current PhysicsWorldConfiguration to the backend) and unregisters it on uninstall — both outside the registry lock. The lockForRuntime() seam from [Feature] Add physics backend plugin interface #1123 is wired with zero core edits: the coordinator locks the registry on the first substep it simulates, freezing the backend choice for the run.
  • Configurable gravity: PhysicsCoordinator.setWorldConfiguration(_:) applies to the active backend and to the built-in integrator, whose hardcoded (0, -9.8, 0) in updatePhysicsSystem now reads the same configuration (default identical, hoisted out of the per-entity loop).
  • 15 unit tests with a recording mock backend: install/uninstall scheduling, world-config propagation, body add/remove lifecycle (destroy and component removal), descriptor snapshot fidelity, kinematic batch writes, dynamic read-back (including ignoring kinematic/unknown entities), per-substep call order, runtime lock, and built-in-integrator gravity parity/configurability.

Engine touch outside Physics/: one hoisted gravity read in Systems/PhysicsSystem.swift (3 lines). Follow-ups per the plan: PR C (event sink subscriptions + USC OnCollision), PR D (raycast facade).

Test plan

  • swift test --filter PhysicsCoordinatorTests — 15/15 pass
  • PhysicsBackendRegistryTests, ComponentRegistryTest, ECSTests, EngineExtensionLifecycleTest, CustomSystemTest — all pass
  • Full UntoldEngineTests module: 1060 tests, no new failures (the one failure, ExternalRenderExtensionPackageTests, fails identically on clean develop on this machine)

@miogds
miogds requested a review from untoldengine as a code owner August 4, 2026 05:49
@miogds
miogds force-pushed the feature/physics_coordinator-upstream branch from 0c942be to 3fec784 Compare August 4, 2026 09:10
…dinator

PR B of the physics plugin readiness plan (discussion untoldengine#1116), built on the
EngineExtension infrastructure from untoldengine#1127 instead of a custom runFrame
seam:

- PhysicsCoordinator (EngineExtension) steps the installed backend once
  per fixed substep: body-set diffing against the RigidBodyComponent +
  ColliderComponent query, batch kinematic writes, step, batch transform
  read-back into LocalTransformComponent, event drain into an inert sink
  (subscriptions arrive in the events PR).
- PhysicsBackendRegistry schedules/unschedules the coordinator in
  EngineExtensionRegistry on install/uninstall and locks for runtime on
  the first simulated substep.
- Gravity for the built-in integrator now reads from the global
  PhysicsWorldConfiguration (default unchanged at (0, -9.8, 0)), so
  legacy and backend-owned bodies share one gravity.

With no backend installed nothing is scheduled and behavior is unchanged;
the built-in integrator keeps running legacy entities either way.
@miogds
miogds force-pushed the feature/physics_coordinator-upstream branch from 3fec784 to b1f3039 Compare August 4, 2026 09:37
@untoldengine
untoldengine merged commit c3b8b0e into untoldengine:develop Aug 5, 2026
4 checks passed
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.

2 participants