@volter/editor-game 0.5.65

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 (302) hide show
  1. package/LICENSE +661 -0
  2. package/NOTICE +23 -0
  3. package/contributions/asset-budget-asset.menu.ts +26 -0
  4. package/contributions/asset-budget.action.ts +18 -0
  5. package/contributions/asset-budget.document.tsx +21 -0
  6. package/contributions/asset-budget.menu.ts +22 -0
  7. package/contributions/audio-unlock.service.ts +17 -0
  8. package/contributions/audio.utility.tsx +15 -0
  9. package/contributions/autoplay.service.ts +48 -0
  10. package/contributions/bridge.command.ts +172 -0
  11. package/contributions/build-profiles.document.tsx +21 -0
  12. package/contributions/build-progress.status.tsx +45 -0
  13. package/contributions/build.action.ts +26 -0
  14. package/contributions/build.command.ts +27 -0
  15. package/contributions/build.header.tsx +37 -0
  16. package/contributions/build.menu.ts +31 -0
  17. package/contributions/build.service.ts +31 -0
  18. package/contributions/connection.status.tsx +43 -0
  19. package/contributions/coverage.service.ts +118 -0
  20. package/contributions/edit-mode-audio.service.ts +23 -0
  21. package/contributions/edit-mode-networking.service.ts +30 -0
  22. package/contributions/game-document.service.ts +25 -0
  23. package/contributions/game-eval.command.ts +210 -0
  24. package/contributions/game.layout.ts +19 -0
  25. package/contributions/gameplay.command.ts +255 -0
  26. package/contributions/generation.service.ts +96 -0
  27. package/contributions/generations.status.tsx +53 -0
  28. package/contributions/ingest.service.ts +80 -0
  29. package/contributions/instances.command.ts +75 -0
  30. package/contributions/navmesh.menu.ts +39 -0
  31. package/contributions/navmesh.service.ts +19 -0
  32. package/contributions/network.utility.tsx +17 -0
  33. package/contributions/play.command.ts +355 -0
  34. package/contributions/profiler.action.ts +16 -0
  35. package/contributions/profiler.menu.ts +22 -0
  36. package/contributions/profiler.utility.tsx +17 -0
  37. package/contributions/react/component-board.service.ts +21 -0
  38. package/contributions/react/design-time-mount.service.ts +60 -0
  39. package/contributions/react/pasteboard.action.ts +41 -0
  40. package/contributions/react/react-inspector.service.ts +85 -0
  41. package/contributions/react/story-documents.service.ts +44 -0
  42. package/contributions/scene-document.service.ts +32 -0
  43. package/contributions/state-watch.action.ts +17 -0
  44. package/contributions/state-watch.menu.ts +23 -0
  45. package/contributions/state-watch.utility.tsx +20 -0
  46. package/contributions/team-playtest.service.ts +124 -0
  47. package/contributions/three/camera-runtime.inspector.tsx +34 -0
  48. package/contributions/three/component-board.service.ts +24 -0
  49. package/contributions/three/component-verbs.command.ts +110 -0
  50. package/contributions/three/component-verbs.service.ts +92 -0
  51. package/contributions/three/constraints.inspector.tsx +34 -0
  52. package/contributions/three/model-asset-sections.service.ts +26 -0
  53. package/contributions/three/reflection-probe-capture.inspector.tsx +32 -0
  54. package/contributions/three/story-documents.service.ts +31 -0
  55. package/contributions/three/three-authoring.service.ts +66 -0
  56. package/contributions/transport.header.tsx +19 -0
  57. package/contributions/xstate-behavior.action.ts +42 -0
  58. package/contributions/xstate-behavior.document.tsx +73 -0
  59. package/contributions/xstate-behavior.inspector.tsx +28 -0
  60. package/contributions/xstate-behavior.menu.ts +23 -0
  61. package/package.json +144 -0
  62. package/src/asset-budget/AssetBudgetPanel.tsx +1172 -0
  63. package/src/asset-budget/asset-budget-model.ts +799 -0
  64. package/src/asset-budget/basis-encoder.ts +165 -0
  65. package/src/asset-budget/gltf-io.ts +154 -0
  66. package/src/asset-budget/gltf-optimize.ts +384 -0
  67. package/src/asset-budget/image-dims.ts +78 -0
  68. package/src/asset-budget/optimize-apply.ts +221 -0
  69. package/src/audio/AudioDebuggerPanel.tsx +457 -0
  70. package/src/audio/audio-debugger-model.ts +87 -0
  71. package/src/bridge/call.ts +60 -0
  72. package/src/bridge/dispatch.ts +459 -0
  73. package/src/bridge/live-frames.ts +25 -0
  74. package/src/bridge/screenshot.ts +316 -0
  75. package/src/build/BuildProfilesPanel.tsx +476 -0
  76. package/src/build/build-session.ts +247 -0
  77. package/src/build/format-bytes.ts +14 -0
  78. package/src/command-results.ts +36 -0
  79. package/src/coverage/live-authoring-surface.ts +30 -0
  80. package/src/coverage/live-project-verbs.ts +162 -0
  81. package/src/coverage/native-system-coverage.ts +143 -0
  82. package/src/coverage/root-coverage.ts +79 -0
  83. package/src/coverage/session-coverage.ts +193 -0
  84. package/src/design-system-stories/ApplicationChrome.stories.tsx +100 -0
  85. package/src/design-system-stories/InspectorNarrowBodies.stories.tsx +406 -0
  86. package/src/edit-mode/edit-mode-audio.ts +56 -0
  87. package/src/edit-mode/edit-mode-networking.ts +112 -0
  88. package/src/game-document/DevicePresetPicker.tsx +84 -0
  89. package/src/game-document/GameCaptureFrameButton.tsx +45 -0
  90. package/src/game-document/GameDocument.tsx +215 -0
  91. package/src/game-document/GamePanel.tsx +662 -0
  92. package/src/game-document/InstanceInspectorPicker.tsx +183 -0
  93. package/src/game-document/crowd-debug.ts +183 -0
  94. package/src/game-document/device-preview.ts +336 -0
  95. package/src/game-document/game-view-store.ts +107 -0
  96. package/src/game-document/physics-debug.ts +187 -0
  97. package/src/generation/GenerationActivity.tsx +421 -0
  98. package/src/generation/GenerationGallery.css +231 -0
  99. package/src/generation/generation-documents.tsx +338 -0
  100. package/src/generation/generation-jobs.ts +128 -0
  101. package/src/generation/generation-presentation.ts +257 -0
  102. package/src/host/adapter-reach.ts +445 -0
  103. package/src/host/adapter-runtime-bindings.ts +303 -0
  104. package/src/host/after-paint.ts +112 -0
  105. package/src/host/api/configurations.ts +66 -0
  106. package/src/host/authoring/babylon-authoring-adapter.ts +703 -0
  107. package/src/host/authoring/canvas-runtime-recognition.ts +50 -0
  108. package/src/host/authoring/contract-hierarchy-authoring.ts +144 -0
  109. package/src/host/authoring/contract-scenes-stories.ts +204 -0
  110. package/src/host/authoring/creation-site-related.ts +55 -0
  111. package/src/host/authoring/ephemeral-persistence.ts +29 -0
  112. package/src/host/authoring/gesture-persist.ts +84 -0
  113. package/src/host/authoring/ingest-data-writer.ts +232 -0
  114. package/src/host/authoring/ingest-source-persistence.ts +826 -0
  115. package/src/host/authoring/mount-isolated-pixi-screen.ts +250 -0
  116. package/src/host/authoring/mounted-authoring.ts +41 -0
  117. package/src/host/authoring/owned-pixi-ticker-listeners.ts +96 -0
  118. package/src/host/authoring/phaser-live-authoring-adapter.ts +265 -0
  119. package/src/host/authoring/pixi-authoring-adapter.ts +1554 -0
  120. package/src/host/authoring/pixi-creation-site-write-target.ts +60 -0
  121. package/src/host/authoring/pixi-isolation-assets.ts +25 -0
  122. package/src/host/authoring/pixi-live-write-target.ts +979 -0
  123. package/src/host/authoring/pixi-source-identity.ts +141 -0
  124. package/src/host/authoring/pixi-still-presentation.ts +71 -0
  125. package/src/host/authoring/pixi-structure-history.ts +237 -0
  126. package/src/host/authoring/pixi-transform-channels.ts +205 -0
  127. package/src/host/authoring/selection-remount-handoff.ts +23 -0
  128. package/src/host/authoring/source-persistence-backend.ts +373 -0
  129. package/src/host/authoring/source-refresh-revisions.ts +81 -0
  130. package/src/host/authoring/struct-write-pipe.ts +143 -0
  131. package/src/host/auto-frame-window.ts +89 -0
  132. package/src/host/binding-resolver.ts +393 -0
  133. package/src/host/browser-transpile.ts +631 -0
  134. package/src/host/canvas-entry-runtime.ts +95 -0
  135. package/src/host/components/CameraAuthoringOverlay.tsx +216 -0
  136. package/src/host/components/HeaderTelemetry.tsx +341 -0
  137. package/src/host/components/PixiIsolationSceneContent.tsx +280 -0
  138. package/src/host/components/ResolutionPicker.tsx +89 -0
  139. package/src/host/components/ThreeIsolationSceneContent.tsx +180 -0
  140. package/src/host/components/frame-debugger-model.ts +579 -0
  141. package/src/host/components/header-telemetry-model.ts +74 -0
  142. package/src/host/components/scene-document.tsx +447 -0
  143. package/src/host/components/utility-view-state.ts +87 -0
  144. package/src/host/components/world-root-stage-binding.tsx +133 -0
  145. package/src/host/components/world-root-stage.ts +1046 -0
  146. package/src/host/coverage/authoring-read-probe.ts +583 -0
  147. package/src/host/coverage/capability-coverage.ts +1587 -0
  148. package/src/host/coverage/coverage-accounting.ts +261 -0
  149. package/src/host/coverage/game-contract-seam-evidence.ts +82 -0
  150. package/src/host/coverage/project-verb-coverage.ts +148 -0
  151. package/src/host/coverage/system-adapter-coverage.ts +373 -0
  152. package/src/host/design-system-stories/StoryLayout.tsx +104 -0
  153. package/src/host/design-system-stories/fixtures/authoring.ts +247 -0
  154. package/src/host/design-system-stories/fixtures/editor-runtime.tsx +115 -0
  155. package/src/host/document-preview-three.ts +110 -0
  156. package/src/host/entry-adjudication.ts +89 -0
  157. package/src/host/game-location-guard.ts +150 -0
  158. package/src/host/game-module-access.ts +196 -0
  159. package/src/host/game-realm-page.ts +358 -0
  160. package/src/host/game-realm-reclaim.ts +55 -0
  161. package/src/host/game-realm-storage.ts +104 -0
  162. package/src/host/gameplay-export.ts +288 -0
  163. package/src/host/gameplay-recording.ts +717 -0
  164. package/src/host/gated-globals.ts +1511 -0
  165. package/src/host/history/json-history-resource.ts +254 -0
  166. package/src/host/ingest/registry.ts +261 -0
  167. package/src/host/instance-extract-actions.ts +120 -0
  168. package/src/host/instance-fork-actions.ts +120 -0
  169. package/src/host/play-control-hook.ts +26 -0
  170. package/src/host/playwright-shim.ts +479 -0
  171. package/src/host/projection/dom.ts +295 -0
  172. package/src/host/projection/pixi.ts +288 -0
  173. package/src/host/r3f-entry-runtime.ts +77 -0
  174. package/src/host/react-mount-runtime.ts +162 -0
  175. package/src/host/realm-services.ts +148 -0
  176. package/src/host/recording-preview.ts +106 -0
  177. package/src/host/roots/module-root.ts +203 -0
  178. package/src/host/roots/react-root.ts +181 -0
  179. package/src/host/same-realm-loop-gate.ts +544 -0
  180. package/src/host/scene-view-drawability.ts +69 -0
  181. package/src/host/sdk/tools.ts +31 -0
  182. package/src/host/served-bundle-runtime-modules.ts +331 -0
  183. package/src/host/server-log-bridge.ts +60 -0
  184. package/src/host/staged-projects.ts +24 -0
  185. package/src/host/stories/mounted-story-viewport-source.ts +64 -0
  186. package/src/host/stories/story-arg-descriptors.ts +50 -0
  187. package/src/host/stories/story-media-presence.ts +152 -0
  188. package/src/host/surface-content.ts +87 -0
  189. package/src/host/take-named-export.ts +23 -0
  190. package/src/host/three-ingest-runtime.ts +76 -0
  191. package/src/host/types-fastnoise-lite.d.ts +7 -0
  192. package/src/host/types-mikktspace.d.ts +20 -0
  193. package/src/host/types-troika-three-text.d.ts +7 -0
  194. package/src/host/use-active-performance-source.ts +53 -0
  195. package/src/host/viewport-pose-memory.ts +48 -0
  196. package/src/host/viewport-root-presentation.ts +40 -0
  197. package/src/ingest/active-ingest.ts +251 -0
  198. package/src/ingest/active-scene-navigation.ts +43 -0
  199. package/src/ingest/authoring/ingest-dom-surface-authoring.ts +191 -0
  200. package/src/ingest/authoring/ingest-root-adapter.ts +897 -0
  201. package/src/ingest/capture-wait-report.ts +122 -0
  202. package/src/ingest/deferred-ingest-play.ts +216 -0
  203. package/src/ingest/deferred-ingest-session.ts +23 -0
  204. package/src/ingest/discovery-public-ingest.ts +242 -0
  205. package/src/ingest/dom-stub-mark.ts +7 -0
  206. package/src/ingest/entry-load.ts +56 -0
  207. package/src/ingest/game-contract-realm.ts +34 -0
  208. package/src/ingest/game-pointer-lock.ts +89 -0
  209. package/src/ingest/held-scene-repaint.ts +73 -0
  210. package/src/ingest/host-surface-box.ts +174 -0
  211. package/src/ingest/ingest-boot-viewport.ts +54 -0
  212. package/src/ingest/ingest-canvas-scene-document.tsx +177 -0
  213. package/src/ingest/ingest-canvas-scene.ts +50 -0
  214. package/src/ingest/ingest-evidence-hook.ts +131 -0
  215. package/src/ingest/ingest-frame-snapshot.ts +183 -0
  216. package/src/ingest/ingest-play-commands.ts +134 -0
  217. package/src/ingest/ingest-play-control.ts +299 -0
  218. package/src/ingest/ingest-render-debug.ts +320 -0
  219. package/src/ingest/ingest-siblings.ts +487 -0
  220. package/src/ingest/ingest-status.ts +62 -0
  221. package/src/ingest/live-ingest-facet.ts +46 -0
  222. package/src/ingest/module-mode.ts +229 -0
  223. package/src/ingest/mount-canvas-ingest-root.ts +899 -0
  224. package/src/ingest/mount-coverage.ts +296 -0
  225. package/src/ingest/mount-dom-ingest-root.ts +282 -0
  226. package/src/ingest/mount-ingest-root.ts +744 -0
  227. package/src/ingest/mount-three-ingest-root.ts +366 -0
  228. package/src/ingest/resolve-canvas.ts +47 -0
  229. package/src/ingest/resolve-three.ts +123 -0
  230. package/src/ingest/served-bundle.ts +109 -0
  231. package/src/ingest/served-html-boot.ts +292 -0
  232. package/src/ingest/surface-canvas.ts +84 -0
  233. package/src/ingest/surface-dom.ts +84 -0
  234. package/src/ingest/surface-three.ts +128 -0
  235. package/src/ingest/types.ts +76 -0
  236. package/src/ingest/unmount-ingest-root.ts +224 -0
  237. package/src/navmesh/navmesh-actions.ts +27 -0
  238. package/src/navmesh/navmesh-handler.ts +237 -0
  239. package/src/navmesh/navmesh-workflow-store.ts +78 -0
  240. package/src/network/NetworkInspectorPanel.tsx +644 -0
  241. package/src/network/network-inspector-model.ts +225 -0
  242. package/src/play/play-boot-stall.ts +118 -0
  243. package/src/play/play-log-events.ts +26 -0
  244. package/src/play/play-mode.ts +2553 -0
  245. package/src/play/play-recording.ts +335 -0
  246. package/src/play/react-play-live-authoring.ts +165 -0
  247. package/src/play-bar/PlayBar.tsx +488 -0
  248. package/src/play-bar/PlayerCountPicker.tsx +100 -0
  249. package/src/profiler/FrameDebuggerPanel.tsx +458 -0
  250. package/src/profiler/PerformancePanel.tsx +1068 -0
  251. package/src/profiler/ProfilerPanel.tsx +53 -0
  252. package/src/profiler/frame-debugger-store.ts +97 -0
  253. package/src/profiler/main-thread-busy.ts +90 -0
  254. package/src/react/design-time-react-mount.ts +782 -0
  255. package/src/react/dom-authoring-adapter.ts +764 -0
  256. package/src/react/pasteboard-materialize.ts +154 -0
  257. package/src/react/react-inspector-section.tsx +2496 -0
  258. package/src/react/react-world-authoring-adapter.ts +3795 -0
  259. package/src/react/story-documents/story-args-section.ts +19 -0
  260. package/src/react/story-documents/story-documents.tsx +1380 -0
  261. package/src/react/story-paint-bounds.ts +94 -0
  262. package/src/react/ui-board-document.tsx +101 -0
  263. package/src/react/ui-board-title.ts +9 -0
  264. package/src/react/ui-component-board.ts +72 -0
  265. package/src/services/audio-pose-guard.ts +81 -0
  266. package/src/services/game-audio-unlock.ts +48 -0
  267. package/src/state-watch/StateWatchPanel.tsx +535 -0
  268. package/src/three/authoring/camera-runtime-inspector-section.tsx +135 -0
  269. package/src/three/authoring/constraint-inspector-section.tsx +189 -0
  270. package/src/three/authoring/design-time-renderer.ts +189 -0
  271. package/src/three/authoring/model-asset-inspector-section.css +41 -0
  272. package/src/three/authoring/model-asset-inspector-section.tsx +869 -0
  273. package/src/three/authoring/oid-source-persistence.ts +557 -0
  274. package/src/three/authoring/r3f-design-session.ts +1174 -0
  275. package/src/three/authoring/r3f-source-authoring-adapter.ts +5949 -0
  276. package/src/three/authoring/reflection-probe-inspector-section.tsx +76 -0
  277. package/src/three/authoring/spatial-audio-handles.ts +215 -0
  278. package/src/three/authoring/spatial-collider-handles.ts +229 -0
  279. package/src/three/authoring/spatial-joint-handles.ts +114 -0
  280. package/src/three/authoring/spatial-light-handles.ts +178 -0
  281. package/src/three/authoring/spatial-lod-handles.ts +93 -0
  282. package/src/three/authoring/spatial-particle-handles.ts +368 -0
  283. package/src/three/authoring/three-authoring-adapter.ts +1953 -0
  284. package/src/three/authoring/three-scene-identity.ts +19 -0
  285. package/src/three/authoring/three-spatial-handles.ts +161 -0
  286. package/src/three/authoring/typed-three-inspector.ts +528 -0
  287. package/src/three/component-verbs/extract-menu.ts +71 -0
  288. package/src/three/component-verbs/fork-menu.ts +78 -0
  289. package/src/three/component-verbs/internals-menu.ts +91 -0
  290. package/src/three/story-documents/three-story-documents.tsx +607 -0
  291. package/src/three/three-board/ThreeBoardDocument.tsx +888 -0
  292. package/src/three/three-board/board-framing.ts +412 -0
  293. package/src/three/three-board/board-layout.ts +401 -0
  294. package/src/three/three-board/board-scene.ts +901 -0
  295. package/src/three/three-board/three-component-board.ts +62 -0
  296. package/src/xstate/XStateBehaviorSection.tsx +130 -0
  297. package/src/xstate/XStateMachineInspector.tsx +566 -0
  298. package/src/xstate/character-animation-machine.fixture.ts +74 -0
  299. package/src/xstate/live-behaviors.ts +106 -0
  300. package/src/xstate/use-live-actor-state.ts +44 -0
  301. package/src/xstate/xstate-graph.ts +235 -0
  302. package/src/xstate/xstate-layout.ts +76 -0
@@ -0,0 +1,107 @@
1
+ /**
2
+ * THE GAME DOCUMENT'S SHARED VIEW STATE — resolution and scale, read by the
3
+ * document's CONTENT (which owns the canvas resize/scale math) and written by
4
+ * its document-local TOOLBAR (the resolution/device pickers). A tiny
5
+ * module-scope store in the house `useSyncExternalStore` shape, session-scoped.
6
+ *
7
+ * It moved out of the host with the Game document itself (WORK.md §The
8
+ * workbench, P3b); `ResolutionPicker` stayed behind because the host's story
9
+ * documents read it, so it is reached through the `@editor/*` alias.
10
+ */
11
+
12
+ import { RESOLUTIONS, type Resolution } from '../host/components/ResolutionPicker';
13
+ import { type DevicePreset, setDevicePreset } from './device-preview';
14
+
15
+ // --- Game document view state (W2, inventory row V4) -----------------------
16
+ // Resolution/scale are shared between the Game document's CONTENT (which
17
+ // owns the canvas resize/scale math) and its document-local TOOLBAR (which
18
+ // hosts the picker + mute, §6.3 "Game mute/resolution controls appear only
19
+ // in the Game document") — a tiny module-scope store in the house
20
+ // `useSyncExternalStore` shape, session-scoped like the old GamePanel state.
21
+
22
+ let _gameResolution: Resolution = { label: 'Fill', width: null, height: null };
23
+ let _gameScale = 1;
24
+ let _gameViewVersion = 0;
25
+ const _gameViewListeners = new Set<() => void>();
26
+
27
+ export function notifyGameView(): void {
28
+ _gameViewVersion++;
29
+ for (const fn of _gameViewListeners) fn();
30
+ }
31
+
32
+ export function subscribeGameView(fn: () => void): () => void {
33
+ _gameViewListeners.add(fn);
34
+ return () => {
35
+ _gameViewListeners.delete(fn);
36
+ };
37
+ }
38
+
39
+ export const gameViewVersion = () => _gameViewVersion;
40
+
41
+ /** The Game document's current resolution selection (exported for tests). */
42
+ export function gameDocumentResolution(): Resolution {
43
+ return _gameResolution;
44
+ }
45
+
46
+ /** Set the Game document's resolution (exported for tests). */
47
+ export function setGameDocumentResolution(resolution: Resolution): void {
48
+ _gameResolution = resolution;
49
+ notifyGameView();
50
+ }
51
+
52
+ /** What the toolbar's resolution button SHOWS. The presentation precedence
53
+ * lives in `resolvePresentedSize`; this is its display twin: an explicit
54
+ * pick (device preset / resolution) already carries its numbers, but when
55
+ * the store still says Fill and the project declares a `resolution`, the
56
+ * declared size is what is actually on screen — so it is what the bar must
57
+ * say. (Exported for tests.) */
58
+ export function displayedGameResolution(
59
+ picked: Resolution,
60
+ declared: { width: number; height: number } | undefined,
61
+ ): Resolution {
62
+ if (picked.width !== null || !declared) return picked;
63
+ return {
64
+ label: `Project — ${declared.width}×${declared.height}`,
65
+ width: declared.width,
66
+ height: declared.height,
67
+ };
68
+ }
69
+
70
+ /** Menu entries for the Game toolbar's picker. With a declared resolution,
71
+ * "Fill" is unreachable by design (`resolvePresentedSize` puts the declared
72
+ * size back the moment nothing else is picked), so offering it would be a
73
+ * lie — the declared entry stands in its place. */
74
+ export function gameResolutionOptions(
75
+ declared: { width: number; height: number } | undefined,
76
+ ): Resolution[] {
77
+ if (!declared) return RESOLUTIONS;
78
+ return [
79
+ displayedGameResolution({ label: 'Fill', width: null, height: null }, declared),
80
+ ...RESOLUTIONS.filter((r) => r.width !== null),
81
+ ];
82
+ }
83
+
84
+ /** One entry point for "a device preset was chosen" (dropdown pick AND
85
+ * per-project restore): the device-preview store takes the preset (DPR,
86
+ * safe-area, touch policy) and the shared game-resolution store takes its
87
+ * CSS resolution, so the existing GamePanel letterbox/scale path applies. */
88
+ export function applyGameDevicePreset(preset: DevicePreset): void {
89
+ setDevicePreset(preset.id);
90
+ setGameDocumentResolution(
91
+ preset.width === null || preset.height === null
92
+ ? { label: 'Fill', width: null, height: null }
93
+ : { label: preset.label, width: preset.width, height: preset.height },
94
+ );
95
+ }
96
+
97
+ export function setGameScale(scale: number): void {
98
+ if (scale === _gameScale) return;
99
+ _gameScale = scale;
100
+ notifyGameView();
101
+ }
102
+
103
+ /** The scale the content last fitted the game at — what the toolbar's
104
+ * percentage readout shows. */
105
+ export function gameDocumentScale(): number {
106
+ return _gameScale;
107
+ }
@@ -0,0 +1,187 @@
1
+ /**
2
+ * Play-mode physics debug draw (W2a §2.3c) — Rapier's `debugRender()` collider
3
+ * /joint wireframes plus live contact-point markers, over the running game in
4
+ * the Game tab.
5
+ *
6
+ * Split of labor:
7
+ * - The WIREFRAMES are the engine's own: `PhysicsAdapter.setDebugDrawEnabled`
8
+ * flips the flag the first-party adapter's render system reads, feeding
9
+ * `world.debugRender()` into the debug `LineSegments` that already lives in
10
+ * the game scene (`setup-physics.ts`, buffer-reusing). The editor does zero
11
+ * per-frame work for them.
12
+ * - The CONTACT POINTS come from `PhysicsAdapter.contactPoints()` (world-space
13
+ * solver contacts over a reused buffer) and are drawn here as a pooled
14
+ * `THREE.Points` in the game scene, updated per RAF tick.
15
+ *
16
+ * Reads only `SystemAdapters.physics` capabilities off the active game
17
+ * (installed by play-mode's `setActiveSystems`) — the editor never touches
18
+ * Rapier directly. An adapter lacking the capabilities draws nothing
19
+ * (capability-absence degradation). Toggled from the Game tab's Debug menu
20
+ * (`CenterDocuments.tsx`), mirroring `crowd-debug.ts` exactly: RAF-driven,
21
+ * scene-swap safe across play restarts, torn down on disable; on stop the
22
+ * adapters are cleared (`setActiveSystems(null)`) and the game scene is
23
+ * destroyed, so nothing leaks into the next session.
24
+ */
25
+
26
+ import { getActiveSystems } from '@volter/editor-core/authoring/active-systems';
27
+ import { nodeKeyedPhysics, type PhysicsAdapter } from '@volter/editor-project/adapter';
28
+ import { setUserData } from '@volter/threejs-runtime/ecs/user-data';
29
+ import * as THREE from 'three';
30
+ import { getGameScene } from '../play/play-mode';
31
+
32
+ let _enabled = false;
33
+ let _raf = 0;
34
+ let _group: THREE.Group | null = null;
35
+ let _attachedScene: THREE.Scene | null = null;
36
+ /** The adapter instance the debug-draw flag was last applied to (play restarts install a fresh one). */
37
+ let _appliedAdapter: PhysicsAdapter | null = null;
38
+
39
+ let _points: THREE.Points | null = null;
40
+ let _pointsCapacity = 0;
41
+
42
+ export function physicsDebugEnabled(): boolean {
43
+ return _enabled;
44
+ }
45
+
46
+ export function setPhysicsDebugEnabled(enabled: boolean): void {
47
+ if (enabled === _enabled) return;
48
+ _enabled = enabled;
49
+ if (enabled) {
50
+ _raf = requestAnimationFrame(_tick);
51
+ } else {
52
+ cancelAnimationFrame(_raf);
53
+ _appliedAdapter?.setDebugDrawEnabled?.(false);
54
+ _teardown();
55
+ }
56
+ }
57
+
58
+ function _tick(): void {
59
+ if (!_enabled) return;
60
+ _raf = requestAnimationFrame(_tick);
61
+ const scene = getGameScene();
62
+ // Node-id keyed only: the wireframes and contact points are THREE objects
63
+ // drawn into the game scene, so a canvas mount's display-keyed carrier is
64
+ // not a degraded source here — it is a different subsystem entirely.
65
+ const physics = scene ? nodeKeyedPhysics(getActiveSystems().physics) : null;
66
+
67
+ // (Re)apply the engine-side debug-draw flag whenever the live adapter
68
+ // changes — enabling before play starts, and re-enabling across play
69
+ // restarts, both converge here.
70
+ if (physics !== _appliedAdapter) {
71
+ _appliedAdapter?.setDebugDrawEnabled?.(false);
72
+ _appliedAdapter = physics;
73
+ physics?.setDebugDrawEnabled?.(true);
74
+ }
75
+
76
+ _syncAttachment(scene);
77
+ if (!_group || !scene) return;
78
+
79
+ const contacts = physics?.contactPoints?.() ?? null;
80
+ _updateContactPoints(_group, contacts);
81
+ }
82
+
83
+ /** (Re)attach the marker group to whatever game scene is currently live. */
84
+ function _syncAttachment(scene: THREE.Scene | null): void {
85
+ if (scene === _attachedScene) return;
86
+ if (_attachedScene && _group) _attachedScene.remove(_group);
87
+ _attachedScene = scene;
88
+ if (scene) {
89
+ if (!_group) {
90
+ _group = new THREE.Group();
91
+ _group.name = 'physics-debug-draw';
92
+ setUserData(_group, 'editorHelper', true);
93
+ }
94
+ scene.add(_group);
95
+ }
96
+ }
97
+
98
+ /** Copy this frame's contact xyz triples into the pooled Points geometry. */
99
+ function _updateContactPoints(group: THREE.Group, contacts: Float32Array | null): void {
100
+ const count = contacts ? contacts.length / 3 : 0;
101
+ if (!_points) {
102
+ _pointsCapacity = 64;
103
+ const geo = new THREE.BufferGeometry();
104
+ geo.setAttribute(
105
+ 'position',
106
+ new THREE.BufferAttribute(new Float32Array(_pointsCapacity * 3), 3),
107
+ );
108
+ const mat = new THREE.PointsMaterial({
109
+ color: 0xff3355,
110
+ size: 10,
111
+ sizeAttenuation: false,
112
+ depthTest: false,
113
+ transparent: true,
114
+ opacity: 0.95,
115
+ });
116
+ _points = new THREE.Points(geo, mat);
117
+ _points.renderOrder = 999;
118
+ _points.frustumCulled = false;
119
+ group.add(_points);
120
+ }
121
+ const geo = _points.geometry;
122
+ if (count > _pointsCapacity) {
123
+ // Rare growth path (matches `updatePhysicsDebug`'s size-change handling):
124
+ // replace the attribute only when capacity is actually exceeded.
125
+ while (_pointsCapacity < count) _pointsCapacity *= 2;
126
+ geo.setAttribute(
127
+ 'position',
128
+ new THREE.BufferAttribute(new Float32Array(_pointsCapacity * 3), 3),
129
+ );
130
+ }
131
+ const attr = geo.getAttribute('position') as THREE.BufferAttribute;
132
+ if (contacts && count > 0) (attr.array as Float32Array).set(contacts);
133
+ attr.needsUpdate = true;
134
+ geo.setDrawRange(0, count);
135
+ _points.visible = count > 0;
136
+ }
137
+
138
+ /**
139
+ * e2e hook (mirrors the world root's stage's `__vgaiViewport`): mechanical state for
140
+ * The question this answers — is the Rapier debug wireframe live in the
141
+ * game scene with real geometry, and how many contact markers are drawn.
142
+ */
143
+ function _stateForE2E(): {
144
+ enabled: boolean;
145
+ wireframeInScene: boolean;
146
+ wireframeVisible: boolean;
147
+ wireframeVertexCount: number;
148
+ contactMarkerCount: number;
149
+ } {
150
+ const scene = getGameScene();
151
+ const adapter = scene ? nodeKeyedPhysics(getActiveSystems().physics) : null;
152
+ const mesh = (adapter?.debugDraw?.() ?? null) as THREE.LineSegments | null;
153
+ let wireframeInScene = false;
154
+ if (mesh && scene) {
155
+ scene.traverse((o) => {
156
+ if (o === mesh) wireframeInScene = true;
157
+ });
158
+ }
159
+ const posAttr = mesh?.geometry.getAttribute('position') as THREE.BufferAttribute | undefined;
160
+ const drawRange = _points?.geometry.drawRange.count ?? 0;
161
+ return {
162
+ enabled: _enabled,
163
+ wireframeInScene,
164
+ wireframeVisible: mesh?.visible ?? false,
165
+ wireframeVertexCount: posAttr?.count ?? 0,
166
+ contactMarkerCount: _points?.visible && Number.isFinite(drawRange) ? drawRange : 0,
167
+ };
168
+ }
169
+ if (typeof window !== 'undefined') {
170
+ (window as unknown as Record<string, unknown>)['__vgaiPhysicsDebug'] = _stateForE2E;
171
+ }
172
+
173
+ function _teardown(): void {
174
+ if (_group) {
175
+ _attachedScene?.remove(_group);
176
+ if (_points) {
177
+ _points.geometry.dispose();
178
+ (_points.material as THREE.Material).dispose();
179
+ }
180
+ _group.clear();
181
+ }
182
+ _points = null;
183
+ _pointsCapacity = 0;
184
+ _group = null;
185
+ _attachedScene = null;
186
+ _appliedAdapter = null;
187
+ }