@downdraft/engine 0.2.0 → 0.3.1

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 (138) hide show
  1. package/app/src/renderer/bootstrap.ts +0 -6
  2. package/app/src/renderer/feature-log.ts +1 -9
  3. package/app/src/renderer/mcp-harness.ts +8 -11
  4. package/app/src/renderer/standard-automation-tools.ts +8 -145
  5. package/core/src/index.ts +2 -2
  6. package/core/src/module/host.ts +3 -6
  7. package/core/src/module/index.ts +1 -1
  8. package/core/src/module/module.ts +4 -47
  9. package/core/src/physics/interface.ts +5 -0
  10. package/core/src/platform/render-surface.ts +13 -0
  11. package/core/src/platform/rpc.ts +4 -1
  12. package/core/src/render/canvas-resize-watcher.ts +1 -10
  13. package/core/src/render/game-renderer.ts +164 -79
  14. package/core/src/render/index.ts +0 -2
  15. package/core/src/render/input-manager.ts +7 -7
  16. package/core/src/render/renderer-module-host.ts +2 -4
  17. package/core/src/sab/grid-buffer.ts +55 -2
  18. package/core/src/sab/record-buffer.ts +48 -5
  19. package/core/src/sab/sim-channel.ts +181 -17
  20. package/core/src/sab/slot-buffer.ts +55 -1
  21. package/core/src/sab/types.ts +11 -0
  22. package/core/src/telemetry/gpu-resource-tracker.ts +24 -0
  23. package/core/src/ui-font-scale.ts +5 -38
  24. package/core/src/util/index.ts +1 -0
  25. package/core/src/util/session-timer.ts +23 -0
  26. package/core/src/worker/base-worker-host.ts +2 -1
  27. package/core/src/worker/buffer-sync.ts +20 -13
  28. package/core/src/worker/rpc.ts +119 -5
  29. package/core/src/worker/sim-worker-base.ts +5 -2
  30. package/core/src/worker/task-pool.ts +4 -0
  31. package/dist/.dd-manifest.json +893 -908
  32. package/dist/app/src/renderer/bootstrap.js +0 -4
  33. package/dist/app/src/renderer/feature-log.js +1 -10
  34. package/dist/app/src/renderer/mcp-harness.js +8 -11
  35. package/dist/app/src/renderer/standard-automation-tools.js +6 -138
  36. package/dist/core/src/index.js +2 -2
  37. package/dist/core/src/module/host.js +3 -6
  38. package/dist/core/src/platform/rpc.js +2 -1
  39. package/dist/core/src/render/canvas-resize-watcher.js +1 -10
  40. package/dist/core/src/render/game-renderer.js +166 -74
  41. package/dist/core/src/render/index.js +0 -1
  42. package/dist/core/src/render/input-manager.js +7 -7
  43. package/dist/core/src/render/renderer-module-host.js +2 -4
  44. package/dist/core/src/sab/grid-buffer.js +53 -1
  45. package/dist/core/src/sab/record-buffer.js +49 -5
  46. package/dist/core/src/sab/sim-channel.js +173 -17
  47. package/dist/core/src/sab/slot-buffer.js +54 -1
  48. package/dist/core/src/telemetry/gpu-resource-tracker.js +26 -0
  49. package/dist/core/src/ui-font-scale.js +5 -38
  50. package/dist/core/src/util/index.js +1 -0
  51. package/dist/core/src/util/session-timer.js +22 -0
  52. package/dist/core/src/worker/base-worker-host.js +2 -1
  53. package/dist/core/src/worker/buffer-sync.js +16 -9
  54. package/dist/core/src/worker/rpc.js +105 -8
  55. package/dist/core/src/worker/sim-worker-base.js +5 -2
  56. package/dist/core/src/worker/task-pool.js +5 -0
  57. package/dist/libraries/blitz-ui/src/native-module.js +8 -1
  58. package/dist/libraries/devtools/src/backend.js +35 -9
  59. package/dist/libraries/devtools/src/collectors.js +73 -22
  60. package/dist/libraries/devtools/src/native-providers.js +0 -2
  61. package/dist/libraries/entities/src/model-renderer.js +87 -27
  62. package/dist/libraries/entities/src/shaders/model.wgsl +13 -4
  63. package/dist/libraries/networking/src/index.js +1 -0
  64. package/dist/libraries/networking/src/ws-server.js +387 -0
  65. package/dist/libraries/physics-rapier/src/api.js +15 -0
  66. package/dist/libraries/physics-rapier/src/backend.js +15 -0
  67. package/dist/libraries/physics-rapier/src/rapier-backend.js +5 -0
  68. package/dist/libraries/postfx/src/post-process-stack.js +1 -3
  69. package/dist/libraries/sand/src/materials.js +2 -32
  70. package/dist/libraries/sand/src/sand-world.js +0 -3
  71. package/dist/modules/devtools/src/api.js +8 -23
  72. package/dist/modules/devtools/src/blitz/fonts.js +75 -0
  73. package/dist/modules/devtools/src/blitz/host.js +124 -30
  74. package/dist/modules/devtools/src/blitz/panels/console.js +19 -7
  75. package/dist/modules/devtools/src/blitz/panels/elements.js +217 -28
  76. package/dist/modules/devtools/src/blitz/panels/gpu.js +5 -2
  77. package/dist/modules/devtools/src/blitz/panels/performance.js +26 -12
  78. package/dist/modules/devtools/src/blitz/panels/snapshot.js +31 -8
  79. package/dist/modules/devtools/src/blitz/panels/types.js +2 -1
  80. package/dist/modules/devtools/src/blitz/theme.js +26 -4
  81. package/dist/modules/devtools/src/data-bridge.js +0 -11
  82. package/dist/modules/devtools/src/index.js +0 -6
  83. package/dist/modules/devtools/src/init.js +8 -42
  84. package/dist/modules/devtools/src/scene-inspector.js +1 -4
  85. package/dist/modules/devtools/src/worker-sync.js +3 -11
  86. package/dist/modules/html-ui/src/doc-backend.js +52 -3
  87. package/dist/modules/html-ui/src/host.js +6 -3
  88. package/dist/test/src/vision-test.js +1 -3
  89. package/libraries/blitz-ui/native/Cargo.toml +6 -0
  90. package/libraries/blitz-ui/native-osr/Cargo.toml +3 -1
  91. package/libraries/blitz-ui/src/native-module.ts +8 -1
  92. package/libraries/devtools/src/backend.ts +38 -12
  93. package/libraries/devtools/src/collectors.ts +75 -22
  94. package/libraries/devtools/src/native-providers.ts +0 -2
  95. package/libraries/entities/src/model-renderer.ts +88 -27
  96. package/libraries/entities/src/shaders/model.wgsl +13 -4
  97. package/libraries/networking/src/index.ts +4 -0
  98. package/libraries/networking/src/ws-server.ts +397 -0
  99. package/libraries/physics-rapier/src/api.ts +15 -0
  100. package/libraries/physics-rapier/src/backend.ts +15 -0
  101. package/libraries/physics-rapier/src/rapier-backend.ts +6 -0
  102. package/libraries/postfx/src/post-process-stack.ts +1 -2
  103. package/libraries/sand/src/materials.ts +2 -32
  104. package/libraries/sand/src/sand-world.ts +0 -4
  105. package/modules/devtools/README.md +1 -3
  106. package/modules/devtools/src/api.ts +10 -34
  107. package/modules/devtools/src/blitz/fonts.ts +78 -0
  108. package/modules/devtools/src/blitz/host.ts +107 -31
  109. package/modules/devtools/src/blitz/panels/console.ts +20 -8
  110. package/modules/devtools/src/blitz/panels/elements.ts +212 -31
  111. package/modules/devtools/src/blitz/panels/gpu.ts +4 -2
  112. package/modules/devtools/src/blitz/panels/performance.ts +24 -12
  113. package/modules/devtools/src/blitz/panels/snapshot.ts +30 -8
  114. package/modules/devtools/src/blitz/panels/types.ts +2 -1
  115. package/modules/devtools/src/blitz/theme.ts +26 -4
  116. package/modules/devtools/src/data-bridge.ts +0 -16
  117. package/modules/devtools/src/index.ts +1 -11
  118. package/modules/devtools/src/init.ts +8 -51
  119. package/modules/devtools/src/scene-inspector.ts +2 -6
  120. package/modules/devtools/src/types.ts +5 -90
  121. package/modules/devtools/src/worker-sync.ts +3 -13
  122. package/modules/html-ui/src/doc-backend.ts +67 -4
  123. package/modules/html-ui/src/host.ts +11 -3
  124. package/modules/html-ui/src/protocol.ts +1 -1
  125. package/package.json +4 -5
  126. package/test/src/vision-test.ts +2 -4
  127. package/core/src/render/sab-canvas-overlay.ts +0 -122
  128. package/dist/core/src/render/sab-canvas-overlay.js +0 -83
  129. package/dist/modules/devtools/src/debug-overlay.js +0 -181
  130. package/dist/modules/devtools/src/doctor-panel.js +0 -163
  131. package/dist/modules/devtools/src/label-overlay.js +0 -129
  132. package/dist/modules/devtools/src/material-stats-panel.js +0 -171
  133. package/dist/modules/devtools/src/sim-stats-panel.js +0 -250
  134. package/modules/devtools/src/debug-overlay.ts +0 -238
  135. package/modules/devtools/src/doctor-panel.ts +0 -182
  136. package/modules/devtools/src/label-overlay.ts +0 -154
  137. package/modules/devtools/src/material-stats-panel.ts +0 -188
  138. package/modules/devtools/src/sim-stats-panel.ts +0 -272
@@ -1,171 +0,0 @@
1
- // ============================================================================
2
- // createMaterialStatsPanelExtension — reusable DevTools "Materials" tab for
3
- // any game using the unified material system.
4
- //
5
- // Shows:
6
- // - Registered material count, material types breakdown
7
- // - Variant cache size, LRU hits/misses
8
- // - Shader graph node count, compiled WGSL size
9
- // - GPU texture count/bytes (from GPUResourceTracker)
10
- //
11
- // Gated by requiredMethods: ["getMaterialStats"]. The framework auto-provides
12
- // getMaterialStats() from the renderer's MaterialLibrary / variant cache
13
- // (feature-detected in createDevToolsRendererAdapter or registered via
14
- // devtools.registerDataFeed).
15
- // ============================================================================
16
- export function createMaterialStatsPanelExtension(opts = {}) {
17
- const tabLabel = opts.tabLabel ?? "Materials";
18
- const tabTooltip = opts.tabTooltip ?? "Material system stats (library, variants, GPU textures)";
19
- const order = opts.order ?? 25;
20
- const hasExtra = typeof opts.extraRows === "function";
21
- const extraRowsSrc = hasExtra ? opts.extraRows.toString() : "null";
22
- const html = `
23
- <div class="panel-header">
24
- <h3>Materials</h3>
25
- <span id="mat-status-badge" class="perf-status">Waiting for data...</span>
26
- </div>
27
- <div id="mat-stats-panel">
28
- <div class="debug-section">
29
- <h4>Library</h4>
30
- <div id="mat-library-grid" class="debug-grid"></div>
31
- </div>
32
- <div class="debug-section">
33
- <h4>Variants</h4>
34
- <div id="mat-variants-grid" class="debug-grid"></div>
35
- </div>
36
- <div class="debug-section">
37
- <h4>GPU Textures</h4>
38
- <div id="mat-textures-grid" class="debug-grid"></div>
39
- </div>
40
- <div class="debug-section">
41
- <h4>Shader Graph</h4>
42
- <div id="mat-graph-grid" class="debug-grid"></div>
43
- </div>
44
- </div>
45
- `;
46
- const css = `
47
- #mat-stats-panel {
48
- flex: 1;
49
- overflow-y: auto;
50
- padding: 8px 12px;
51
- }
52
- #mat-stats-panel .debug-section {
53
- margin-bottom: 12px;
54
- }
55
- #mat-stats-panel .debug-section h4 {
56
- font-size: 12px;
57
- color: #569cd6;
58
- margin: 8px 0 4px 0;
59
- border-bottom: 1px solid #333;
60
- padding-bottom: 2px;
61
- }
62
- #mat-status-badge.running { color: #98c379; }
63
- #mat-status-badge.empty { color: #e5c07b; }
64
- `;
65
- const script = `
66
- (function(DevToolsPanel) {
67
- var callInspector = DevToolsPanel.callInspector;
68
- var evalInPage = DevToolsPanel.evalInPage;
69
- var escapeHtml = DevToolsPanel.escapeHtml;
70
- var fmtVal = DevToolsPanel.fmtVal;
71
- var debugGridHtml = DevToolsPanel.debugGridHtml;
72
-
73
- var extraRowsFn = ${extraRowsSrc};
74
-
75
- function refreshMatStats() {
76
- var libEl = document.getElementById("mat-library-grid");
77
- var varEl = document.getElementById("mat-variants-grid");
78
- var texEl = document.getElementById("mat-textures-grid");
79
- var graphEl = document.getElementById("mat-graph-grid");
80
- var statusEl = document.getElementById("mat-status-badge");
81
-
82
- evalInPage(
83
- "window.__sceneInspector ? JSON.stringify(window.__sceneInspector.getMaterialStats()) : null",
84
- function (result, err) {
85
- if (err || !result) {
86
- if (libEl) libEl.innerHTML = '<div class="debug-label">Not available</div>';
87
- if (statusEl) { statusEl.textContent = "Not available"; statusEl.className = "perf-status"; }
88
- return;
89
- }
90
- try {
91
- var s = JSON.parse(result);
92
- if (!s) {
93
- if (libEl) libEl.innerHTML = '<div class="debug-label">Not available</div>';
94
- return;
95
- }
96
-
97
- // Library stats
98
- var libRows = [
99
- ["Registered", s.library?.count ?? 0],
100
- ["Types", s.library?.types?.join(", ") ?? "—"],
101
- ];
102
- if (libEl) libEl.innerHTML = debugGridHtml(libRows);
103
-
104
- // Variant stats
105
- var varRows = [
106
- ["Cached Pipelines", s.variants?.cachedPipelines ?? 0],
107
- ["Max Pipelines", s.variants?.maxPipelines ?? 0],
108
- ["LRU Hits", s.variants?.lruHits ?? 0],
109
- ["LRU Misses", s.variants?.lruMisses ?? 0],
110
- ["Permutation Count", s.variants?.permutationCount ?? 0],
111
- ];
112
- if (varEl) varEl.innerHTML = debugGridHtml(varRows);
113
-
114
- // GPU textures
115
- var texRows = [
116
- ["Texture Count", s.textures?.count ?? 0],
117
- ["Texture Memory", fmtVal((s.textures?.bytes ?? 0) / 1048576, 1) + " MB"],
118
- ["Bindless Entries", s.textures?.bindlessEntries ?? 0],
119
- ];
120
- if (texEl) texEl.innerHTML = debugGridHtml(texRows);
121
-
122
- // Shader graph
123
- var graphRows = [
124
- ["Graph Materials", s.graph?.materialCount ?? 0],
125
- ["Total Nodes", s.graph?.totalNodes ?? 0],
126
- ["Total Connections", s.graph?.totalConnections ?? 0],
127
- ["Compiled WGSL Size", fmtVal((s.graph?.compiledWgslBytes ?? 0) / 1024, 1) + " KB"],
128
- ];
129
- if (graphEl) graphEl.innerHTML = debugGridHtml(graphRows);
130
-
131
- // Extra rows
132
- if (extraRowsFn && s) {
133
- try {
134
- var extra = extraRowsFn(s);
135
- if (extra && extra.length) {
136
- var extraHtml = debugGridHtml(extra);
137
- if (graphEl) graphEl.innerHTML += extraHtml;
138
- }
139
- } catch (e) { /* ignore */ }
140
- }
141
-
142
- if (statusEl) {
143
- var count = s.library?.count ?? 0;
144
- statusEl.textContent = count > 0 ? count + " materials" : "No materials";
145
- statusEl.className = "perf-status " + (count > 0 ? "running" : "empty");
146
- }
147
- } catch (e) {
148
- if (libEl) libEl.innerHTML = '<div class="debug-label">Error: ' + escapeHtml(String(e)) + '</div>';
149
- }
150
- }
151
- );
152
- }
153
-
154
- return {
155
- onActivate: function () { refreshMatStats(); },
156
- onRefresh: refreshMatStats,
157
- onDeactivate: function () {},
158
- };
159
- })
160
- `;
161
- return {
162
- id: "material-stats",
163
- tabLabel,
164
- tabTooltip,
165
- order,
166
- requiredMethods: ["getMaterialStats"],
167
- html,
168
- css,
169
- script,
170
- };
171
- }
@@ -1,250 +0,0 @@
1
- // ============================================================================
2
- // createSimStatsPanelExtension — reusable DevTools "Sim" tab for any game
3
- // that implements ISimStatsProvider (exposed via __sceneInspector).
4
- //
5
- // Renders a stats grid (FPS, tick, frame, paused, speed + game-specific extra
6
- // rows) and control buttons (Pause/Resume, Step, Clear) + a speed slider.
7
- // The tab is gated by requiredMethods so it only appears for games that
8
- // implement the provider.
9
- // ============================================================================
10
- export function createSimStatsPanelExtension(opts = {}) {
11
- const tabLabel = opts.tabLabel ?? "Sim";
12
- const tabTooltip = opts.tabTooltip ?? "Simulation stats & controls";
13
- const order = opts.order ?? 50;
14
- const hasExtra = typeof opts.extraRows === "function";
15
- // The extraRows callback runs in the page context (renderer side), but the
16
- // panel script is sandboxed and can't see the TS closure. We serialize the
17
- // callback's source so it can be eval'd in-page alongside getSimStats().
18
- // If no extraRows is provided, the script skips the extra-rows section.
19
- const extraRowsSrc = hasExtra
20
- ? opts.extraRows.toString()
21
- : "null";
22
- const html = `
23
- <div class="panel-header">
24
- <h3>Simulation</h3>
25
- <span id="sim-status-badge" class="perf-status">Waiting for data...</span>
26
- </div>
27
- <div id="sim-stats-panel">
28
- <div class="debug-section">
29
- <h4>Stats</h4>
30
- <div id="sim-stats-grid" class="debug-grid"></div>
31
- </div>
32
- <div class="debug-section">
33
- <h4>Controls</h4>
34
- <div class="world-control-row">
35
- <button id="btn-sim-pause" class="import-action-btn">Pause</button>
36
- <button id="btn-sim-step" class="import-action-btn" title="Advance one tick while paused">Step</button>
37
- <button id="btn-sim-clear" class="import-action-btn" title="Clear/reset the simulation">Clear</button>
38
- </div>
39
- <div class="world-control-row">
40
- <label>Speed <span id="sim-speed-display" style="color:#888;font-weight:normal">1.00x</span></label>
41
- <input type="range" id="sim-speed-slider" min="0" max="10" step="0.25" value="1" style="flex:1">
42
- <button id="btn-sim-speed-reset" class="import-action-btn">Reset</button>
43
- </div>
44
- </div>
45
- </div>
46
- `;
47
- const css = `
48
- #sim-stats-panel {
49
- flex: 1;
50
- overflow-y: auto;
51
- padding: 8px 12px;
52
- }
53
- #sim-stats-panel .debug-section {
54
- margin-bottom: 12px;
55
- }
56
- #sim-stats-panel .debug-section h4 {
57
- font-size: 12px;
58
- color: #569cd6;
59
- margin: 8px 0 4px 0;
60
- border-bottom: 1px solid #333;
61
- padding-bottom: 2px;
62
- }
63
- #sim-stats-panel .world-control-row {
64
- display: flex;
65
- align-items: center;
66
- flex-wrap: wrap;
67
- gap: 8px;
68
- padding: 4px 0;
69
- }
70
- #sim-stats-panel .world-control-row label {
71
- display: flex;
72
- align-items: center;
73
- gap: 4px;
74
- font-size: 11px;
75
- color: #d4d4d4;
76
- }
77
- #sim-stats-panel .world-control-row input[type="range"] {
78
- accent-color: #0e639c;
79
- cursor: pointer;
80
- }
81
- #sim-stats-panel .import-action-btn {
82
- background: #0e639c;
83
- color: #fff;
84
- border: 1px solid #0e639c;
85
- padding: 3px 10px;
86
- font-size: 11px;
87
- border-radius: 2px;
88
- cursor: pointer;
89
- }
90
- #sim-stats-panel .import-action-btn:hover {
91
- background: #1177bb;
92
- }
93
- #sim-stats-panel #btn-sim-clear {
94
- background: #5a1d1d;
95
- border-color: #5a1d1d;
96
- }
97
- #sim-stats-panel #btn-sim-clear:hover {
98
- background: #7a2828;
99
- }
100
- #sim-status-badge.paused {
101
- color: #e5c07b;
102
- }
103
- #sim-status-badge.running {
104
- color: #98c379;
105
- }
106
- `;
107
- // The script is sandbox-evaled with {document, console, panel} and uses
108
- // window.DevToolsPanel helpers (callInspector, evalInPage, escapeHtml,
109
- // fmtVal, debugGridHtml). It returns {onActivate, onRefresh, onDeactivate}.
110
- const script = `
111
- (function(DevToolsPanel) {
112
- var callInspector = DevToolsPanel.callInspector;
113
- var evalInPage = DevToolsPanel.evalInPage;
114
- var escapeHtml = DevToolsPanel.escapeHtml;
115
- var fmtVal = DevToolsPanel.fmtVal;
116
- var debugGridHtml = DevToolsPanel.debugGridHtml;
117
-
118
- // extraRowsFn is serialized from the game's SimStatsPanelOptions.extraRows
119
- // callback. It runs in the PAGE context (not the sandbox) so it can access
120
- // the full ISimStats object returned by __sceneInspector.getSimStats().
121
- var extraRowsFn = ${extraRowsSrc};
122
-
123
- function refreshSimStats() {
124
- var gridEl = document.getElementById("sim-stats-grid");
125
- var statusEl = document.getElementById("sim-status-badge");
126
- evalInPage(
127
- "window.__sceneInspector ? JSON.stringify(window.__sceneInspector.getSimStats()) : null",
128
- function (result, err) {
129
- if (err || !result) {
130
- if (gridEl) gridEl.innerHTML = '<div class="debug-label">Not available</div>';
131
- if (statusEl) { statusEl.textContent = "Not available"; statusEl.className = "perf-status"; }
132
- return;
133
- }
134
- try {
135
- var s = JSON.parse(result);
136
- if (!s) {
137
- if (gridEl) gridEl.innerHTML = '<div class="debug-label">Not available</div>';
138
- return;
139
- }
140
- var rows = [
141
- ["FPS", fmtVal(s.fps, 0)],
142
- ["Tick", s.tick],
143
- ["Frame", s.frame],
144
- ["Paused", s.paused ? "Yes" : "No"],
145
- ["Speed", fmtVal(s.speed, 2) + "x"],
146
- ];
147
- if (extraRowsFn && s.extra) {
148
- try {
149
- var extra = extraRowsFn(s);
150
- if (extra && extra.length) {
151
- for (var i = 0; i < extra.length; i++) {
152
- rows.push([extra[i][0], String(extra[i][1])]);
153
- }
154
- }
155
- } catch (e) {
156
- rows.push(["Extra error", escapeHtml(String(e))]);
157
- }
158
- }
159
- if (gridEl) gridEl.innerHTML = debugGridHtml(rows);
160
- if (statusEl) {
161
- statusEl.textContent = s.paused ? "Paused" : "Running";
162
- statusEl.className = "perf-status " + (s.paused ? "paused" : "running");
163
- }
164
- // Sync pause button label
165
- var pauseBtn = document.getElementById("btn-sim-pause");
166
- if (pauseBtn) pauseBtn.textContent = s.paused ? "Resume" : "Pause";
167
- // Sync speed slider/display (only if not focused — avoid clobbering user input)
168
- var slider = document.getElementById("sim-speed-slider");
169
- var display = document.getElementById("sim-speed-display");
170
- if (slider && document.activeElement !== slider) slider.value = s.speed;
171
- if (display) display.textContent = fmtVal(s.speed, 2) + "x";
172
- } catch (e) {
173
- if (gridEl) gridEl.innerHTML = '<div class="debug-label">Error: ' + escapeHtml(String(e)) + '</div>';
174
- }
175
- }
176
- );
177
- }
178
-
179
- function setupControls() {
180
- var pauseBtn = document.getElementById("btn-sim-pause");
181
- if (pauseBtn) {
182
- pauseBtn.addEventListener("click", function () {
183
- // Toggle based on current label to avoid race with refresh
184
- var isPaused = pauseBtn.textContent === "Resume";
185
- if (isPaused) {
186
- callInspector("resumeSim", null);
187
- } else {
188
- callInspector("pauseSim", null);
189
- }
190
- setTimeout(refreshSimStats, 50);
191
- });
192
- }
193
- var stepBtn = document.getElementById("btn-sim-step");
194
- if (stepBtn) {
195
- stepBtn.addEventListener("click", function () {
196
- callInspector("stepSim", null);
197
- setTimeout(refreshSimStats, 50);
198
- });
199
- }
200
- var clearBtn = document.getElementById("btn-sim-clear");
201
- if (clearBtn) {
202
- clearBtn.addEventListener("click", function () {
203
- callInspector("clearSim", null);
204
- setTimeout(refreshSimStats, 50);
205
- });
206
- }
207
- var slider = document.getElementById("sim-speed-slider");
208
- var display = document.getElementById("sim-speed-display");
209
- if (slider) {
210
- slider.addEventListener("input", function () {
211
- var speed = parseFloat(slider.value);
212
- if (display) display.textContent = speed.toFixed(2) + "x";
213
- });
214
- slider.addEventListener("change", function () {
215
- var speed = parseFloat(slider.value);
216
- callInspector("setSimSpeed", [speed]);
217
- });
218
- }
219
- var resetBtn = document.getElementById("btn-sim-speed-reset");
220
- if (resetBtn) {
221
- resetBtn.addEventListener("click", function () {
222
- if (slider) slider.value = 1;
223
- if (display) display.textContent = "1.00x";
224
- callInspector("setSimSpeed", [1]);
225
- });
226
- }
227
- }
228
-
229
- return {
230
- onActivate: function () {
231
- setupControls();
232
- // Initial refresh — onRefresh timer is started by the panel core
233
- refreshSimStats();
234
- },
235
- onRefresh: refreshSimStats,
236
- onDeactivate: function () {},
237
- };
238
- })
239
- `;
240
- return {
241
- id: "sim-stats",
242
- tabLabel,
243
- tabTooltip,
244
- order,
245
- requiredMethods: ["getSimStats", "pauseSim", "resumeSim"],
246
- html,
247
- css,
248
- script,
249
- };
250
- }
@@ -1,238 +0,0 @@
1
- // ============================================================================
2
- // DebugOverlay — 2D canvas overlay for chunk grid + velocity arrows
3
- // Generic rendering logic; game provides data via IDebugOverlayData interface.
4
- // ============================================================================
5
-
6
- import { calculateViewProj, CanvasResizeWatcher, type RenderSurface } from "@downdraft/engine";
7
- import type { IDebugOverlayData } from "./types";
8
-
9
- export class DebugOverlay {
10
- private canvas: HTMLCanvasElement;
11
- private overlay: HTMLCanvasElement;
12
- private ctx: CanvasRenderingContext2D;
13
- private showChunkGrid = false;
14
- private showVelocityArrows = false;
15
- private resizeWatcher: CanvasResizeWatcher;
16
-
17
- constructor(surface: RenderSurface) {
18
- // DOM-overlay widget — the canvas/element members it touches
19
- // (parentElement, 2d overlay canvas) are DOM-host concepts; on native
20
- // they degrade to the compat stubs (display:none anyway).
21
- const canvas = surface as unknown as HTMLCanvasElement;
22
- this.canvas = canvas;
23
- this.overlay = document.createElement("canvas");
24
- this.overlay.className = "debug-overlay";
25
- this.overlay.style.cssText = `
26
- position: absolute;
27
- top: 0; left: 0;
28
- width: 100%; height: 100%;
29
- pointer-events: none;
30
- z-index: 0;
31
- display: none;
32
- `;
33
- const parent = canvas.parentElement;
34
- if (parent) {
35
- parent.style.position = parent.style.position || "relative";
36
- parent.appendChild(this.overlay);
37
- } else {
38
- document.body.appendChild(this.overlay);
39
- }
40
- this.ctx = this.overlay.getContext("2d")!;
41
- this.resizeWatcher = new CanvasResizeWatcher(canvas, {
42
- onResize: (w, h) => {
43
- if (this.overlay.width !== w || this.overlay.height !== h) {
44
- this.overlay.width = w;
45
- this.overlay.height = h;
46
- }
47
- },
48
- });
49
- }
50
-
51
- setShowChunkGrid(show: boolean): void {
52
- this.showChunkGrid = show;
53
- this.updateDisplay();
54
- }
55
-
56
- setShowVelocityArrows(show: boolean): void {
57
- this.showVelocityArrows = show;
58
- this.updateDisplay();
59
- }
60
-
61
- isChunkGridVisible(): boolean {
62
- return this.showChunkGrid;
63
- }
64
-
65
- isVelocityArrowsVisible(): boolean {
66
- return this.showVelocityArrows;
67
- }
68
-
69
- private updateDisplay(): void {
70
- const active = this.showChunkGrid || this.showVelocityArrows;
71
- this.overlay.style.display = active ? "block" : "none";
72
- if (!active) {
73
- this.ctx.clearRect(0, 0, this.overlay.width, this.overlay.height);
74
- }
75
- }
76
-
77
- private projectToScreen(
78
- worldX: number, worldY: number, worldZ: number,
79
- viewProj: Float32Array,
80
- canvasW: number, canvasH: number,
81
- ): { x: number; y: number; behind: boolean } | null {
82
- const x = viewProj[0] * worldX + viewProj[4] * worldY + viewProj[8] * worldZ + viewProj[12];
83
- const y = viewProj[1] * worldX + viewProj[5] * worldY + viewProj[9] * worldZ + viewProj[13];
84
- const w = viewProj[3] * worldX + viewProj[7] * worldY + viewProj[11] * worldZ + viewProj[15];
85
-
86
- if (w <= 0) return { x: 0, y: 0, behind: true };
87
-
88
- const ndcX = x / w;
89
- const ndcY = y / w;
90
-
91
- const screenX = (ndcX + 1) * 0.5 * canvasW;
92
- const screenY = (1 - ndcY) * 0.5 * canvasH;
93
-
94
- return { x: screenX, y: screenY, behind: false };
95
- }
96
-
97
- update(
98
- camera: Parameters<typeof calculateViewProj>[0],
99
- data: IDebugOverlayData,
100
- ): void {
101
- if (!this.showChunkGrid && !this.showVelocityArrows) return;
102
-
103
- const ctx = this.ctx;
104
- const canvasW = this.overlay.width;
105
- const canvasH = this.overlay.height;
106
- ctx.clearRect(0, 0, canvasW, canvasH);
107
-
108
- const viewProj = calculateViewProj(camera);
109
-
110
- if (this.showChunkGrid) {
111
- this.renderChunkGrid(ctx, camera, viewProj, canvasW, canvasH, data);
112
- }
113
-
114
- if (this.showVelocityArrows) {
115
- this.renderVelocityArrows(ctx, viewProj, canvasW, canvasH, data);
116
- }
117
- }
118
-
119
- private renderChunkGrid(
120
- ctx: CanvasRenderingContext2D,
121
- camera: { position: [number, number, number] },
122
- viewProj: Float32Array,
123
- canvasW: number, canvasH: number,
124
- data: IDebugOverlayData,
125
- ): void {
126
- const playerPos = data.getPlayerPosition();
127
- if (!playerPos) return;
128
- const { chunkSize, chunksVisible } = data.getChunkGridConfig();
129
-
130
- const playerChunkX = Math.floor(playerPos.x / chunkSize);
131
- const playerChunkZ = Math.floor(playerPos.z / chunkSize);
132
- const half = Math.ceil(chunksVisible / 2);
133
-
134
- ctx.strokeStyle = "rgba(100, 200, 255, 0.35)";
135
- ctx.lineWidth = 1;
136
- ctx.font = "10px monospace";
137
- ctx.fillStyle = "rgba(100, 200, 255, 0.6)";
138
-
139
- for (let cx = playerChunkX - half; cx <= playerChunkX + half; cx++) {
140
- for (let cz = playerChunkZ - half; cz <= playerChunkZ + half; cz++) {
141
- const worldX = cx * chunkSize;
142
- const worldZ = cz * chunkSize;
143
-
144
- const corners = [
145
- this.projectToScreen(worldX, 0, worldZ, viewProj, canvasW, canvasH),
146
- this.projectToScreen(worldX + chunkSize, 0, worldZ, viewProj, canvasW, canvasH),
147
- this.projectToScreen(worldX + chunkSize, 0, worldZ + chunkSize, viewProj, canvasW, canvasH),
148
- this.projectToScreen(worldX, 0, worldZ + chunkSize, viewProj, canvasW, canvasH),
149
- ];
150
-
151
- if (corners.some(c => c === null || c.behind)) continue;
152
-
153
- ctx.beginPath();
154
- ctx.moveTo(corners[0]!.x, corners[0]!.y);
155
- ctx.lineTo(corners[1]!.x, corners[1]!.y);
156
- ctx.lineTo(corners[2]!.x, corners[2]!.y);
157
- ctx.lineTo(corners[3]!.x, corners[3]!.y);
158
- ctx.closePath();
159
- ctx.stroke();
160
-
161
- const centerProj = this.projectToScreen(
162
- worldX + chunkSize / 2, 0, worldZ + chunkSize / 2,
163
- viewProj, canvasW, canvasH,
164
- );
165
- if (centerProj && !centerProj.behind) {
166
- const dx = (worldX + chunkSize / 2) - camera.position[0];
167
- const dz = (worldZ + chunkSize / 2) - camera.position[2];
168
- const dist = Math.sqrt(dx * dx + dz * dz);
169
- if (dist < chunkSize * half) {
170
- ctx.fillText(`${cx},${cz}`, centerProj.x - 15, centerProj.y);
171
- }
172
- }
173
- }
174
- }
175
- }
176
-
177
- private renderVelocityArrows(
178
- ctx: CanvasRenderingContext2D,
179
- viewProj: Float32Array,
180
- canvasW: number, canvasH: number,
181
- data: IDebugOverlayData,
182
- ): void {
183
- const entityCount = data.getEntityCount();
184
-
185
- ctx.strokeStyle = "rgba(255, 200, 50, 0.8)";
186
- ctx.fillStyle = "rgba(255, 200, 50, 0.8)";
187
- ctx.lineWidth = 2;
188
-
189
- for (let i = 0; i < entityCount; i++) {
190
- const pos = data.getEntityPosition(i);
191
- const vel = data.getEntityVelocity(i);
192
- if (!pos || !vel) continue;
193
-
194
- const speed = Math.sqrt(vel.x * vel.x + vel.y * vel.y + vel.z * vel.z);
195
- if (speed < 0.1) continue;
196
-
197
- const scale = 2.0;
198
- const endX = pos.x + vel.x * scale;
199
- const endY = pos.y + vel.y * scale;
200
- const endZ = pos.z + vel.z * scale;
201
-
202
- const startProj = this.projectToScreen(pos.x, pos.y, pos.z, viewProj, canvasW, canvasH);
203
- const endProj = this.projectToScreen(endX, endY, endZ, viewProj, canvasW, canvasH);
204
-
205
- if (!startProj || startProj.behind || !endProj || endProj.behind) continue;
206
-
207
- ctx.beginPath();
208
- ctx.moveTo(startProj.x, startProj.y);
209
- ctx.lineTo(endProj.x, endProj.y);
210
- ctx.stroke();
211
-
212
- const dx = endProj.x - startProj.x;
213
- const dy = endProj.y - startProj.y;
214
- const angle = Math.atan2(dy, dx);
215
- const headLen = 6;
216
- ctx.beginPath();
217
- ctx.moveTo(endProj.x, endProj.y);
218
- ctx.lineTo(
219
- endProj.x - headLen * Math.cos(angle - Math.PI / 6),
220
- endProj.y - headLen * Math.sin(angle - Math.PI / 6),
221
- );
222
- ctx.lineTo(
223
- endProj.x - headLen * Math.cos(angle + Math.PI / 6),
224
- endProj.y - headLen * Math.sin(angle + Math.PI / 6),
225
- );
226
- ctx.closePath();
227
- ctx.fill();
228
-
229
- ctx.font = "10px monospace";
230
- ctx.fillText(`${speed.toFixed(1)} m/s`, endProj.x + 4, endProj.y - 4);
231
- }
232
- }
233
-
234
- destroy(): void {
235
- this.resizeWatcher.destroy();
236
- this.overlay.remove();
237
- }
238
- }