@woosh/meep-engine 3.31.0 → 3.33.0

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 (117) hide show
  1. package/build/bundle-worker-terrain.js +1 -1
  2. package/package.json +1 -1
  3. package/src/core/collection/table/RowFirstTable.d.ts +8 -0
  4. package/src/core/collection/table/RowFirstTable.d.ts.map +1 -1
  5. package/src/core/collection/table/RowFirstTable.js +40 -11
  6. package/src/engine/graphics/ecs/sprite/Sprite.d.ts +3 -0
  7. package/src/engine/graphics/ecs/sprite/Sprite.d.ts.map +1 -1
  8. package/src/engine/graphics/ecs/sprite/Sprite.js +3 -0
  9. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts +3 -0
  10. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.d.ts.map +1 -1
  11. package/src/engine/graphics/ecs/sprite/SpriteSystemPE.js +3 -0
  12. package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
  13. package/src/engine/graphics3/DebugDrawSystem.js +6 -2
  14. package/src/engine/graphics3/PathDisplaySystem.d.ts.map +1 -1
  15. package/src/engine/graphics3/PathDisplaySystem.js +311 -307
  16. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  17. package/src/shade/RENDERER_CONTRACT.md +15 -2
  18. package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +5 -0
  19. package/src/shade/device/ShadeGPUCommandContext.d.ts +15 -2
  20. package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
  21. package/src/shade/device/ShadeGPUCommandContext.js +154 -25
  22. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.d.ts +20 -3
  23. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.d.ts.map +1 -1
  24. package/src/shade/device/mock/SoftwareGPURenderPassEncoder.js +21 -3
  25. package/src/shade/renderer/GraphicsContext.d.ts +20 -1
  26. package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
  27. package/src/shade/renderer/GraphicsContext.js +105 -52
  28. package/src/shade/renderer/Renderer.d.ts +19 -4
  29. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  30. package/src/shade/renderer/Renderer.js +113 -2
  31. package/src/shade/renderer/animation/GPUAnimationManager.d.ts +10 -7
  32. package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
  33. package/src/shade/renderer/animation/GPUAnimationManager.js +147 -45
  34. package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.d.ts +6 -5
  35. package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.d.ts.map +1 -1
  36. package/src/shade/renderer/buffer/GPUBufferReadbackAllocator.js +241 -240
  37. package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts +62 -2
  38. package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts.map +1 -1
  39. package/src/shade/renderer/buffer/GPUBufferStagingAllocator.js +306 -38
  40. package/src/shade/renderer/buffer/GPUBufferUploadAllocator.d.ts +60 -0
  41. package/src/shade/renderer/buffer/GPUBufferUploadAllocator.d.ts.map +1 -0
  42. package/src/shade/renderer/buffer/GPUBufferUploadAllocator.js +202 -0
  43. package/src/shade/renderer/buffer/GPUUploadBlock.d.ts +68 -0
  44. package/src/shade/renderer/buffer/GPUUploadBlock.d.ts.map +1 -0
  45. package/src/shade/renderer/buffer/GPUUploadBlock.js +97 -0
  46. package/src/shade/renderer/dynamic/DynamicMeshAVBOITChannel.d.ts +2 -1
  47. package/src/shade/renderer/dynamic/DynamicMeshAVBOITChannel.d.ts.map +1 -1
  48. package/src/shade/renderer/dynamic/DynamicMeshAVBOITChannel.js +19 -17
  49. package/src/shade/renderer/dynamic/GPUDynamicMeshDrawList.d.ts +70 -0
  50. package/src/shade/renderer/dynamic/GPUDynamicMeshDrawList.d.ts.map +1 -0
  51. package/src/shade/renderer/dynamic/GPUDynamicMeshDrawList.js +257 -0
  52. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts +20 -7
  53. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts.map +1 -1
  54. package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +39 -41
  55. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.d.ts +6 -20
  56. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.d.ts.map +1 -1
  57. package/src/shade/renderer/dynamic/GPUDynamicMeshResidency.js +42 -36
  58. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.d.ts +2 -1
  59. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.d.ts.map +1 -1
  60. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_avboit_vertex.js +4 -3
  61. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_vertex.d.ts +4 -3
  62. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_vertex.d.ts.map +1 -1
  63. package/src/shade/renderer/dynamic/chunk_dynamic_mesh_vertex.js +7 -4
  64. package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.d.ts +14 -15
  65. package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.d.ts.map +1 -1
  66. package/src/shade/renderer/dynamic/graph_dynamic_mesh_avboit.js +49 -83
  67. package/src/shade/renderer/dynamic/shader_dynamic_mesh.d.ts +19 -3
  68. package/src/shade/renderer/dynamic/shader_dynamic_mesh.d.ts.map +1 -1
  69. package/src/shade/renderer/dynamic/shader_dynamic_mesh.js +26 -6
  70. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts +2 -1
  71. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.d.ts.map +1 -1
  72. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_draw.js +12 -6
  73. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.d.ts +3 -2
  74. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.d.ts.map +1 -1
  75. package/src/shade/renderer/dynamic/shader_dynamic_mesh_avboit_splat.js +21 -13
  76. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts +5 -0
  77. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
  78. package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +19 -0
  79. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  80. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  81. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +3 -1
  82. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -1
  83. package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +3 -1
  84. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.d.ts.map +1 -1
  85. package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js +134 -88
  86. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
  87. package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +57 -26
  88. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
  89. package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +28 -20
  90. package/src/shade/renderer/rasterize/graph_clear_raster_targets.d.ts +38 -0
  91. package/src/shade/renderer/rasterize/graph_clear_raster_targets.d.ts.map +1 -0
  92. package/src/shade/renderer/rasterize/graph_clear_raster_targets.js +105 -0
  93. package/src/shade/renderer/rasterize/native/avboit/AVBOIT_LEDGER.md +20 -0
  94. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts +1 -1
  95. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
  96. package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +21 -1
  97. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
  98. package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +12 -20
  99. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts +2 -1
  100. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts.map +1 -1
  101. package/src/shade/renderer/shader/graph/graph_import_typed_buffer.js +15 -28
  102. package/src/shade/renderer/shader/type/serde/write_gpu_typed_buffer.d.ts.map +1 -1
  103. package/src/shade/renderer/shader/type/serde/write_gpu_typed_buffer.js +8 -1
  104. package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts +0 -73
  105. package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts.map +0 -1
  106. package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.d.ts +0 -59
  107. package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.d.ts.map +0 -1
  108. package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts +0 -22
  109. package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts.map +0 -1
  110. package/src/engine/graphics3/terrain/shader_terrain_splat.d.ts +0 -26
  111. package/src/engine/graphics3/terrain/shader_terrain_splat.d.ts.map +0 -1
  112. package/src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts +0 -25
  113. package/src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts.map +0 -1
  114. package/src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts +0 -26
  115. package/src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts.map +0 -1
  116. package/src/shade/renderer/animation/ANIMATION_SKIN_JOINT_BLOCK_STRUCT.d.ts +0 -29
  117. package/src/shade/renderer/animation/ANIMATION_SKIN_JOINT_BLOCK_STRUCT.d.ts.map +0 -1
@@ -1,307 +1,311 @@
1
- import { assert } from "../../core/assert.js";
2
- import { GPUDynamicMeshRenderer } from "../../shade/renderer/dynamic/GPUDynamicMeshRenderer.js";
3
- import { Geometry } from "../../shade/renderer/geometry/Geometry.js";
4
- import { FramePhase } from "../../shade/renderer/extension/FramePhase.js";
5
- import { RenderExtension } from "../../shade/renderer/extension/RenderExtension.js";
6
- import { SceneColor } from "../../shade/renderer/extension/SceneColor.js";
7
- import { ViewTextures } from "../../shade/renderer/extension/ViewTextures.js";
8
- import { DynamicMesh } from "../../shade/renderer/scene/DynamicMesh.js";
9
- import { DynamicMeshBatch } from "../../shade/renderer/scene/DynamicMeshBatch.js";
10
- import { System } from "../ecs/System.js";
11
- import { PathDisplay } from "../graphics/ecs/path/PathDisplay.js";
12
- import { PathDisplayEvents } from "../graphics/ecs/path/PathDisplayEvents.js";
13
- import { PathDisplayType } from "../graphics/ecs/path/PathDisplayType.js";
14
- import Path from "../navigation/ecs/components/Path.js";
15
- import { PathEvents } from "../navigation/ecs/components/PathEvents.js";
16
- import { build_tube_path_streams } from "./path/build_tube_path_streams.js";
17
- import { tube_stream_write_geometry } from "./path/tube_stream_write_geometry.js";
18
-
19
- /**
20
- * Draws {@link PathDisplay}'s tubes under Shade, on the dynamic-geometry path.
21
- *
22
- * **A tube is dynamic content by definition** — the game's one path display is the combat targeting
23
- * arc, rebuilt every time the cursor moves — so it goes down S4's dedicated non-meshlet path rather
24
- * than becoming a `ShadedGeometry` behind the meshlet encode that D5/I-10 rule out (D21).
25
- *
26
- * **No owned entities.** The legacy system spawns a child entity per mask span because three needs
27
- * an object-carrying entity per drawable; here the system holds the meshes itself, so link builds
28
- * them, a path or style change rewrites them, and unlink releases them.
29
- *
30
- * **The geometry is kept and rewritten, never replaced.** GPU residency on this path is keyed on
31
- * the geometry and survives leaving the batch, so a rebuild that produced a new one each time would
32
- * strand a vertex buffer per cursor move.
33
- *
34
- * Tubes are all it draws. `PathDisplayType.Ribbon` retires with the three path (I-6c) and
35
- * `PathDisplayType.Entity` builds marker entities that carry their own components — neither is
36
- * this system's to render.
37
- */
38
- export class PathDisplaySystem extends System {
39
- dependencies = [PathDisplay, Path];
40
-
41
- /**
42
- * @type {GraphicsEngine}
43
- */
44
- #graphics;
45
-
46
- /**
47
- * @type {GPUDynamicMeshRenderer}
48
- */
49
- #renderer = new GPUDynamicMeshRenderer();
50
-
51
- /**
52
- * @type {DynamicMeshBatch}
53
- */
54
- #batch = new DynamicMeshBatch();
55
-
56
- /**
57
- * @type {Map<number, {display: PathDisplay, path: Path, meshes: DynamicMesh[], listener: Function}>}
58
- */
59
- #entries = new Map();
60
-
61
- /**
62
- * Entities whose tubes are out of date. A path change and a style change both arrive as events,
63
- * and the arc sends one of each per cursor move — coalescing to one rebuild a tick is what the
64
- * queue is for.
65
- *
66
- * @type {Set<number>}
67
- */
68
- #dirty = new Set();
69
-
70
- /**
71
- * @type {RenderExtension|null}
72
- */
73
- #extension = null;
74
-
75
- /**
76
- * What the pass draws. Read-only — the meshes follow the entities.
77
- *
78
- * @returns {DynamicMeshBatch}
79
- */
80
- get batch() {
81
- return this.#batch;
82
- }
83
-
84
- /**
85
- * @param {GraphicsEngine} graphics
86
- */
87
- constructor(graphics) {
88
- super();
89
-
90
- assert.defined(graphics, 'graphics');
91
-
92
- this.#graphics = graphics;
93
- }
94
-
95
- async startup(entityManager) {
96
- this.entityManager = entityManager;
97
-
98
- this.#extension = this.#graphics.add_extension(new PathDisplayExtension(this));
99
- }
100
-
101
- async shutdown(entityManager) {
102
- if (this.#extension !== null) {
103
- this.#graphics.remove_extension(this.#extension);
104
-
105
- this.#extension = null;
106
- }
107
-
108
- this.#renderer.destroy();
109
-
110
- this.#batch.clear();
111
- this.#entries.clear();
112
- this.#dirty.clear();
113
- }
114
-
115
- /**
116
- * @param {PathDisplay} display
117
- * @param {Path} path
118
- * @param {number} entity
119
- */
120
- link(display, path, entity) {
121
- const dataset = this.entityManager.dataset;
122
-
123
- const listener = () => this.#dirty.add(entity);
124
-
125
- dataset.addEntityEventListener(entity, PathEvents.Changed, listener);
126
- dataset.addEntityEventListener(entity, PathDisplayEvents.Changed, listener);
127
-
128
- const entry = { display, path, meshes: [], listener };
129
-
130
- this.#entries.set(entity, entry);
131
-
132
- this.#rebuild(entry);
133
- }
134
-
135
- /**
136
- * @param {PathDisplay} display
137
- * @param {Path} path
138
- * @param {number} entity
139
- */
140
- unlink(display, path, entity) {
141
- const entry = this.#entries.get(entity);
142
-
143
- if (entry === undefined) {
144
- return;
145
- }
146
-
147
- const dataset = this.entityManager.dataset;
148
-
149
- dataset.removeEntityEventListener(entity, PathEvents.Changed, entry.listener);
150
- dataset.removeEntityEventListener(entity, PathDisplayEvents.Changed, entry.listener);
151
-
152
- this.#release(entry, 0);
153
-
154
- this.#entries.delete(entity);
155
- this.#dirty.delete(entity);
156
- }
157
-
158
- update(timeDelta) {
159
- if (this.#dirty.size === 0) {
160
- return;
161
- }
162
-
163
- for (const entity of this.#dirty) {
164
- const entry = this.#entries.get(entity);
165
-
166
- if (entry === undefined) {
167
- continue;
168
- }
169
-
170
- this.#rebuild(entry);
171
- }
172
-
173
- this.#dirty.clear();
174
- }
175
-
176
- /**
177
- * @param {{display: PathDisplay, path: Path, meshes: DynamicMesh[]}} entry
178
- */
179
- #rebuild(entry) {
180
- const specs = entry.display.specs;
181
-
182
- let written = 0;
183
-
184
- for (let i = 0; i < specs.length; i++) {
185
- const spec = specs[i];
186
-
187
- if (spec.type !== PathDisplayType.Tube) {
188
- continue;
189
- }
190
-
191
- const style = spec.style;
192
-
193
- const streams = build_tube_path_streams(entry.path, style);
194
-
195
- const color = style.color;
196
-
197
- for (let j = 0; j < streams.length; j++) {
198
- const mesh = this.#obtain_mesh(entry, written);
199
-
200
- tube_stream_write_geometry(mesh.geometry, streams[j]);
201
-
202
- mesh.color[0] = color.r;
203
- mesh.color[1] = color.g;
204
- mesh.color[2] = color.b;
205
- mesh.color[3] = style.opacity;
206
-
207
- written++;
208
- }
209
- }
210
-
211
- this.#release(entry, written);
212
- }
213
-
214
- /**
215
- * The mesh drawing span `index` of this entity, created on first use.
216
- *
217
- * @param {{meshes: DynamicMesh[]}} entry
218
- * @param {number} index
219
- * @returns {DynamicMesh}
220
- */
221
- #obtain_mesh(entry, index) {
222
- const existing = entry.meshes[index];
223
-
224
- if (existing !== undefined) {
225
- return existing;
226
- }
227
-
228
- // the sweep produces world-space vertices, and what the draw applies is the mesh's own
229
- // transform, identity until moved
230
- const mesh = DynamicMesh.from(new Geometry());
231
-
232
- entry.meshes.push(mesh);
233
-
234
- this.#batch.add(mesh);
235
-
236
- return mesh;
237
- }
238
-
239
- /**
240
- * Give up every mesh from `count` on — a style whose mask lost a span, or an entity going away.
241
- *
242
- * @param {{meshes: DynamicMesh[]}} entry
243
- * @param {number} count
244
- */
245
- #release(entry, count) {
246
- const meshes = entry.meshes;
247
-
248
- for (let i = count; i < meshes.length; i++) {
249
- const mesh = meshes[i];
250
-
251
- this.#batch.remove(mesh);
252
-
253
- // residency deliberately survives leaving the batch, so nothing else would free it
254
- this.#renderer.release(mesh.geometry);
255
- }
256
-
257
- meshes.length = count;
258
- }
259
-
260
- /**
261
- * @param {FrameContext} frame
262
- */
263
- record(frame) {
264
- if (this.#batch.count === 0) {
265
- // nothing to draw, and the frame's colour is left exactly as it was
266
- return;
267
- }
268
-
269
- const scene = frame.get(SceneColor);
270
-
271
- scene.color = this.#renderer.graph_draw({
272
- graph: frame.graph,
273
- batch: this.#batch,
274
- color: scene.color,
275
- depth: frame.get(ViewTextures).depth,
276
- camera: frame.view.camera.buffer
277
- });
278
- }
279
- }
280
-
281
- /**
282
- * The targeting arc draws into the scene, so it belongs before the fog of war that conceals it and
283
- * before the upscale that resolves it.
284
- */
285
- export class PathDisplayExtension extends RenderExtension {
286
- name = "path display";
287
-
288
- phase = FramePhase.AfterTransparency;
289
-
290
- /**
291
- * @type {PathDisplaySystem}
292
- */
293
- #system;
294
-
295
- /**
296
- * @param {PathDisplaySystem} system
297
- */
298
- constructor(system) {
299
- super();
300
-
301
- this.#system = system;
302
- }
303
-
304
- record(frame) {
305
- this.#system.record(frame);
306
- }
307
- }
1
+ import { assert } from "../../core/assert.js";
2
+ import { GPUDynamicMeshRenderer } from "../../shade/renderer/dynamic/GPUDynamicMeshRenderer.js";
3
+ import { Geometry } from "../../shade/renderer/geometry/Geometry.js";
4
+ import { FramePhase } from "../../shade/renderer/extension/FramePhase.js";
5
+ import { RenderExtension } from "../../shade/renderer/extension/RenderExtension.js";
6
+ import { SceneColor } from "../../shade/renderer/extension/SceneColor.js";
7
+ import { ViewTextures } from "../../shade/renderer/extension/ViewTextures.js";
8
+ import { DynamicMesh } from "../../shade/renderer/scene/DynamicMesh.js";
9
+ import { DynamicMeshBatch } from "../../shade/renderer/scene/DynamicMeshBatch.js";
10
+ import { System } from "../ecs/System.js";
11
+ import { PathDisplay } from "../graphics/ecs/path/PathDisplay.js";
12
+ import { PathDisplayEvents } from "../graphics/ecs/path/PathDisplayEvents.js";
13
+ import { PathDisplayType } from "../graphics/ecs/path/PathDisplayType.js";
14
+ import Path from "../navigation/ecs/components/Path.js";
15
+ import { PathEvents } from "../navigation/ecs/components/PathEvents.js";
16
+ import { build_tube_path_streams } from "./path/build_tube_path_streams.js";
17
+ import { tube_stream_write_geometry } from "./path/tube_stream_write_geometry.js";
18
+
19
+ /**
20
+ * Draws {@link PathDisplay}'s tubes under Shade, on the dynamic-geometry path.
21
+ *
22
+ * **A tube is dynamic content by definition** — the game's one path display is the combat targeting
23
+ * arc, rebuilt every time the cursor moves — so it goes down S4's dedicated non-meshlet path rather
24
+ * than becoming a `ShadedGeometry` behind the meshlet encode that D5/I-10 rule out (D21).
25
+ *
26
+ * **No owned entities.** The legacy system spawns a child entity per mask span because three needs
27
+ * an object-carrying entity per drawable; here the system holds the meshes itself, so link builds
28
+ * them, a path or style change rewrites them, and unlink releases them.
29
+ *
30
+ * **The geometry is kept and rewritten, never replaced.** GPU residency on this path is keyed on
31
+ * the geometry and survives leaving the batch, so a rebuild that produced a new one each time would
32
+ * strand a vertex buffer per cursor move.
33
+ *
34
+ * Tubes are all it draws. `PathDisplayType.Ribbon` retires with the three path (I-6c) and
35
+ * `PathDisplayType.Entity` builds marker entities that carry their own components — neither is
36
+ * this system's to render.
37
+ */
38
+ export class PathDisplaySystem extends System {
39
+ dependencies = [PathDisplay, Path];
40
+
41
+ /**
42
+ * @type {GraphicsEngine}
43
+ */
44
+ #graphics;
45
+
46
+ /**
47
+ * @type {GPUDynamicMeshRenderer}
48
+ */
49
+ #renderer = new GPUDynamicMeshRenderer();
50
+
51
+ /**
52
+ * @type {DynamicMeshBatch}
53
+ */
54
+ #batch = new DynamicMeshBatch();
55
+
56
+ /**
57
+ * @type {Map<number, {display: PathDisplay, path: Path, meshes: DynamicMesh[], listener: Function}>}
58
+ */
59
+ #entries = new Map();
60
+
61
+ /**
62
+ * Entities whose tubes are out of date. A path change and a style change both arrive as events,
63
+ * and the arc sends one of each per cursor move — coalescing to one rebuild a tick is what the
64
+ * queue is for.
65
+ *
66
+ * @type {Set<number>}
67
+ */
68
+ #dirty = new Set();
69
+
70
+ /**
71
+ * @type {RenderExtension|null}
72
+ */
73
+ #extension = null;
74
+
75
+ /**
76
+ * What the pass draws. Read-only — the meshes follow the entities.
77
+ *
78
+ * @returns {DynamicMeshBatch}
79
+ */
80
+ get batch() {
81
+ return this.#batch;
82
+ }
83
+
84
+ /**
85
+ * @param {GraphicsEngine} graphics
86
+ */
87
+ constructor(graphics) {
88
+ super();
89
+
90
+ assert.defined(graphics, 'graphics');
91
+
92
+ this.#graphics = graphics;
93
+ }
94
+
95
+ async startup(entityManager) {
96
+ this.entityManager = entityManager;
97
+
98
+ this.#extension = this.#graphics.add_extension(new PathDisplayExtension(this));
99
+ }
100
+
101
+ async shutdown(entityManager) {
102
+ if (this.#extension !== null) {
103
+ this.#graphics.remove_extension(this.#extension);
104
+
105
+ this.#extension = null;
106
+ }
107
+
108
+ this.#renderer.destroy();
109
+
110
+ this.#batch.clear();
111
+ this.#entries.clear();
112
+ this.#dirty.clear();
113
+ }
114
+
115
+ /**
116
+ * @param {PathDisplay} display
117
+ * @param {Path} path
118
+ * @param {number} entity
119
+ */
120
+ link(display, path, entity) {
121
+ const dataset = this.entityManager.dataset;
122
+
123
+ const listener = () => this.#dirty.add(entity);
124
+
125
+ dataset.addEntityEventListener(entity, PathEvents.Changed, listener);
126
+ dataset.addEntityEventListener(entity, PathDisplayEvents.Changed, listener);
127
+
128
+ const entry = { display, path, meshes: [], listener };
129
+
130
+ this.#entries.set(entity, entry);
131
+
132
+ this.#rebuild(entry);
133
+ }
134
+
135
+ /**
136
+ * @param {PathDisplay} display
137
+ * @param {Path} path
138
+ * @param {number} entity
139
+ */
140
+ unlink(display, path, entity) {
141
+ const entry = this.#entries.get(entity);
142
+
143
+ if (entry === undefined) {
144
+ return;
145
+ }
146
+
147
+ const dataset = this.entityManager.dataset;
148
+
149
+ dataset.removeEntityEventListener(entity, PathEvents.Changed, entry.listener);
150
+ dataset.removeEntityEventListener(entity, PathDisplayEvents.Changed, entry.listener);
151
+
152
+ this.#release(entry, 0);
153
+
154
+ this.#entries.delete(entity);
155
+ this.#dirty.delete(entity);
156
+ }
157
+
158
+ update(timeDelta) {
159
+ if (this.#dirty.size === 0) {
160
+ return;
161
+ }
162
+
163
+ for (const entity of this.#dirty) {
164
+ const entry = this.#entries.get(entity);
165
+
166
+ if (entry === undefined) {
167
+ continue;
168
+ }
169
+
170
+ this.#rebuild(entry);
171
+ }
172
+
173
+ this.#dirty.clear();
174
+ }
175
+
176
+ /**
177
+ * @param {{display: PathDisplay, path: Path, meshes: DynamicMesh[]}} entry
178
+ */
179
+ #rebuild(entry) {
180
+ const specs = entry.display.specs;
181
+
182
+ let written = 0;
183
+
184
+ for (let i = 0; i < specs.length; i++) {
185
+ const spec = specs[i];
186
+
187
+ if (spec.type !== PathDisplayType.Tube) {
188
+ continue;
189
+ }
190
+
191
+ const style = spec.style;
192
+
193
+ const streams = build_tube_path_streams(entry.path, style);
194
+
195
+ const color = style.color;
196
+
197
+ for (let j = 0; j < streams.length; j++) {
198
+ const mesh = this.#obtain_mesh(entry, written);
199
+
200
+ tube_stream_write_geometry(mesh.geometry, streams[j]);
201
+
202
+ mesh.color[0] = color.r;
203
+ mesh.color[1] = color.g;
204
+ mesh.color[2] = color.b;
205
+ mesh.color[3] = style.opacity;
206
+
207
+ written++;
208
+ }
209
+ }
210
+
211
+ this.#release(entry, written);
212
+ }
213
+
214
+ /**
215
+ * The mesh drawing span `index` of this entity, created on first use.
216
+ *
217
+ * @param {{meshes: DynamicMesh[]}} entry
218
+ * @param {number} index
219
+ * @returns {DynamicMesh}
220
+ */
221
+ #obtain_mesh(entry, index) {
222
+ const existing = entry.meshes[index];
223
+
224
+ if (existing !== undefined) {
225
+ return existing;
226
+ }
227
+
228
+ // the sweep produces world-space vertices, and what the draw applies is the mesh's own
229
+ // transform, identity until moved
230
+ const mesh = DynamicMesh.from(new Geometry());
231
+
232
+ entry.meshes.push(mesh);
233
+
234
+ this.#batch.add(mesh);
235
+
236
+ return mesh;
237
+ }
238
+
239
+ /**
240
+ * Give up every mesh from `count` on — a style whose mask lost a span, or an entity going away.
241
+ *
242
+ * @param {{meshes: DynamicMesh[]}} entry
243
+ * @param {number} count
244
+ */
245
+ #release(entry, count) {
246
+ const meshes = entry.meshes;
247
+
248
+ for (let i = count; i < meshes.length; i++) {
249
+ const mesh = meshes[i];
250
+
251
+ this.#batch.remove(mesh);
252
+
253
+ // residency deliberately survives leaving the batch, so nothing else would free it
254
+ this.#renderer.release(mesh.geometry);
255
+ }
256
+
257
+ meshes.length = count;
258
+ }
259
+
260
+ /**
261
+ * @param {FrameContext} frame
262
+ */
263
+ record(frame) {
264
+ if (this.#batch.count === 0) {
265
+ // nothing to draw, and the frame's colour is left exactly as it was
266
+ return;
267
+ }
268
+
269
+ const scene = frame.get(SceneColor);
270
+ const view = frame.get(ViewTextures);
271
+
272
+ const drawn = this.#renderer.graph_draw({
273
+ graph: frame.graph,
274
+ batch: this.#batch,
275
+ color: scene.color,
276
+ depth: view.depth,
277
+ camera: frame.view.camera.buffer
278
+ });
279
+
280
+ scene.color = drawn.color;
281
+ view.depth = drawn.depth;
282
+ }
283
+ }
284
+
285
+ /**
286
+ * The targeting arc draws into the scene, so it belongs before the fog of war that conceals it and
287
+ * before the upscale that resolves it.
288
+ */
289
+ export class PathDisplayExtension extends RenderExtension {
290
+ name = "path display";
291
+
292
+ phase = FramePhase.AfterTransparency;
293
+
294
+ /**
295
+ * @type {PathDisplaySystem}
296
+ */
297
+ #system;
298
+
299
+ /**
300
+ * @param {PathDisplaySystem} system
301
+ */
302
+ constructor(system) {
303
+ super();
304
+
305
+ this.#system = system;
306
+ }
307
+
308
+ record(frame) {
309
+ this.#system.record(frame);
310
+ }
311
+ }
@@ -64,11 +64,11 @@ export class FluidObstacleSystem extends System<any> {
64
64
  /**
65
65
  * @param {FluidComponent} fluid
66
66
  */
67
- static "__#415@#refresh_masks"(fluid: FluidComponent): void;
67
+ static "__#418@#refresh_masks"(fluid: FluidComponent): void;
68
68
  /**
69
69
  * @param {FluidComponent} fluid
70
70
  */
71
- static "__#415@#clear_field"(fluid: FluidComponent): void;
71
+ static "__#418@#clear_field"(fluid: FluidComponent): void;
72
72
  /**
73
73
  * Mark every cell of `fluid` whose centre lies within `inflation` of the
74
74
  * posed shape as solid. Iteration is clipped to the shape's world AABB
@@ -80,7 +80,7 @@ export class FluidObstacleSystem extends System<any> {
80
80
  * @param {number} inflation world-units SDF threshold
81
81
  * @param {Float64Array} point length-3 scratch
82
82
  */
83
- static "__#415@#voxelize"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array): void;
83
+ static "__#418@#voxelize"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array): void;
84
84
  /**
85
85
  * Write the obstacle's translation velocity onto every face of every cell
86
86
  * it voxelized — the moving-wall boundary condition. Runs AFTER the mask
@@ -100,7 +100,7 @@ export class FluidObstacleSystem extends System<any> {
100
100
  * @param {number} wvy
101
101
  * @param {number} wvz
102
102
  */
103
- static "__#415@#stamp_wall_velocity"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array, wvx: number, wvy: number, wvz: number): void;
103
+ static "__#418@#stamp_wall_velocity"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array, wvx: number, wvy: number, wvz: number): void;
104
104
  constructor();
105
105
  dependencies: (typeof FluidObstacle)[];
106
106
  components_used: (ResourceAccessSpecification<typeof Transform64> | ResourceAccessSpecification<typeof RigidBody> | ResourceAccessSpecification<typeof Collider> | ResourceAccessSpecification<typeof FluidComponent> | ResourceAccessSpecification<typeof FluidObstacle>)[];