@woosh/meep-engine 3.13.0 → 3.13.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.
package/package.json CHANGED
@@ -6,7 +6,7 @@
6
6
  "description": "Pure JavaScript game engine. Fully featured and production ready.",
7
7
  "type": "module",
8
8
  "author": "Alexander Goldring",
9
- "version": "3.13.0",
9
+ "version": "3.13.1",
10
10
  "exports": {
11
11
  "./build/*": "./build/*",
12
12
  "./src/*": "./src/*",
@@ -1 +1 @@
1
- {"version":3,"file":"ConvexHullShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/ConvexHullShape3D.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH;IA8CI;;;;;;;OAOG;IACH,sBAJW,YAAY,WACZ,WAAW,GACT,iBAAiB,CAQ7B;IA1DD;;;;OAIG;IACH,UAFU,YAAY,CAES;IAE/B;;;;OAIG;IACH,SAFU,WAAW,CAEQ;IAE7B;;;;;;OAMG;IACH,cAFU,WAAW,CAEa;IAElC,0BAA0B;IAC1B,YADW,WAAW,CACU;IAEhC;;;;;;OAMG;IACH,iBAA+B;IAE/B,oCAAoC;IACpC,eAA6B;IAE7B,8BAA8B;IAC9B,iBAAa;IAEb,8BAA8B;IAC9B,uBAAmB;IAkBnB;;;OAGG;IACH,kBA8EC;IAKD,wCAIC;IAED;;;;;OAKG;IACH,qGAwCC;IAED;;;;;;;;OAQG;IACH,yCAEC;IAED;;;;OAIG;IACH,wCAEC;IAED,qCAkBC;IAED;;;OAGG;IACH,oCAgBC;IAED;;;OAGG;IACH,6CAiBC;IAED,mEAEC;IAED,4DAoBC;IAGD;;;OAGG;IACH,cAHW,iBAAiB,GACf,OAAO,CAanB;IAcL;;;;OAIG;IACH,8BAFU,OAAO,CAE8B;CAP9C;gCAvX+B,sBAAsB"}
1
+ {"version":3,"file":"ConvexHullShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/ConvexHullShape3D.js"],"names":[],"mappings":"AAQA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH;IA8CI;;;;;;;OAOG;IACH,sBAJW,YAAY,WACZ,WAAW,GACT,iBAAiB,CAQ7B;IA1DD;;;;OAIG;IACH,UAFU,YAAY,CAES;IAE/B;;;;OAIG;IACH,SAFU,WAAW,CAEQ;IAE7B;;;;;;OAMG;IACH,cAFU,WAAW,CAEa;IAElC,0BAA0B;IAC1B,YADW,WAAW,CACU;IAEhC;;;;;;OAMG;IACH,iBAA+B;IAE/B,oCAAoC;IACpC,eAA6B;IAE7B,8BAA8B;IAC9B,iBAAa;IAEb,8BAA8B;IAC9B,uBAAmB;IAkBnB;;;OAGG;IACH,kBAkFC;IAUD,wCAQC;IAED;;;;;OAKG;IACH,qGAwCC;IAED;;;;;;;;OAQG;IACH,yCAEC;IAED;;;;OAIG;IACH,wCAEC;IAED,qCAkBC;IAED;;;OAGG;IACH,oCAgBC;IAED;;;OAGG;IACH,6CAiBC;IAED,mEAEC;IAED,4DAwBC;IAGD;;;OAGG;IACH,cAHW,iBAAiB,GACf,OAAO,CAanB;IAcL;;;;OAIG;IACH,8BAFU,OAAO,CAE8B;CAP9C;gCAtY+B,sBAAsB"}
@@ -1,10 +1,10 @@
1
1
  import { hash_mix2 } from "../../../math/hash/hash_mix2.js";
2
2
  import { computeHashFloat } from "../../../primitives/numbers/computeHashFloat.js";
3
+ import { aabb3_from_v3_array } from "../aabb/aabb3_from_v3_array.js";
4
+ import { convex_polyhedron3_raycast } from "../polyhedron/convex_polyhedron3_raycast.js";
3
5
  import { tri3_closest_point } from "../triangle/tri3_closest_point.js";
4
6
  import { AbstractShape3D } from "./AbstractShape3D.js";
5
7
  import { compute_signed_distance_gradient_by_sampling } from "./util/compute_signed_distance_gradient_by_sampling.js";
6
- import { aabb3_from_v3_array } from "../aabb/aabb3_from_v3_array.js";
7
- import { convex_polyhedron3_raycast } from "../polyhedron/convex_polyhedron3_raycast.js";
8
8
 
9
9
  /**
10
10
  * Convex polyhedron collider.
@@ -161,9 +161,13 @@ export class ConvexHullShape3D extends AbstractShape3D {
161
161
  // Σ a·n === Σ (a − o)·n over a closed surface, since Σ n = 0.
162
162
  vol6 += (ax - ox) * nx + (ay - oy) * ny + (az - oz) * nz;
163
163
 
164
- nx /= len; ny /= len; nz /= len;
164
+ nx /= len;
165
+ ny /= len;
166
+ nz /= len;
165
167
  const o = f * 4;
166
- planes[o] = nx; planes[o + 1] = ny; planes[o + 2] = nz;
168
+ planes[o] = nx;
169
+ planes[o + 1] = ny;
170
+ planes[o + 2] = nz;
167
171
  planes[o + 3] = nx * ax + ny * ay + nz * az;
168
172
  }
169
173
  this.__planes = planes;
@@ -178,13 +182,22 @@ export class ConvexHullShape3D extends AbstractShape3D {
178
182
  this.face_loops = idx;
179
183
  }
180
184
 
181
- get volume() { return this.__volume; }
182
- get surface_area() { return this.__surface_area; }
185
+ get volume() {
186
+ return this.__volume;
187
+ }
188
+
189
+ get surface_area() {
190
+ return this.__surface_area;
191
+ }
183
192
 
184
193
  compute_bounding_box(result) {
185
194
  const b = this.__bbox;
186
- result[0] = b[0]; result[1] = b[1]; result[2] = b[2];
187
- result[3] = b[3]; result[4] = b[4]; result[5] = b[5];
195
+ result[0] = b[0];
196
+ result[1] = b[1];
197
+ result[2] = b[2];
198
+ result[3] = b[3];
199
+ result[4] = b[4];
200
+ result[5] = b[5];
188
201
  }
189
202
 
190
203
  /**
@@ -201,9 +214,9 @@ export class ConvexHullShape3D extends AbstractShape3D {
201
214
  ) {
202
215
 
203
216
  const v = this.vertices;
204
- const vc = v.length / 3;
217
+ const vertex_array_length = v.length;
205
218
 
206
- if (vc === 0) {
219
+ if (vertex_array_length === 0) {
207
220
 
208
221
  result[result_offset] = 0;
209
222
  result[result_offset + 1] = 0;
@@ -212,27 +225,27 @@ export class ConvexHullShape3D extends AbstractShape3D {
212
225
  return;
213
226
  }
214
227
 
215
- let best = -Infinity;
216
- let bi = 0;
228
+ let best_distance = -Infinity;
229
+ let best_offset = 0;
217
230
 
218
- for (let i = 0; i < vc; i++) {
231
+ for (let offset = 0; offset < vertex_array_length; offset += 3) {
219
232
 
220
- const v_x = v[i * 3];
221
- const v_y = v[i * 3 + 1];
222
- const v_z = v[i * 3 + 2];
233
+ const v_x = v[offset];
234
+ const v_y = v[offset + 1];
235
+ const v_z = v[offset + 2];
223
236
 
224
237
  const d = direction_x * v_x + direction_y * v_y + direction_z * v_z;
225
238
 
226
- if (d > best) {
227
- best = d;
228
- bi = i;
239
+ if (d > best_distance) {
240
+ best_distance = d;
241
+ best_offset = offset;
229
242
  }
230
243
 
231
244
  }
232
245
 
233
- result[result_offset] = v[bi * 3];
234
- result[result_offset + 1] = v[bi * 3 + 1];
235
- result[result_offset + 2] = v[bi * 3 + 2];
246
+ result[result_offset] = v[best_offset];
247
+ result[result_offset + 1] = v[best_offset + 1];
248
+ result[result_offset + 2] = v[best_offset + 2];
236
249
  }
237
250
 
238
251
  /**
@@ -342,10 +355,14 @@ export class ConvexHullShape3D extends AbstractShape3D {
342
355
  const d2 = tri3_closest_point(scratch_closest, 0, px, py, pz, ax, ay, az, bx, by, bz, cx, cy, cz);
343
356
  if (d2 < best_d2) {
344
357
  best_d2 = d2;
345
- bx_ = scratch_closest[0]; by_ = scratch_closest[1]; bz_ = scratch_closest[2];
358
+ bx_ = scratch_closest[0];
359
+ by_ = scratch_closest[1];
360
+ bz_ = scratch_closest[2];
346
361
  }
347
362
  }
348
- result[0] = bx_; result[1] = by_; result[2] = bz_;
363
+ result[0] = bx_;
364
+ result[1] = by_;
365
+ result[2] = bz_;
349
366
  }
350
367
 
351
368
 
@@ -387,4 +404,3 @@ ConvexHullShape3D.prototype.isConvexHullShape3D = true;
387
404
 
388
405
  // closest-point-on-triangle output for the surface-scan query helpers
389
406
  const scratch_closest = new Float64Array(3);
390
- const scratch_point = new Float64Array(3);
@@ -1 +1 @@
1
- {"version":3,"file":"PosedShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/PosedShape3D.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAEI,mCAAmC;IACnC,OADW,eAAe,GAAC,IAAI,CAClB;IAEb,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IAEP;;;;;;;OAOG;IACH,aAJW,eAAe,YACf,UAAQ;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,YACpC,aAAW;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAM1D;IAoED,wCASC;IA4BD;;;;;;;OAOG;IACH,gCAHW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,GAChC,MAAM,CAKlB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,GAChC,OAAO,CAKnB;IAGL;;;;OAIG;IACH,yBAFU,OAAO,CAEoB;;CAPpC;gCAlL+B,sBAAsB"}
1
+ {"version":3,"file":"PosedShape3D.d.ts","sourceRoot":"","sources":["../../../../../../src/core/geom/3d/shape/PosedShape3D.js"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAEI,mCAAmC;IACnC,OADW,eAAe,GAAC,IAAI,CAClB;IAEb,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IACP,WAAO;IAEP;;;;;;;OAOG;IACH,aAJW,eAAe,YACf,UAAQ;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,YACpC,aAAW;QAAC,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAC;QAAA,CAAC,EAAC,MAAM,CAAA;KAAC,QAM1D;IAyED,wCASC;IA4BD;;;;;;;OAOG;IACH,gCAHW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,GAChC,MAAM,CAKlB;IAED;;;;;OAKG;IACH,sBAHW,MAAM,EAAE,GAAC,YAAY,GAAC,YAAY,GAChC,OAAO,CAKnB;IAGL;;;;OAIG;IACH,yBAFU,OAAO,CAEoB;;CAPpC;gCAvL+B,sBAAsB"}
@@ -86,7 +86,10 @@ export class PosedShape3D extends AbstractShape3D {
86
86
  // inline here at the cost of ~7 extra lines per direction.
87
87
  // See core/geom/vec3/v3_quat3_apply.js for the canonical form
88
88
  // — this is a literal expansion of it.
89
- const qx = this.qx, qy = this.qy, qz = this.qz, qw = this.qw;
89
+ const qx = this.qx;
90
+ const qy = this.qy;
91
+ const qz = this.qz;
92
+ const qw = this.qw;
90
93
 
91
94
  // Rotate world direction into body frame via the conjugate
92
95
  // quaternion: v_local = q* · v_world · q. Conjugate xyz are
@@ -105,7 +108,9 @@ export class PosedShape3D extends AbstractShape3D {
105
108
 
106
109
  // Rotate the body-local support point back to world: v_world =
107
110
  // q · v_local · q*.
108
- const lsx = scratch_local[0], lsy = scratch_local[1], lsz = scratch_local[2];
111
+ const lsx = scratch_local[0];
112
+ const lsy = scratch_local[1];
113
+ const lsz = scratch_local[2];
109
114
 
110
115
  const tsx = qw * lsx + qy * lsz - qz * lsy;
111
116
  const tsy = qw * lsy + qz * lsx - qx * lsz;
@@ -64,11 +64,11 @@ export class FluidObstacleSystem extends System<any> {
64
64
  /**
65
65
  * @param {FluidComponent} fluid
66
66
  */
67
- static "__#360@#refresh_masks"(fluid: FluidComponent): void;
67
+ static "__#362@#refresh_masks"(fluid: FluidComponent): void;
68
68
  /**
69
69
  * @param {FluidComponent} fluid
70
70
  */
71
- static "__#360@#clear_field"(fluid: FluidComponent): void;
71
+ static "__#362@#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 "__#360@#voxelize"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array): void;
83
+ static "__#362@#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 "__#360@#stamp_wall_velocity"(fluid: FluidComponent, posed: PosedShape3D, aabb: Float64Array, inflation: number, point: Float64Array, wvx: number, wvy: number, wvz: number): void;
103
+ static "__#362@#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 Transform> | ResourceAccessSpecification<typeof RigidBody> | ResourceAccessSpecification<typeof Collider> | ResourceAccessSpecification<typeof FluidComponent> | ResourceAccessSpecification<typeof FluidObstacle>)[];
@@ -12,6 +12,10 @@
12
12
  > **Revision 3** records what shipped. The §9 decisions were taken (§9), the error-carrying
13
13
  > `workgroup_size_error` of revision 2 was rejected in favour of throwing at construction (§3.5),
14
14
  > and the code landed in three commits (§10).
15
+ >
16
+ > **Revision 4** rewrites §3.2, which was wrong and shipped a renderer that could not draw. Revision
17
+ > 2 identified `setBindGroup` as the one forward that could be actively wrong and then guarded the
18
+ > wrong end of it. §8.9 is the post-mortem; §3.2 is now the rule that replaced it.
15
19
 
16
20
  ---
17
21
 
@@ -228,9 +232,7 @@ export class ShadeGPUComputePassEncoder {
228
232
 
229
233
  setBindGroupRange(index, bind_group, data, data_start, data_length) { /* §3.2 */ }
230
234
 
231
- setImmediates(range_offset, data, data_offset, data_size) {
232
- this.#pass.setImmediates(range_offset, data, data_offset, data_size);
233
- }
235
+ setImmediates(...args) { /* §3.2, shape 1 */ }
234
236
 
235
237
  // --- GPUDebugCommandsMixin ------------------------------------------------
236
238
 
@@ -662,6 +664,16 @@ call it out. This is the single most likely place for this change to introduce a
662
664
  - **"Both classes ~120 lines."** Now ~130 with the `label` setter and the split `setBindGroup`.
663
665
  Immaterial, but the earlier number was a guess presented as a measurement.
664
666
 
667
+ ### 8.8 Residual risks, accepted
668
+
669
+ - **A WebGPU method Chrome ships that Shade has not modelled** is now a hard failure rather than a
670
+ silent forward. That is the intended trade (§8.2), but it means the browser check is load-bearing
671
+ and needs to actually run in CI or a pre-release checklist, not just exist.
672
+ - **`executeBundles` work is uncounted** (§3.3). Pre-existing, documented, nothing uses bundles.
673
+ - **99 call sites change type.** No reflection over pass objects exists anywhere in the tree
674
+ (`typeof pass.x`, `Object.keys(pass)`, spread, `instanceof` — all checked, all absent), so the
675
+ surface is method calls only. If one is missed it throws at that call; it cannot silently misbehave.
676
+
665
677
  ### 8.9 It shipped broken anyway — the arity bug *(found in a browser, after landing)*
666
678
 
667
679
  `setBindGroup(0, bind_group)` forwarded `setBindGroup(0, bind_group, undefined)`, and the renderer
@@ -702,16 +714,6 @@ rather than claiming a mechanism it cannot demonstrate.
702
714
  three tiers in §6 are blind to the mock/browser boundary by construction, and this change has now
703
715
  been wrong at that boundary twice.
704
716
 
705
- ### 8.8 Residual risks, accepted
706
-
707
- - **A WebGPU method Chrome ships that Shade has not modelled** is now a hard failure rather than a
708
- silent forward. That is the intended trade (§8.2), but it means the browser check is load-bearing
709
- and needs to actually run in CI or a pre-release checklist, not just exist.
710
- - **`executeBundles` work is uncounted** (§3.3). Pre-existing, documented, nothing uses bundles.
711
- - **99 call sites change type.** No reflection over pass objects exists anywhere in the tree
712
- (`typeof pass.x`, `Object.keys(pass)`, spread, `instanceof` — all checked, all absent), so the
713
- surface is method calls only. If one is missed it throws at that call; it cannot silently misbehave.
714
-
715
717
  ---
716
718
 
717
719
  ## 9. Decisions taken
@@ -745,6 +747,9 @@ Still open, and deliberately not done here:
745
747
  2. `refactor(shade): pass encoders become explicit decorator classes` — §§3.1–3.4 and 3.6, plus the
746
748
  BVH verification tool moved onto a `GPUFrameRecorder`.
747
749
  3. `refactor(shade): remove the debug-timer path, the profiler is the only one` — §4.
750
+ 4. `fix(shade): pass encoders forward the arity they received` — §3.2, after the renderer failed on
751
+ its first bind group in a browser. §8.9.
748
752
 
749
753
  Revision 2 ordered these decorators-first; that was wrong (§8.4) and the workgroup size went first
750
- instead. Full suite green after each: 15,012 tests.
754
+ instead. Full suite green after each: 15,014 tests — which, per §8.9, was never going to be enough
755
+ on its own.
@@ -1 +1 @@
1
- {"version":3,"file":"Mesh.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/scene/Mesh.js"],"names":[],"mappings":"AAYA;IA2HI;;;;;;OAMG;IACH,mFAFY,IAAI,CAoBf;IAnJD;;OAEG;IACH,0BAAS;IAET;;OAEG;IACH,wBAAS;IAET;;;OAGG;IACH,cAFU,YAAY,CAEa;IAEnC;;;OAGG;IACH,iBAFU,YAAY,CAEgB;IAEtC;;;OAGG;IACH,SAFa,IAAI,CAQhB;IAED;;;OAGG;IACH,YAFW,IAAI,QAiBd;IAED;;;;OAIG;IACH,cAHW,IAAI,GACF,OAAO,CAQnB;IAQD,0BAOC;IAED,0BAqCC;IA+BL,gBAAqB;CAHpB;uBAvJsB,aAAa"}
1
+ {"version":3,"file":"Mesh.d.ts","sourceRoot":"","sources":["../../../../../src/shade/renderer/scene/Mesh.js"],"names":[],"mappings":"AAUA;IA2HI;;;;;;OAMG;IACH,mFAFY,IAAI,CAoBf;IAnJD;;OAEG;IACH,0BAAS;IAET;;OAEG;IACH,wBAAS;IAET;;;OAGG;IACH,cAFU,YAAY,CAEa;IAEnC;;;OAGG;IACH,iBAFU,YAAY,CAEgB;IAEtC;;;OAGG;IACH,SAFa,IAAI,CAQhB;IAED;;;OAGG;IACH,YAFW,IAAI,QAiBd;IAED;;;;OAIG;IACH,cAHW,IAAI,GACF,OAAO,CAQnB;IAQD,0BAOC;IAED,0BAqCC;IA+BL,gBAAqB;CAHpB;uBAvJsB,aAAa"}
@@ -1,10 +1,8 @@
1
1
  import { assert } from "../../../core/assert.js";
2
2
  import { AABB3 } from "../../../core/geom/3d/aabb/AABB3.js";
3
- import {
4
- aabb3_from_v3_array_transformed
5
- } from "../../../core/geom/3d/aabb/aabb3_from_v3_array_transformed.js";
3
+ import { aabb3_from_v3_array_transformed } from "../../../core/geom/3d/aabb/aabb3_from_v3_array_transformed.js";
6
4
  import { aabb3_matrix4_project } from "../../../core/geom/3d/aabb/aabb3_matrix4_project.js";
7
- import { sphere_matrix4_project } from "../../../core/geom/3d/sphere/sphere_matrix4_transform.js";
5
+ import { sphere_matrix4_transform } from "../../../core/geom/3d/sphere/sphere_matrix4_transform.js";
8
6
  import { epsilonEquals } from "../../../core/math/epsilonEquals.js";
9
7
  import { optional_equality } from "../../descriptor/util/optional_equality.js";
10
8
  import { StandardAttributes } from "../geometry/StandardAttributes.js";
@@ -91,7 +89,7 @@ export class Mesh extends Node3D {
91
89
  const m = this.transform_global;
92
90
 
93
91
  aabb3_matrix4_project(this.bounding_box, this.geometry.bounding_box, m);
94
- sphere_matrix4_project(this.bounding_sphere, this.geometry.bounding_sphere, m);
92
+ sphere_matrix4_transform(this.bounding_sphere, this.geometry.bounding_sphere, m);
95
93
  }
96
94
 
97
95
  updateBoundsTight() {
@@ -128,7 +126,7 @@ export class Mesh extends Node3D {
128
126
 
129
127
  {
130
128
  // todo do a tight sphere bounds
131
- sphere_matrix4_project(this.bounding_sphere, this.geometry.bounding_sphere, this.transform_global);
129
+ sphere_matrix4_transform(this.bounding_sphere, this.geometry.bounding_sphere, this.transform_global);
132
130
  }
133
131
  }
134
132
  }
@@ -81,7 +81,8 @@ export class GPUSceneShadowmapContext {
81
81
  * Runs at the top of every frame, before {@link draw}. Responsibilities:
82
82
  *
83
83
  * 1. **Reset per-frame signals.** `should_draw` is cleared on every map so a stale "yes"
84
- * from a prior frame can never cause a spurious extra raster.
84
+ * from a prior frame can never cause a spurious extra raster. Folded into the selector's
85
+ * scoring pass, which walks every map anyway.
85
86
  * 2. **Measure on-screen presence.** `projected_area_px` is computed once per map and stored
86
87
  * on the map itself — consumed by the resolution policy, the scorer, and the selector,
87
88
  * and exposed for debug tooling.
@@ -90,7 +91,8 @@ export class GPUSceneShadowmapContext {
90
91
  * resize. The committed `layout` is left untouched so sampling remains valid; the
91
92
  * pending is consumed later by {@link draw} when the scheduler naturally picks this map
92
93
  * up (no forced redraw).
93
- * 4. **Score + select.** For each map:
94
+ * 4. **Score + select.** Delegated to {@link ShadowmapDrawSelector}, which sets `should_draw`
95
+ * on the winners and reports the view spend and whether any candidate went unmet:
94
96
  * - **Directional** → always selected (infinite score); view cost still charged.
95
97
  * - **Local, off-screen** (`projected_area_px <= 0`) → skipped entirely.
96
98
  * - **Local, in-view, invalid** → MUST draw. `is_invalid` means the committed layout
@@ -99,15 +101,16 @@ export class GPUSceneShadowmapContext {
99
101
  * layout model — this flag fires only on first-draw.
100
102
  * - **Local, in-view, valid** → compete for the remaining budget by score. Ordering
101
103
  * is descending score so the most important (largest-on-screen, stalest) maps win.
102
- * 5. **Commit selection.** On each winner we set `should_draw = true` and call
103
- * `map.update()` so its projection matrices reflect the current frame. The
104
- * `last_updated_frame_index` stamp is deferred to {@link draw} — it records when the
105
- * atlas contents become current, not when the projection is fitted.
104
+ * 5. **Fit the winners.** `map.update()` on each selected map, so its projection matrices
105
+ * reflect the current frame. The `last_updated_frame_index` stamp is deferred to
106
+ * {@link draw} — it records when the atlas contents become current, not when the
107
+ * projection is fitted.
106
108
  *
107
109
  * Shadow records are NOT written here; they're written at the end of {@link draw} so the
108
110
  * shader reads slots that reflect any just-committed pending swaps within the same frame.
109
111
  *
110
- * See {@link compute_shadowmap_update_score} for the scoring formula and rationale.
112
+ * See {@link compute_shadowmap_update_score} for the scoring formula and rationale, and
113
+ * {@link ShadowmapDrawSelector} for why the ordering is a heap drain rather than a sort.
111
114
  *
112
115
  * @param {Camera} main_camera used both for rendering the final frame and for projected-area
113
116
  * computation and directional CSM fit; must be callable for `.update()`
@@ -160,6 +163,12 @@ export class GPUSceneShadowmapContext {
160
163
  * {@link LightCollection.version} hasn't moved — `light.casts_shadow` flips are not
161
164
  * tracked by version and are detected here.
162
165
  *
166
+ * Runs as three linear passes — mark, reclaim, allocate — over {@link #registry}, which owns
167
+ * the light -> map index and the marks. It used to scan the map array once per light to find
168
+ * out whether that light already had one, which made the pass quadratic: 148us per frame at
169
+ * 512 lights against 19us now, and 554us against 45us at 1024. See
170
+ * benchmark/reconcile.spec.js.
171
+ *
163
172
  * @returns {boolean} true when at least one map was added/evicted or at least one light's
164
173
  * per-type shadow_id moved. The caller uses this to decide whether the light
165
174
  * records need to be re-published (their `shadow_id` and CASTS_SHADOW_BIT may
@@ -1 +1 @@
1
- {"version":3,"file":"GPUSceneShadowmapContext.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js"],"names":[],"mappings":"AAsFA;;GAEG;AACH;IA8JI;;;;;;;;OAQG;IACH,8FA0DC;IAhOD,2BAAwG;IAExG;;;OAGG;IACH,SAFU,iBAAiB,CAEnB;IAQR;;;;;;;;OAQG;IACH,qBAFU,WAAW,CAED;IAsBpB;;;;;;;;OAQG;IACH,SAFU,OAAO,CAEF;IAEf;;;;;;;;;;OAUG;IACH,+BAFU,OAAO,CAEqB;IAqCtC;;OAEG;IACH,iCAEC;IAED;;;;;;OAMG;IACH,oCAEC;IAED;;;;;OAKG;IACH,oCAEC;IAgGD;;;;OAIG;IACH,uBAFa,YAAU,SAAS,CAI/B;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAwCG;IACH,0EAJW,MAAM,mBACN,UAAU,MAAM,CAAC,QA6G3B;IAyED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,gDAiFC;IA6aD;;;;;;;;;;;;;;OAcG;IACH,kBALa,OAAO,CAkMnB;IAED;;;;OAIG;IACH,iBAHW,MAAM,GACJ,KAAK,CAWjB;IAED,gBAWC;;CACJ;oCAvyCmC,+DAA+D;kCAkBjE,oCAAoC;4BAG1C,2BAA2B;kBAtBrC,wCAAwC"}
1
+ {"version":3,"file":"GPUSceneShadowmapContext.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/shadow/map/GPUSceneShadowmapContext.js"],"names":[],"mappings":"AAwFA;;GAEG;AACH;IA8KI;;;;;;;;OAQG;IACH,8FA0DC;IAhPD,2BAAwG;IAExG;;;OAGG;IACH,SAFU,iBAAiB,CAEnB;IAQR;;;;;;;;OAQG;IACH,qBAFU,WAAW,CAED;IAsCpB;;;;;;;;OAQG;IACH,SAFU,OAAO,CAEF;IAEf;;;;;;;;;;OAUG;IACH,+BAFU,OAAO,CAEqB;IAqCtC;;OAEG;IACH,iCAEC;IAED;;;;;;OAMG;IACH,oCAEC;IAED;;;;;OAKG;IACH,oCAEC;IAgGD;;;;OAIG;IACH,uBAFa,YAAU,SAAS,CAI/B;IAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA2CG;IACH,0EAJW,MAAM,mBACN,UAAU,MAAM,CAAC,QAgE3B;IAyED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OA+BG;IACH,gDAiFC;IA6aD;;;;;;;;;;;;;;;;;;;;OAoBG;IACH,kBALa,OAAO,CAuOnB;IAED;;;;OAIG;IACH,iBAHW,MAAM,GACJ,KAAK,CAWjB;IAED,gBAYC;;CACJ;oCA3zCmC,+DAA+D;kCAkBjE,oCAAoC;4BAG1C,2BAA2B;kBAtBrC,wCAAwC"}