@vgai/engine 0.5.22 → 0.5.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (303) hide show
  1. package/README.md +6 -3
  2. package/dist/adapter/adapter-module.d.ts.map +1 -1
  3. package/dist/adapter/adapter-module.js +2 -4
  4. package/dist/adapter/adapter-surface.d.ts +1 -1
  5. package/dist/adapter/adapter-surface.js +1 -1
  6. package/dist/adapter/authoring.d.ts +19 -21
  7. package/dist/adapter/authoring.d.ts.map +1 -1
  8. package/dist/adapter/authoring.js +2 -2
  9. package/dist/adapter/binding.d.ts +341 -0
  10. package/dist/adapter/binding.d.ts.map +1 -0
  11. package/dist/adapter/binding.js +148 -0
  12. package/dist/adapter/colyseus-networking-adapter.d.ts.map +1 -1
  13. package/dist/adapter/colyseus-networking-adapter.js +23 -4
  14. package/dist/adapter/entrypoint-selection-readers.d.ts +24 -0
  15. package/dist/adapter/entrypoint-selection-readers.d.ts.map +1 -0
  16. package/dist/adapter/entrypoint-selection-readers.js +63 -0
  17. package/dist/adapter/entrypoint-selection-source.d.ts.map +1 -1
  18. package/dist/adapter/entrypoint-selection-source.js +1 -44
  19. package/dist/adapter/finders/scenes-from-entrypoint-selection.d.ts.map +1 -1
  20. package/dist/adapter/finders/scenes-from-entrypoint-selection.js +1 -52
  21. package/dist/adapter/host-context.d.ts +46 -15
  22. package/dist/adapter/host-context.d.ts.map +1 -1
  23. package/dist/adapter/host-context.js +2 -9
  24. package/dist/adapter/index.d.ts +11 -12
  25. package/dist/adapter/index.d.ts.map +1 -1
  26. package/dist/adapter/index.js +9 -10
  27. package/dist/adapter/ingest/game-contract-seams.d.ts +5 -0
  28. package/dist/adapter/ingest/game-contract-seams.d.ts.map +1 -1
  29. package/dist/adapter/ingest/game-contract-seams.js +1 -0
  30. package/dist/adapter/ingest/game-contract.d.ts +22 -3
  31. package/dist/adapter/ingest/game-contract.d.ts.map +1 -1
  32. package/dist/adapter/ingest/game-contract.js +0 -3
  33. package/dist/adapter/ingest/scene-capture.d.ts +19 -23
  34. package/dist/adapter/ingest/scene-capture.d.ts.map +1 -1
  35. package/dist/adapter/ingest/scene-capture.js +143 -17
  36. package/dist/adapter/ingest/visible-capture-window.d.ts +34 -0
  37. package/dist/adapter/ingest/visible-capture-window.d.ts.map +1 -1
  38. package/dist/adapter/native-debug-module.d.ts +40 -5
  39. package/dist/adapter/native-debug-module.d.ts.map +1 -1
  40. package/dist/adapter/native-debug-module.js +36 -9
  41. package/dist/adapter/rapier-physics-adapter.d.ts +31 -1
  42. package/dist/adapter/rapier-physics-adapter.d.ts.map +1 -1
  43. package/dist/adapter/rapier-physics-adapter.js +50 -36
  44. package/dist/adapter/renderer-config.d.ts +2 -2
  45. package/dist/adapter/renderer-config.js +2 -2
  46. package/dist/adapter/root-adapter.d.ts +7 -7
  47. package/dist/adapter/root-adapter.js +4 -4
  48. package/dist/adapter/system-adapter.d.ts +45 -7
  49. package/dist/adapter/system-adapter.d.ts.map +1 -1
  50. package/dist/adapter/system-seam-contract.d.ts +3 -3
  51. package/dist/adapter/system-seam-contract.js +3 -3
  52. package/dist/adapter/system-slot.d.ts +168 -0
  53. package/dist/adapter/system-slot.d.ts.map +1 -0
  54. package/dist/adapter/system-slot.js +229 -0
  55. package/dist/adapter/transform.d.ts +11 -1
  56. package/dist/adapter/transform.d.ts.map +1 -1
  57. package/dist/ai/navigation.d.ts +17 -1
  58. package/dist/ai/navigation.d.ts.map +1 -1
  59. package/dist/ai/navigation.js +26 -5
  60. package/dist/animation/anim-graph-types.d.ts +11 -3
  61. package/dist/animation/anim-graph-types.d.ts.map +1 -1
  62. package/dist/animation/theatre-clock-binding.d.ts +4 -4
  63. package/dist/animation/theatre-clock-binding.js +4 -4
  64. package/dist/animation/xstate-animation-binding.d.ts.map +1 -1
  65. package/dist/animation/xstate-animation-binding.js +2 -2
  66. package/dist/animation/xstate-animation-meta.d.ts +13 -0
  67. package/dist/animation/xstate-animation-meta.d.ts.map +1 -1
  68. package/dist/animation/xstate-animation-meta.js +14 -0
  69. package/dist/asset-formats/index.d.ts +4 -7
  70. package/dist/asset-formats/index.d.ts.map +1 -1
  71. package/dist/asset-formats/index.js +4 -7
  72. package/dist/asset-formats/render-env.d.ts +5 -8
  73. package/dist/asset-formats/render-env.d.ts.map +1 -1
  74. package/dist/asset-formats/render-env.js +3 -6
  75. package/dist/asset-parse-error.d.ts +2 -5
  76. package/dist/asset-parse-error.d.ts.map +1 -1
  77. package/dist/asset-parse-error.js +2 -5
  78. package/dist/audio/bus-mixer.d.ts +101 -0
  79. package/dist/audio/bus-mixer.d.ts.map +1 -0
  80. package/dist/audio/bus-mixer.js +115 -0
  81. package/dist/core/frame-pacing.d.ts +1 -1
  82. package/dist/core/frame-pacing.js +2 -2
  83. package/dist/core/game-loop.d.ts +1 -3
  84. package/dist/core/game-loop.d.ts.map +1 -1
  85. package/dist/core/game-loop.js +1 -3
  86. package/dist/core/seeded-random.d.ts +1 -1
  87. package/dist/core/sim-clock.d.ts +5 -6
  88. package/dist/core/sim-clock.d.ts.map +1 -1
  89. package/dist/core/sim-clock.js +4 -5
  90. package/dist/core/types.d.ts +1 -3
  91. package/dist/core/types.d.ts.map +1 -1
  92. package/dist/defaults.d.ts +6 -14
  93. package/dist/defaults.d.ts.map +1 -1
  94. package/dist/defaults.js +6 -14
  95. package/dist/dev/register-render-vitals.d.ts +1 -4
  96. package/dist/dev/register-render-vitals.d.ts.map +1 -1
  97. package/dist/dev/register-render-vitals.js +1 -4
  98. package/dist/dev/render-debug-adapter.d.ts +2 -3
  99. package/dist/dev/render-debug-adapter.d.ts.map +1 -1
  100. package/dist/dev/render-debug-adapter.js +2 -3
  101. package/dist/dev/render-vitals.d.ts +3 -3
  102. package/dist/dev/render-vitals.js +3 -3
  103. package/dist/dev/webgl-frame-capture.d.ts +1 -1
  104. package/dist/dev/webgl-frame-capture.js +1 -1
  105. package/dist/index.d.ts +2 -5
  106. package/dist/index.d.ts.map +1 -1
  107. package/dist/index.js +2 -4
  108. package/dist/input/host-pointer.d.ts +122 -0
  109. package/dist/input/host-pointer.d.ts.map +1 -0
  110. package/dist/input/host-pointer.js +160 -0
  111. package/dist/input/input-manager.d.ts +18 -5
  112. package/dist/input/input-manager.d.ts.map +1 -1
  113. package/dist/input/input-manager.js +33 -13
  114. package/dist/input/rebind-controller.d.ts.map +1 -1
  115. package/dist/input/rebind-controller.js +6 -8
  116. package/dist/input/schema.d.ts.map +1 -1
  117. package/dist/input/schema.js +56 -28
  118. package/dist/manifest/filename.d.ts.map +1 -1
  119. package/dist/manifest/filename.js +8 -12
  120. package/dist/manifest/load.d.ts +10 -10
  121. package/dist/manifest/load.d.ts.map +1 -1
  122. package/dist/manifest/load.js +9 -6
  123. package/dist/manifest/schema.d.ts +17 -21
  124. package/dist/manifest/schema.d.ts.map +1 -1
  125. package/dist/manifest/schema.js +45 -35
  126. package/dist/physics/collider-dimensions.d.ts +8 -12
  127. package/dist/physics/collider-dimensions.d.ts.map +1 -1
  128. package/dist/physics/collider-dimensions.js +8 -12
  129. package/dist/pixi/scene-capture.d.ts +11 -13
  130. package/dist/pixi/scene-capture.d.ts.map +1 -1
  131. package/dist/react/world-state.d.ts +1 -1
  132. package/dist/react/world-state.d.ts.map +1 -1
  133. package/dist/render/light-camera-factory.d.ts +5 -7
  134. package/dist/render/light-camera-factory.d.ts.map +1 -1
  135. package/dist/render/light-camera-factory.js +8 -11
  136. package/dist/render/material-factory.js +1 -1
  137. package/dist/render/render-batch-system.d.ts.map +1 -1
  138. package/dist/render/render-features.d.ts +17 -6
  139. package/dist/render/render-features.d.ts.map +1 -1
  140. package/dist/render/render-features.js +0 -28
  141. package/dist/runtime/create-runtime.d.ts +34 -25
  142. package/dist/runtime/create-runtime.d.ts.map +1 -1
  143. package/dist/runtime/create-runtime.js +218 -93
  144. package/dist/runtime/debug-bridge.d.ts +12 -0
  145. package/dist/runtime/debug-bridge.d.ts.map +1 -1
  146. package/dist/runtime/debug-bridge.js +4 -0
  147. package/dist/runtime/debug-registry.d.ts +95 -8
  148. package/dist/runtime/debug-registry.d.ts.map +1 -1
  149. package/dist/runtime/debug-registry.js +24 -3
  150. package/dist/runtime/dev-build.d.ts +2 -2
  151. package/dist/runtime/dev-build.js +2 -2
  152. package/dist/runtime/game-input-seams.d.ts +2 -2
  153. package/dist/runtime/game-input-seams.d.ts.map +1 -1
  154. package/dist/runtime/game.d.ts +102 -41
  155. package/dist/runtime/game.d.ts.map +1 -1
  156. package/dist/runtime/game.js +80 -54
  157. package/dist/runtime/mount-game.d.ts +0 -16
  158. package/dist/runtime/mount-game.d.ts.map +1 -1
  159. package/dist/runtime/mount-game.js +8 -39
  160. package/dist/runtime/mount-manifest.d.ts +22 -14
  161. package/dist/runtime/mount-manifest.d.ts.map +1 -1
  162. package/dist/runtime/mount-manifest.js +18 -18
  163. package/dist/runtime/playtest.d.ts +22 -0
  164. package/dist/runtime/playtest.d.ts.map +1 -0
  165. package/dist/runtime/playtest.js +12 -0
  166. package/dist/runtime/run-ticks-settled.d.ts +34 -0
  167. package/dist/runtime/run-ticks-settled.d.ts.map +1 -0
  168. package/dist/runtime/run-ticks-settled.js +59 -0
  169. package/dist/runtime/state-bridge.d.ts +2 -3
  170. package/dist/runtime/state-bridge.d.ts.map +1 -1
  171. package/dist/runtime/state-bridge.js +1 -0
  172. package/dist/setup/setup-audio.d.ts +12 -0
  173. package/dist/setup/setup-audio.d.ts.map +1 -1
  174. package/dist/setup/setup-audio.js +9 -10
  175. package/dist/setup/setup-renderer.d.ts +2 -25
  176. package/dist/setup/setup-renderer.d.ts.map +1 -1
  177. package/dist/setup/setup-renderer.js +2 -31
  178. package/dist/world3d-react/r3f-root-factory.d.ts +7 -7
  179. package/dist/world3d-react/r3f-root-factory.js +7 -7
  180. package/dist/world3d-react/rapier-physics-bridge.d.ts +9 -8
  181. package/dist/world3d-react/rapier-physics-bridge.d.ts.map +1 -1
  182. package/dist/world3d-react/rapier-physics-bridge.js +100 -48
  183. package/package.json +1 -1
  184. package/schemas/engine-capabilities.json +16 -14
  185. package/schemas/vgai-project.schema.json +35 -26
  186. package/src/adapter/adapter-module.ts +2 -4
  187. package/src/adapter/adapter-surface.ts +1 -1
  188. package/src/adapter/authoring.ts +19 -21
  189. package/src/adapter/binding.ts +468 -0
  190. package/src/adapter/colyseus-networking-adapter.ts +20 -5
  191. package/src/adapter/entrypoint-selection-readers.ts +66 -0
  192. package/src/adapter/entrypoint-selection-source.ts +1 -40
  193. package/src/adapter/finders/scenes-from-entrypoint-selection.ts +1 -48
  194. package/src/adapter/host-context.ts +45 -15
  195. package/src/adapter/index.ts +23 -11
  196. package/src/adapter/ingest/game-contract-seams.ts +1 -0
  197. package/src/adapter/ingest/game-contract.ts +22 -3
  198. package/src/adapter/ingest/scene-capture.ts +190 -39
  199. package/src/adapter/ingest/visible-capture-window.ts +35 -0
  200. package/src/adapter/native-debug-module.ts +66 -9
  201. package/src/adapter/rapier-physics-adapter.ts +76 -20
  202. package/src/adapter/renderer-config.ts +2 -2
  203. package/src/adapter/root-adapter.ts +7 -7
  204. package/src/adapter/system-adapter.ts +43 -8
  205. package/src/adapter/system-seam-contract.ts +3 -3
  206. package/src/adapter/system-slot.ts +291 -0
  207. package/src/adapter/transform.ts +18 -1
  208. package/src/ai/navigation.ts +30 -3
  209. package/src/animation/anim-graph-types.ts +11 -3
  210. package/src/animation/theatre-clock-binding.ts +4 -4
  211. package/src/animation/xstate-animation-binding.ts +2 -2
  212. package/src/animation/xstate-animation-meta.ts +16 -0
  213. package/src/asset-formats/index.ts +4 -7
  214. package/src/asset-formats/render-env.ts +3 -6
  215. package/src/asset-parse-error.ts +2 -5
  216. package/src/audio/bus-mixer.ts +161 -0
  217. package/src/core/frame-pacing.ts +2 -2
  218. package/src/core/game-loop.ts +1 -3
  219. package/src/core/seeded-random.ts +1 -1
  220. package/src/core/sim-clock.ts +5 -6
  221. package/src/core/types.ts +1 -3
  222. package/src/defaults.ts +6 -14
  223. package/src/dev/register-render-vitals.ts +1 -4
  224. package/src/dev/render-debug-adapter.ts +2 -3
  225. package/src/dev/render-vitals.ts +3 -3
  226. package/src/dev/webgl-frame-capture.ts +1 -1
  227. package/src/index.ts +2 -5
  228. package/src/input/host-pointer.ts +230 -0
  229. package/src/input/input-manager.ts +34 -13
  230. package/src/input/input-types.ts +1 -1
  231. package/src/input/rebind-controller.ts +6 -8
  232. package/src/input/schema.ts +216 -188
  233. package/src/manifest/filename.ts +8 -12
  234. package/src/manifest/load.ts +17 -13
  235. package/src/manifest/schema.ts +55 -47
  236. package/src/physics/collider-dimensions.ts +8 -12
  237. package/src/pixi/scene-capture.ts +12 -15
  238. package/src/react/world-state.tsx +1 -1
  239. package/src/render/light-camera-factory.ts +8 -11
  240. package/src/render/material-factory.ts +1 -1
  241. package/src/render/render-batch-system.ts +14 -6
  242. package/src/render/render-features.ts +17 -35
  243. package/src/runtime/create-runtime.ts +270 -108
  244. package/src/runtime/debug-bridge.ts +16 -0
  245. package/src/runtime/debug-registry.ts +121 -8
  246. package/src/runtime/dev-build.ts +2 -2
  247. package/src/runtime/game-input-seams.ts +2 -2
  248. package/src/runtime/game.ts +180 -80
  249. package/src/runtime/mount-game.ts +8 -56
  250. package/src/runtime/mount-manifest.ts +43 -34
  251. package/src/runtime/playtest.ts +22 -0
  252. package/src/runtime/run-ticks-settled.ts +72 -0
  253. package/src/runtime/state-bridge.ts +3 -3
  254. package/src/setup/setup-audio.ts +21 -11
  255. package/src/setup/setup-renderer.ts +2 -61
  256. package/src/world3d-react/r3f-root-factory.tsx +7 -7
  257. package/src/world3d-react/rapier-physics-bridge.tsx +107 -50
  258. package/dist/adapter/setup-three-root-adapter.d.ts +0 -86
  259. package/dist/adapter/setup-three-root-adapter.d.ts.map +0 -1
  260. package/dist/adapter/setup-three-root-adapter.js +0 -908
  261. package/dist/animation/clip-map.d.ts +0 -12
  262. package/dist/animation/clip-map.d.ts.map +0 -1
  263. package/dist/animation/clip-map.js +0 -31
  264. package/dist/asset-registry.d.ts +0 -38
  265. package/dist/asset-registry.d.ts.map +0 -1
  266. package/dist/asset-registry.js +0 -66
  267. package/dist/canvas-react/engine-bridge.d.ts +0 -45
  268. package/dist/canvas-react/engine-bridge.d.ts.map +0 -1
  269. package/dist/canvas-react/engine-bridge.js +0 -45
  270. package/dist/canvas-react/pixi-react-adapter.d.ts +0 -77
  271. package/dist/canvas-react/pixi-react-adapter.d.ts.map +0 -1
  272. package/dist/canvas-react/pixi-react-adapter.js +0 -294
  273. package/dist/canvas-react/world-context.d.ts +0 -98
  274. package/dist/canvas-react/world-context.d.ts.map +0 -1
  275. package/dist/canvas-react/world-context.js +0 -173
  276. package/dist/dev/debug-draw.d.ts +0 -24
  277. package/dist/dev/debug-draw.d.ts.map +0 -1
  278. package/dist/dev/debug-draw.js +0 -73
  279. package/dist/render/auto-batcher.d.ts +0 -34
  280. package/dist/render/auto-batcher.d.ts.map +0 -1
  281. package/dist/render/auto-batcher.js +0 -140
  282. package/dist/render/lod.d.ts +0 -13
  283. package/dist/render/lod.d.ts.map +0 -1
  284. package/dist/render/lod.js +0 -16
  285. package/dist/runtime/types.d.ts +0 -303
  286. package/dist/runtime/types.d.ts.map +0 -1
  287. package/dist/runtime/types.js +0 -1
  288. package/dist/world3d-react/engine-bridge.d.ts +0 -47
  289. package/dist/world3d-react/engine-bridge.d.ts.map +0 -1
  290. package/dist/world3d-react/engine-bridge.js +0 -73
  291. package/dist/world3d-react/r3f-adapter.d.ts +0 -58
  292. package/dist/world3d-react/r3f-adapter.d.ts.map +0 -1
  293. package/dist/world3d-react/r3f-adapter.js +0 -634
  294. package/dist/world3d-react/world-context.d.ts +0 -182
  295. package/dist/world3d-react/world-context.d.ts.map +0 -1
  296. package/dist/world3d-react/world-context.js +0 -235
  297. package/src/adapter/setup-three-root-adapter.ts +0 -1032
  298. package/src/animation/clip-map.ts +0 -34
  299. package/src/asset-registry.ts +0 -89
  300. package/src/dev/debug-draw.ts +0 -80
  301. package/src/render/auto-batcher.ts +0 -168
  302. package/src/render/lod.ts +0 -17
  303. package/src/runtime/types.ts +0 -328
@@ -4,11 +4,9 @@
4
4
  *
5
5
  * WHY IT EXISTS. `SystemAdapters.physics` is what the editor's transform
6
6
  * coordination (`freeze → apply → unfreeze`) and the universal
7
- * "Colliders drawn" instrument (`@engine/dev/instruments`) read. The
8
- * first-party imperative three root registers one from
9
- * `../adapter/setup-three-root-adapter.ts`; an R3F world builds no first-party
10
- * Rapier runtime at all (see `R3FGameContext`'s doc comment) — it does physics
11
- * with `@react-three/rapier` INSIDE the fiber tree, where nothing engine-side
7
+ * "Colliders drawn" instrument (`@engine/dev/instruments`) read. An R3F world
8
+ * builds no first-party Rapier runtime at all it does physics with
9
+ * `@react-three/rapier` INSIDE the fiber tree, where nothing engine-side
12
10
  * can see it. Result, before this: the colliders instrument read `null` (an
13
11
  * em-dash) and its toggle threw "no physics adapter" in every shipped example
14
12
  * and every scaffolded game.
@@ -51,9 +49,12 @@
51
49
  * TEARDOWN: the effect's cleanup, and nothing else — it clears the SAME key
52
50
  * and the slot it set (identity-guarded), so a play stop/start, a hot
53
51
  * restart, or a world that unmounts `<Physics>` leaves no stale adapter
54
- * behind. Because the slot is game-global, mount exactly ONE bridge per
55
- * GAME: with two, the later mount wins the slot and the later unmount clears
56
- * it while the earlier bridge is still live.
52
+ * behind. Because the slot is PAGE-global (see its doc below — an engine
53
+ * module is shared across the editor's Play instances), mount exactly ONE
54
+ * bridge per PAGE: with two a scene that mounts two, or two Play instances
55
+ * of one rapier world — the later mount wins the slot and the later unmount
56
+ * clears it while the earlier bridge is still live. That state is reported
57
+ * loudly the moment it starts (`warnOnContendedMount`).
57
58
  *
58
59
  * PHASE ORDERING: none to respect — this component registers pull-only
59
60
  * callbacks and runs no per-frame work. Every method it exposes is invoked
@@ -66,6 +67,7 @@ import { useEffect } from 'react';
66
67
  import * as THREE from 'three';
67
68
  import {
68
69
  createRapierBodyEditing,
70
+ type RapierBodyLookup,
69
71
  type RapierEditableBody,
70
72
  } from '../adapter/rapier-physics-adapter';
71
73
  import type {
@@ -75,6 +77,7 @@ import type {
75
77
  PhysicsJointSnapshot,
76
78
  PhysicsJointType,
77
79
  } from '../adapter/system-adapter';
80
+ import { createSystemSlot } from '../adapter/system-slot';
78
81
 
79
82
  export interface RapierPhysicsBridgeProps {
80
83
  /**
@@ -151,12 +154,33 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
151
154
  return owningBody(object, rigidBodyStates.values());
152
155
  };
153
156
 
157
+ /**
158
+ * The same question, with the two negative answers KEPT APART for the
159
+ * editing protocol — see {@link RapierBodyLookup}. This world's search
160
+ * space is the `<RigidBody>` subtrees, so "no object here" really is "not a
161
+ * node of mine", not "a node of mine without physics".
162
+ */
163
+ const bodyLookup = (nodeId: string): RapierBodyLookup => {
164
+ const object = lateEntityObject(rigidBodyStates.values(), nodeId);
165
+ if (!object) return { kind: 'unresolved' };
166
+ const body = owningBody(object, rigidBodyStates.values());
167
+ return body ? { kind: 'body', body } : { kind: 'no-body' };
168
+ };
169
+
170
+ /** Rapier's own name for a shape type, via the enum's reverse mapping —
171
+ * so an unprojected collider reports WHICH shape it is (`TriMesh`,
172
+ * `HeightField`, …) instead of a bare number, and a shape added by a
173
+ * future rapier still names itself. */
174
+ const rapierShapeName = (shapeType: number): string =>
175
+ (rapier.ShapeType as unknown as Record<number, string | undefined>)[shapeType] ??
176
+ `shapeType ${shapeType}`;
177
+
154
178
  const colliderShape = (collider: {
155
179
  shapeType(): number;
156
180
  halfExtents(): { x: number; y: number; z: number };
157
181
  radius(): number;
158
182
  halfHeight(): number;
159
- }): PhysicsColliderShape | null => {
183
+ }): PhysicsColliderShape => {
160
184
  switch (collider.shapeType()) {
161
185
  case rapier.ShapeType.Cuboid: {
162
186
  const halfExtents = collider.halfExtents();
@@ -170,8 +194,13 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
170
194
  halfHeight: collider.halfHeight(),
171
195
  radius: collider.radius(),
172
196
  };
197
+ // Rapier has many more shapes than this seam projects (TriMesh,
198
+ // HeightField, ConvexPolyhedron, Cylinder, Cone, …). The collider is
199
+ // REAL; only our plain-data projection of it is missing, so it is
200
+ // reported as itself rather than dropped — a body whose only collider
201
+ // is a trimesh used to inspect as a body with no colliders at all.
173
202
  default:
174
- return null;
203
+ return { type: 'unsupported', kind: rapierShapeName(collider.shapeType()) };
175
204
  }
176
205
  };
177
206
 
@@ -182,7 +211,6 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
182
211
  for (const state of colliderStates.values()) {
183
212
  if (state.collider.parent()?.handle !== body.handle) continue;
184
213
  const shape = colliderShape(state.collider);
185
- if (!shape) continue;
186
214
  state.object.updateWorldMatrix(true, false);
187
215
  const position = state.collider.translation();
188
216
  const rotation = state.collider.rotation();
@@ -219,6 +247,11 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
219
247
  collider.setHalfHeight(shape.halfHeight);
220
248
  collider.setRadius(shape.radius);
221
249
  break;
250
+ // A shape this seam does not project has no dimensions to write back.
251
+ // `colliders()` reports it so the body's physics is not invisible; that
252
+ // is a READ, and it does not make the shape editable.
253
+ case 'unsupported':
254
+ break;
222
255
  }
223
256
  };
224
257
 
@@ -343,16 +376,18 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
343
376
  };
344
377
 
345
378
  const adapter: PhysicsAdapter = {
346
- ...createRapierBodyEditing(bodyFor),
379
+ ...createRapierBodyEditing(bodyLookup),
347
380
  setDebugDrawEnabled: (enabled) => onDebugChange(enabled),
348
381
  colliders,
349
382
  previewCollider,
350
383
  joints,
351
384
  previewJointAnchor,
352
385
  };
353
- liveAdapter = adapter;
386
+ warnOnContendedMount();
387
+ const detach = physicsSlot.attach(adapter);
354
388
  return () => {
355
- if (liveAdapter === adapter) liveAdapter = null;
389
+ liveBridgeCount -= 1;
390
+ detach();
356
391
  };
357
392
  }, [colliderStates, onDebugChange, rapier, rigidBodyStates, world]);
358
393
 
@@ -363,21 +398,66 @@ export function RapierPhysicsBridge({ onDebugChange }: RapierPhysicsBridgeProps)
363
398
  // The entry-declaration half
364
399
  // ---------------------------------------------------------------------------
365
400
 
366
- /** The mounted bridge's adapter, while one is live. Module-scoped on purpose:
367
- * the entry's `systems` declaration is static, the rapier world is not, and
368
- * this slot is the seam between the two. */
369
- let liveAdapter: PhysicsAdapter | null = null;
401
+ /**
402
+ * The bridge's slot. Module-scoped on purpose: the entry's `systems`
403
+ * declaration is static, the rapier world is not, and this slot is the seam
404
+ * between the two. Module scope here means one per PAGE, not one per game:
405
+ * this file is an ENGINE module, reached by bare specifier, so the editor's
406
+ * multi-instance Play (which stamps mount ids onto PROJECT-owned specifiers
407
+ * only — `server/project-module-instance.ts`) shares this one slot across
408
+ * every mounted instance. Two live instances of a rapier world therefore
409
+ * contend for it: the LAST bridge to mount wins every instance's declared
410
+ * `systems.physics`, and the loser's detach is dropped by the identity guard.
411
+ * {@link warnOnContendedMount} makes that state loud at the moment it
412
+ * happens; making the slot instance-scoped needs a per-mount rendezvous the
413
+ * context-free declaration (`rapierPhysicsSystem()` at project-module scope)
414
+ * does not yet have.
415
+ *
416
+ * Every member is an ACTION: each one either edits the sim or reads a live
417
+ * Rapier handle, and all of them arrive at gesture rate (a gizmo drag, an
418
+ * instrument command), never from a per-frame loop — so refusing by name is
419
+ * always the right answer, and a fabricated empty never is.
420
+ * `debugDraw`/`contactPoints` are OMITTED, not stubbed, for the anti-shim
421
+ * reason stated at the top of this file; a member the config does not list
422
+ * does not exist on the slot.
423
+ */
424
+ let liveBridgeCount = 0;
370
425
 
371
- function requireLiveAdapter(): PhysicsAdapter {
372
- if (!liveAdapter) {
373
- throw new Error(
374
- 'rapierPhysicsSystem: no <RapierPhysicsBridge> is mounted inside a live <Physics> right ' +
375
- 'now the world is not playing, or the scene never mounted the bridge.',
376
- );
377
- }
378
- return liveAdapter;
426
+ /** The page-scope contention above, made LOUD instead of silent (never a
427
+ * silent degrade): a second concurrent bridge mount — a scene that mounted
428
+ * two, or a second Play instance of a rapier world — is reported the moment
429
+ * it happens, naming what breaks and until when. */
430
+ function warnOnContendedMount(): void {
431
+ liveBridgeCount += 1;
432
+ if (liveBridgeCount <= 1) return;
433
+ // biome-ignore lint/suspicious/noConsole: the standing-warning channel for a capability the adapter cannot reach — the editor console surfaces it during play
434
+ console.warn(
435
+ `[rapierPhysicsSystem] ${liveBridgeCount} <RapierPhysicsBridge> mounts are live on this ` +
436
+ 'page, but the declared physics slot is one per page (engine modules are shared across ' +
437
+ "Play instances). Every instance's `systems.physics` now forwards to the LAST bridge " +
438
+ 'mounted, so physics gizmo edits and instruments address that world — not necessarily ' +
439
+ 'the one selected — until the extra mounts unmount.',
440
+ );
379
441
  }
380
442
 
443
+ const physicsSlot = createSystemSlot<PhysicsAdapter>({
444
+ name: 'rapierPhysicsSystem',
445
+ unattachedMessage:
446
+ 'no <RapierPhysicsBridge> is mounted inside a live <Physics> right now — the world is not ' +
447
+ 'playing, or the scene never mounted the bridge.',
448
+ members: {
449
+ ownerOf: { kind: 'action' },
450
+ freeze: { kind: 'action' },
451
+ commit: { kind: 'action' },
452
+ unfreeze: { kind: 'action' },
453
+ setDebugDrawEnabled: { kind: 'action' },
454
+ colliders: { kind: 'action' },
455
+ previewCollider: { kind: 'action' },
456
+ joints: { kind: 'action' },
457
+ previewJointAnchor: { kind: 'action' },
458
+ },
459
+ });
460
+
381
461
  /**
382
462
  * The `systems.physics` slot for a default-exported world — declare it from
383
463
  * the entry (`export const systems = { physics: rapierPhysicsSystem() }`) and
@@ -387,28 +467,5 @@ function requireLiveAdapter(): PhysicsAdapter {
387
467
  * name rather than fabricating.
388
468
  */
389
469
  export function rapierPhysicsSystem(): PhysicsAdapter {
390
- // Every forward REQUIRES the live adapter and its member — a silent no-op
391
- // or fabricated empty answer is the shape the anti-shim rule forbids. The
392
- // bridge's adapter always carries all five optional members, so the member
393
- // checks only ever fire against a foreign replacement.
394
- const requireMember = <K extends keyof PhysicsAdapter>(member: K) => {
395
- const adapter = requireLiveAdapter();
396
- const value = adapter[member];
397
- if (typeof value !== 'function') {
398
- throw new Error(`rapierPhysicsSystem: the live physics adapter has no ${String(member)}().`);
399
- }
400
- return value.bind(adapter) as NonNullable<PhysicsAdapter[K]>;
401
- };
402
- return {
403
- ownerOf: (nodeId) => requireLiveAdapter().ownerOf(nodeId),
404
- freeze: (nodeId) => requireLiveAdapter().freeze(nodeId),
405
- commit: (nodeId, t) => requireLiveAdapter().commit(nodeId, t),
406
- unfreeze: (nodeId) => requireLiveAdapter().unfreeze(nodeId),
407
- setDebugDrawEnabled: (enabled) => requireMember('setDebugDrawEnabled')(enabled),
408
- colliders: (nodeId) => requireMember('colliders')(nodeId),
409
- previewCollider: (colliderId, shape) => requireMember('previewCollider')(colliderId, shape),
410
- joints: (nodeId) => requireMember('joints')(nodeId),
411
- previewJointAnchor: (jointId, endpoint, anchor) =>
412
- requireMember('previewJointAnchor')(jointId, endpoint, anchor),
413
- };
470
+ return physicsSlot.slot;
414
471
  }
@@ -1,86 +0,0 @@
1
- /**
2
- * SetupThreeRootAdapter — the first-party implementer of {@link RootAdapter}
3
- * that mounts ONE three root from an imperative `setup(ctx)` function.
4
- *
5
- * This is where Rapier lives now: the host no longer knows about it.
6
- * `mount(host)` builds the full first-party runtime (the body
7
- * that used to live inline in `create-runtime.ts`) from a neutral
8
- * {@link ThreeHostContext}, runs the game's `setup`, and returns a
9
- * {@link MountedThreeRoot}. First-party content is now just one implementer of
10
- * the same interface an unmodified external game implements (Phase B).
11
- *
12
- * It was `VgaiSceneGameAdapter` until WO-9 tasks #6/#9. Both halves of that
13
- * name had stopped being true: it loads no scene (the `.vscn.json` branches
14
- * went with the format in WO-8 — `setup` is the only mount path left), and what
15
- * it mounts is one three ROOT, not a game (a game is the manifest's whole set
16
- * of roots). The R3F/TSX world root is a SEPARATE adapter; this one is
17
- * specifically the `fromSetup` path.
18
- *
19
- * `GameSetupFn` / `GameContext` are imported ONLY here (and in the example
20
- * `fromSetup` wrappers) — never by the host. That confinement is the inversion.
21
- */
22
- import type { EffectComposer } from 'postprocessing';
23
- import { type ViewportShadingMode } from '../render/viewport-shading';
24
- import { type RootFrameHooks } from '../runtime/game';
25
- import type { EditorPreview, GameContext, GameSetupFn } from '../runtime/types';
26
- import type { ThreeHostContext } from './host-context';
27
- import type { MountedThreeRoot, RootAdapter } from './root-adapter';
28
- /** How a {@link SetupThreeRootAdapter} builds its game (the former RuntimeConfig). */
29
- export interface SetupThreeRootConfig {
30
- /** Game setup function — registers systems and loads scenes. */
31
- setup?: GameSetupFn | undefined;
32
- /** Editor context — passed to setup() when launched from the editor. */
33
- editorPreview?: EditorPreview | undefined;
34
- /** Input map JSON path. Defaults to 'inputmaps/default.inputmap.json'. */
35
- inputMapPath?: string | undefined;
36
- /** URL prefix for relative asset paths. Defaults to '/'. */
37
- assetPrefix?: string | undefined;
38
- /** Stable adapter id (for registry/conformance). Defaults to 'setup-three'. */
39
- id?: string | undefined;
40
- }
41
- /**
42
- * The first-party `MountedThreeRoot`, with concrete extras the editor host uses for
43
- * first-party features (HMR, physics sync) that are NOT part of the neutral
44
- * interface. The generic host only touches the `MountedThreeRoot` surface.
45
- */
46
- export interface MountedSetupThreeRoot extends MountedThreeRoot {
47
- /**
48
- * First-party brand (checklist item 1 / T7.3 lookahead): a plain `'ctx' in
49
- * mounted` structural check misfires against any foreign mount that happens
50
- * to carry an unrelated `ctx` key. This literal-`true`
51
- * property is what `isFirstPartyMounted` (`runtime/game.ts`) actually
52
- * checks; `game.ts` reads it via a structural `{ firstParty?: unknown }`
53
- * probe so it never needs a value import of this module (the existing
54
- * import is type-only and must stay that way).
55
- */
56
- readonly firstParty: true;
57
- readonly ctx: GameContext;
58
- readonly composer: EffectComposer | null;
59
- /** Render-only developer view; native game materials are restored before the frame returns. */
60
- setViewportShadingMode(mode: ViewportShadingMode): void;
61
- /** Warm-restart: dispose the current game, re-run a new setup on the same ctx. */
62
- hotReload(newSetup: GameSetupFn, editorPreview?: EditorPreview): Promise<void>;
63
- /**
64
- * Phase-partitioned frame entry point (T7.1 slice 2) — wired onto this
65
- * world's `RootInstance` by `registerThreeRoot` (`create-runtime.ts`).
66
- * `Game.runFrame` is the CANONICAL driver going forward: it calls
67
- * `frame.runPhase` once per (phase, substep) for every world, then
68
- * `frame.endFrame` once per substep after all roots finish all phases.
69
- * `update` is the direct entry point to the same `SystemRunner` for hosts
70
- * that do not partition phases themselves.
71
- */
72
- readonly frame: RootFrameHooks;
73
- }
74
- /**
75
- * Wrap a first-party `setup` function as a {@link RootAdapter}. This is how every
76
- * first-party example becomes an implementer of the same interface an external
77
- * game implements — with no change to the setup's body.
78
- */
79
- export declare function fromSetup(id: string, setup: GameSetupFn): RootAdapter;
80
- export declare class SetupThreeRootAdapter implements RootAdapter {
81
- private readonly config;
82
- readonly id: string;
83
- constructor(config?: SetupThreeRootConfig);
84
- mount(host: ThreeHostContext): Promise<MountedSetupThreeRoot>;
85
- }
86
- //# sourceMappingURL=setup-three-root-adapter.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"setup-three-root-adapter.d.ts","sourceRoot":"","sources":["../../src/adapter/setup-three-root-adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAIH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAoCrD,OAAO,EAAE,KAAK,mBAAmB,EAA2B,MAAM,4BAA4B,CAAC;AAO/F,OAAO,EAAwB,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC5E,OAAO,KAAK,EAAE,aAAa,EAAe,WAAW,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAM7F,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAEvD,OAAO,KAAK,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAC;AAGpE,sFAAsF;AACtF,MAAM,WAAW,oBAAoB;IACnC,gEAAgE;IAChE,KAAK,CAAC,EAAE,WAAW,GAAG,SAAS,CAAC;IAChC,wEAAwE;IACxE,aAAa,CAAC,EAAE,aAAa,GAAG,SAAS,CAAC;IAC1C,0EAA0E;IAC1E,YAAY,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAClC,4DAA4D;IAC5D,WAAW,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IACjC,+EAA+E;IAC/E,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;CACzB;AAED;;;;GAIG;AACH,MAAM,WAAW,qBAAsB,SAAQ,gBAAgB;IAC7D;;;;;;;;OAQG;IACH,QAAQ,CAAC,UAAU,EAAE,IAAI,CAAC;IAC1B,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC;IAC1B,QAAQ,CAAC,QAAQ,EAAE,cAAc,GAAG,IAAI,CAAC;IACzC,+FAA+F;IAC/F,sBAAsB,CAAC,IAAI,EAAE,mBAAmB,GAAG,IAAI,CAAC;IACxD,kFAAkF;IAClF,SAAS,CAAC,QAAQ,EAAE,WAAW,EAAE,aAAa,CAAC,EAAE,aAAa,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/E;;;;;;;;OAQG;IACH,QAAQ,CAAC,KAAK,EAAE,cAAc,CAAC;CAChC;AAeD;;;;GAIG;AACH,wBAAgB,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,WAAW,CAErE;AAED,qBAAa,qBAAsB,YAAW,WAAW;IAE3C,OAAO,CAAC,QAAQ,CAAC,MAAM;IADnC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;gBACS,MAAM,GAAE,oBAAyB;IAKxD,KAAK,CAAC,IAAI,EAAE,gBAAgB,GAAG,OAAO,CAAC,qBAAqB,CAAC;CAq1BpE"}