@woosh/meep-engine 2.33.38 → 2.33.42

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 (40) hide show
  1. package/core/bvh2/binary/2/BinaryUint32BVH.js +3 -3
  2. package/core/collection/array/typed/typed_array_copy.js +2 -2
  3. package/core/geom/3d/aabb/aabb3_matrix4_project_by_corners.js +1 -1
  4. package/core/geom/3d/morton/mortonEncode_magicbits.js +7 -6
  5. package/core/geom/3d/morton/v3_morton_encode_transformed.js +15 -11
  6. package/core/geom/3d/tetrahedra/tetrahedra_collection.js +193 -18
  7. package/core/geom/3d/tetrahedra/tetrahedron_contains_point.js +2 -1
  8. package/engine/asset/loaders/GLTFAssetLoader.js +4 -8
  9. package/engine/ecs/System.d.ts +24 -1
  10. package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +1 -1
  11. package/engine/graphics/particles/particular/engine/parameter/ParameterLookupTable.js +20 -24
  12. package/engine/graphics/particles/particular/engine/shader/ShaderManager.js +1 -1
  13. package/engine/graphics/particles/particular/engine/utils/volume/prototypeParticleVolume.js +2 -2
  14. package/engine/graphics/render/buffer/simple-fx/DepthLimitedBlur.js +9 -4
  15. package/engine/graphics/render/buffer/simple-fx/ao/AmbientOcclusionPostProcessEffect.js +7 -1
  16. package/engine/graphics/render/forward_plus/LightManager.js +295 -51
  17. package/engine/graphics/render/forward_plus/LightManager.spec.js +1 -1
  18. package/engine/graphics/render/forward_plus/PointLightData.js +9 -13
  19. package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +10 -1
  20. package/engine/graphics/render/forward_plus/debug/LightClusterVolumeMaterial.js +1 -1
  21. package/engine/graphics/render/forward_plus/debug/buildLightClusterWidget.js +3 -1
  22. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +31 -18
  23. package/engine/graphics/render/forward_plus/materials/fp_build_fragment_shader.js +1 -0
  24. package/engine/graphics/render/forward_plus/model/AbstractLight.js +50 -0
  25. package/engine/graphics/render/forward_plus/model/Decal.js +68 -0
  26. package/engine/graphics/render/forward_plus/{PointLight.js → model/PointLight.js} +19 -11
  27. package/engine/graphics/render/forward_plus/{SpotLight.js → model/SpotLight.js} +20 -12
  28. package/engine/graphics/render/forward_plus/plugin/ptototypeFPPlugin.js +6 -5
  29. package/engine/graphics/render/forward_plus/{prototypeLightManager.js → prototype/prototypeLightManager.js} +100 -25
  30. package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +29 -20
  31. package/engine/graphics/render/visibility/IncrementalDeltaSet.d.ts +2 -0
  32. package/engine/graphics/render/visibility/IncrementalDeltaSet.js +1 -0
  33. package/engine/graphics/texture/atlas/AbstractTextureAtlas.js +19 -0
  34. package/engine/graphics/texture/atlas/CachingTextureAtlas.js +99 -0
  35. package/engine/graphics/texture/atlas/ManagedTextureAtlas.js +19 -39
  36. package/engine/graphics/texture/atlas/ReferencedTextureAtlas.js +75 -0
  37. package/engine/graphics/texture/atlas/TextureAtlas.js +22 -2
  38. package/engine/reference/v2/Reference.d.ts +4 -0
  39. package/engine/reference/v2/Reference.js +3 -1
  40. package/package.json +1 -1
@@ -434,11 +434,11 @@ export class BinaryUint32BVH {
434
434
  * Assemble leaf nodes into hierarchy, set binary node bounds iteratively bottom up
435
435
  */
436
436
  build() {
437
- const nodeCount = this.__node_count_binary;
437
+ const binary_node_count = this.__node_count_binary;
438
438
 
439
- const leaf_node_block_address = this.__node_count_binary * BVH_BINARY_NODE_SIZE;
439
+ const leaf_node_block_address = binary_node_count * BVH_BINARY_NODE_SIZE;
440
440
 
441
- let level = Math.floor(Math.log(nodeCount) / Math.log(2));
441
+ let level = Math.floor(Math.log(binary_node_count) / Math.log(2));
442
442
 
443
443
  let i, offset, level_node_count;
444
444
  //NOTE: building first level separately allows to avoid some switching logic needed to determine what is the type of lower level node
@@ -3,8 +3,8 @@ import { array_copy } from "../copyArray.js";
3
3
 
4
4
  /**
5
5
  * Assumes arrays have the same type
6
- * @param {TypedArray|Float32Array} source
7
- * @param {TypedArray|Float32Array} destination
6
+ * @param {TypedArray|Float32Array|Uint8Array} source
7
+ * @param {TypedArray|Float32Array|Uint8Array} destination
8
8
  */
9
9
  export function typed_array_copy(source, destination) {
10
10
  const destination_size = destination.length;
@@ -4,7 +4,7 @@ import { assert } from "../../../assert.js";
4
4
  *
5
5
  * @param {number[]|Float32Array} result
6
6
  * @param {number[]|Float32Array|Float64Array} aabb_corners
7
- * @param {number[]|Float32Array} projection_matrix
7
+ * @param {number[]|Float32Array|mat4} projection_matrix
8
8
  */
9
9
  export function aabb3_matrix4_project_by_corners(result, aabb_corners, projection_matrix) {
10
10
  assert.greaterThanOrEqual(result.length, 6, 'result.length must >= 6');
@@ -1,10 +1,12 @@
1
1
  /**
2
2
  * method to separate bits from a given integer 3 positions apart
3
3
  * we only look at the first 10 bits
4
+ *
5
+ * @example when input is ABC, output bits are A00B00C
4
6
  * @param {number} a
5
7
  * @returns {number}
6
8
  */
7
- function splitBy3(a) {
9
+ function split_by_3(a) {
8
10
  let x = a;
9
11
  x = (x | x << 16) & 0x30000ff;
10
12
  x = (x | x << 8) & 0x0300f00f;
@@ -25,10 +27,9 @@ function splitBy3(a) {
25
27
  */
26
28
  export function mortonEncode_magicbits(x, y, z) {
27
29
 
28
- const x_bits = splitBy3(x);
29
- const y_bits = splitBy3(y) << 1;
30
- const z_bit = splitBy3(z) << 2;
31
-
32
- return x_bits | y_bits | z_bit;
30
+ const x_bits = split_by_3(x);
31
+ const y_bits = split_by_3(y) << 1;
32
+ const z_bits = split_by_3(z) << 2;
33
33
 
34
+ return x_bits | y_bits | z_bits;
34
35
  }
@@ -1,4 +1,5 @@
1
1
  import { mortonEncode_magicbits } from "./mortonEncode_magicbits.js";
2
+ import { clamp } from "../../../math/clamp.js";
2
3
 
3
4
  /**
4
5
  * Assumes that coordinates will be mapped to -1..1 range after transform
@@ -16,18 +17,21 @@ export function v3_morton_encode_transformed(x, y, z, matrix) {
16
17
  const _z = matrix[2] * x + matrix[6] * y + matrix[10] * z + matrix[14];
17
18
  const _w = matrix[3] * x + matrix[7] * y + matrix[11] * z + matrix[15];
18
19
 
19
- const d = 1 / _w;
20
+ const d = 511.5 / _w;
20
21
 
21
- // apply perspective divide, values are now in Normalized Coordinate system (-1,1)
22
- const px = _x * d;
23
- const py = _y * d;
24
- const pz = _z * d;
22
+ // apply perspective divide as well as scaling to 10 bits
23
+ const ndc_x = _x * d;
24
+ const ndc_y = _y * d;
25
+ const ndc_z = _z * d;
25
26
 
26
- // scale each value up to 10 bits (for 10 bits, the multiplier = 1024. As we have -1,1 range, we do (v+1)/2. 1024/2 = 512)
27
+ // Normalize values (0,1023) and clamp to prevent wrapping
28
+ const px = clamp(ndc_x + 511.5, 0, 1023);
29
+ const py = clamp(ndc_y + 511.5, 0, 1023);
30
+ const pz = clamp(ndc_z + 511.5, 0, 1023);
27
31
 
28
- const cx = ((px + 1) * 512) | 0;
29
- const cy = ((py + 1) * 512) | 0;
30
- const cz = ((pz + 1) * 512) | 0;
31
-
32
- return mortonEncode_magicbits(cx, cy, cz);
32
+ return mortonEncode_magicbits(
33
+ Math.round(px),
34
+ Math.round(py),
35
+ Math.round(pz)
36
+ );
33
37
  }
@@ -2,18 +2,34 @@ import { tetrahedron_contains_point } from "./tetrahedron_contains_point.js";
2
2
  import { assert } from "../../../assert.js";
3
3
  import { point_in_tetrahedron_circumsphere } from "./point_in_tetrahedron_circumsphere.js";
4
4
  import { BitSet } from "../../../binary/BitSet.js";
5
+ import { typed_array_copy } from "../../../collection/array/typed/typed_array_copy.js";
6
+ import { max3 } from "../../../math/max3.js";
5
7
 
6
8
  /**
7
9
  * @readonly
8
10
  * @type {number}
9
11
  */
10
12
  const LAYOUT_TETRA_BYTE_SIZE = 9 * 4;
13
+
14
+ /**
15
+ * @readonly
16
+ * @type {number}
17
+ */
11
18
  const INVALID_NEIGHBOUR = 0xFFFFFFFF;
12
19
 
13
20
  class Cavity {
14
21
  constructor() {
15
22
  this.indices = [];
16
- this.__size = 0;
23
+
24
+ /**
25
+ *
26
+ * @type {number}
27
+ */
28
+ this.size = 0;
29
+ }
30
+
31
+ update() {
32
+ // compute in/out state for each side of each tetra
17
33
  }
18
34
 
19
35
  /**
@@ -21,7 +37,7 @@ class Cavity {
21
37
  * @param {number} i
22
38
  */
23
39
  push(i) {
24
- this.indices[this.__size++] = i;
40
+ this.indices[this.size++] = i;
25
41
  }
26
42
 
27
43
  /**
@@ -30,7 +46,7 @@ class Cavity {
30
46
  * @returns {boolean}
31
47
  */
32
48
  includes(i) {
33
- const n = this.__size;
49
+ const n = this.size;
34
50
  for (let j = 0; j < n; j++) {
35
51
  if (this.indices[j] === i) {
36
52
  return true;
@@ -41,7 +57,7 @@ class Cavity {
41
57
  }
42
58
 
43
59
  reset() {
44
- this.__size = 0;
60
+ this.size = 0;
45
61
  }
46
62
  }
47
63
 
@@ -63,7 +79,7 @@ const SCRATCH_CAVITY = new Cavity();
63
79
  */
64
80
  export class TetrahedraCollection {
65
81
  constructor(initial_size) {
66
- this.__buffer = new ArrayBuffer(initial_size * 4 * 9);
82
+ this.__buffer = new ArrayBuffer(initial_size * LAYOUT_TETRA_BYTE_SIZE);
67
83
 
68
84
  this.__view = new DataView(this.__buffer);
69
85
 
@@ -79,6 +95,49 @@ export class TetrahedraCollection {
79
95
  this.__occupancy = new BitSet();
80
96
  }
81
97
 
98
+ /**
99
+ *
100
+ * @param {number} n
101
+ */
102
+ setCapacity(n) {
103
+ if (n === this.__capacity) {
104
+ // do nothing
105
+ return;
106
+ }
107
+
108
+ if (n < this.__length) {
109
+ throw new Error('Dropping information, not supported');
110
+ }
111
+
112
+ // allocate new buffer
113
+ const new_buffer = new ArrayBuffer(n * LAYOUT_TETRA_BYTE_SIZE);
114
+
115
+ // move data across from old buffer to the new one
116
+ const destination_uint8 = new Uint8Array(new_buffer);
117
+ const source_uint8 = new Uint8Array(this.__buffer);
118
+
119
+ typed_array_copy(source_uint8, destination_uint8);
120
+
121
+ // set new buffer
122
+ this.__buffer = new_buffer;
123
+ this.__view = new DataView(new_buffer);
124
+ }
125
+
126
+ /**
127
+ *
128
+ * @param {number} n
129
+ */
130
+ ensureCapacity(n) {
131
+
132
+ const new_capacity = max3(
133
+ n,
134
+ this.__capacity * 1.2,
135
+ this.__capacity + 32
136
+ );
137
+
138
+ this.setCapacity(new_capacity);
139
+ }
140
+
82
141
  /**
83
142
  *
84
143
  * @param {number} tetra_index
@@ -90,6 +149,17 @@ export class TetrahedraCollection {
90
149
  return this.__view.getUint32(tetra_address + (4 + neighbour_index) * 4);
91
150
  }
92
151
 
152
+ /**
153
+ *
154
+ * @param {number} tetra_index
155
+ * @param {number} neighbour_index which neighbour to set (0..11)
156
+ * @param {number} neighbour index of the neighbour
157
+ */
158
+ setNeighbour(tetra_index, neighbour_index, neighbour) {
159
+ const tetra_address = LAYOUT_TETRA_BYTE_SIZE * tetra_index;
160
+ return this.__view.setUint32(tetra_address + (4 + neighbour_index) * 4, neighbour);
161
+ }
162
+
93
163
  /**
94
164
  *
95
165
  * @param {number[]|Float32Array} points
@@ -97,12 +167,16 @@ export class TetrahedraCollection {
97
167
  * @param {number} b
98
168
  * @param {number} c
99
169
  * @param {number} d
170
+ * @returns {number} index of the new tetra
100
171
  */
101
172
  append(points, a, b, c, d) {
102
- const tetra_index = this.__length;
173
+ const tetra_index = this.__occupancy.nextClearBit(0);
103
174
  const address = tetra_index * LAYOUT_TETRA_BYTE_SIZE;
104
175
 
105
- // TODO check if there's enough room
176
+ if (tetra_index >= this.__capacity) {
177
+ // needs to be increased in size
178
+ this.ensureCapacity(tetra_index);
179
+ }
106
180
 
107
181
  const view = this.__view;
108
182
 
@@ -120,6 +194,8 @@ export class TetrahedraCollection {
120
194
  this.__occupancy.set(tetra_index, true);
121
195
 
122
196
  this.__length++;
197
+
198
+ return tetra_index;
123
199
  }
124
200
 
125
201
  /**
@@ -127,7 +203,27 @@ export class TetrahedraCollection {
127
203
  * @param {number} tera_index
128
204
  */
129
205
  remove(tera_index) {
206
+
207
+ // find neighbours and remove reference to self
208
+ for (let i = 0; i < 4; i++) {
209
+ const neighbour_encoded = this.getNeighbour(tera_index, i);
210
+
211
+ if (neighbour_encoded === INVALID_NEIGHBOUR) {
212
+ // no neighbour
213
+ continue;
214
+ }
215
+
216
+ // get tetrahedra index and point index
217
+ const neighbour_index = neighbour_encoded >> 2;
218
+ const neighbour_point = neighbour_encoded & 4;
219
+
220
+ // clear reference to self
221
+ this.setNeighbour(neighbour_index, neighbour_point, INVALID_NEIGHBOUR);
222
+ }
223
+
224
+ // mark as dead
130
225
  this.__occupancy.clear(tera_index);
226
+ this.__length--;
131
227
  }
132
228
 
133
229
  /**
@@ -140,12 +236,11 @@ export class TetrahedraCollection {
140
236
  removeTetrasConnectedToPoints(points, range_start, range_end) {
141
237
  const data_view = this.__view;
142
238
 
143
- const n = this.__length;
144
-
145
239
  let i = 0;
146
240
  let j = 0;
147
241
 
148
- for (; i < n; i++) {
242
+ const occupancy = this.__occupancy;
243
+ for (i = occupancy.nextSetBit(0); i !== -1; i = occupancy.nextSetBit(i + 1)) {
149
244
 
150
245
  const tet_address = i * LAYOUT_TETRA_BYTE_SIZE;
151
246
 
@@ -155,6 +250,7 @@ export class TetrahedraCollection {
155
250
  if (point_index >= range_start && point_index <= range_end) {
156
251
  // point index is in range, tetra should be removed
157
252
  this.remove(i);
253
+ break;
158
254
  }
159
255
  }
160
256
  }
@@ -228,18 +324,19 @@ export class TetrahedraCollection {
228
324
 
229
325
  // get neighbours
230
326
  for (let i = 0; i < 4; i++) {
231
- const neighbour = this.getNeighbour(tetra, i);
327
+ const neighbour_encoded = this.getNeighbour(tetra, i);
328
+ const neighbour_tetra_index = neighbour_encoded >> 2;
232
329
 
233
330
  if (
234
- (neighbour === INVALID_NEIGHBOUR) // no neighbour
235
- || closed_set.includes(neighbour) // already visited
236
- || open_set.includes(neighbour) // already in open set
331
+ (neighbour_encoded === INVALID_NEIGHBOUR) // no neighbour
332
+ || closed_set.includes(neighbour_tetra_index) // already visited
333
+ || open_set.includes(neighbour_tetra_index) // already in open set
237
334
  ) {
238
335
 
239
336
  continue;
240
337
  }
241
338
 
242
- const neighbour_address = neighbour * LAYOUT_TETRA_BYTE_SIZE;
339
+ const neighbour_address = neighbour_tetra_index * LAYOUT_TETRA_BYTE_SIZE;
243
340
 
244
341
  const a = data_view.getUint32(neighbour_address);
245
342
  const b = data_view.getUint32(neighbour_address + 4);
@@ -250,10 +347,10 @@ export class TetrahedraCollection {
250
347
  const is_in_sphere = point_in_tetrahedron_circumsphere(points, a, b, c, d, index);
251
348
 
252
349
  if (is_in_sphere) {
253
- open_set.push(neighbour);
350
+ open_set.push(neighbour_tetra_index);
254
351
  } else {
255
352
  // move directly to closed set
256
- closed_set.push(neighbour);
353
+ closed_set.push(neighbour_tetra_index);
257
354
  }
258
355
 
259
356
 
@@ -270,10 +367,88 @@ export class TetrahedraCollection {
270
367
  // Compute the cavity, that is the set of tetrahedra that need to be removed and rebuilt
271
368
  this.computeCavity(SCRATCH_CAVITY, points, index);
272
369
 
370
+
273
371
  // identify cavity "edge" - the set of tetras that are neighbours of the tetras that form the cavity
372
+ const cavity_size = SCRATCH_CAVITY.size;
373
+ const cavity_indices = SCRATCH_CAVITY.indices;
374
+ const data = this.__view;
375
+
376
+ for (let i = 0; i < cavity_size; i++) {
377
+ const cavity_index = cavity_indices[i];
378
+
379
+ // take each side of the tetra
380
+ const tetra_address = cavity_index * LAYOUT_TETRA_BYTE_SIZE;
381
+
382
+ const a = data.getUint32(tetra_address);
383
+ const b = data.getUint32(tetra_address + 4);
384
+ const c = data.getUint32(tetra_address + 8);
385
+ const d = data.getUint32(tetra_address + 12);
386
+
387
+ // attempt to form 4 tetras using the sides of the cavity
388
+ const a_neighbour = data.getUint32(tetra_address + 16);
389
+ const a_neighbour_index = a_neighbour >> 2;
390
+
391
+ const b_neighbour = data.getUint32(tetra_address + 20);
392
+ const b_neighbour_index = b_neighbour >> 2;
393
+
394
+ const c_neighbour = data.getUint32(tetra_address + 24);
395
+ const c_neighbour_index = c_neighbour >> 2;
396
+
397
+ const d_neighbour = data.getUint32(tetra_address + 28);
398
+ const d_neighbour_index = d_neighbour >> 2;
399
+
400
+
401
+ if (a_neighbour === INVALID_NEIGHBOUR || cavity_indices.indexOf(a_neighbour_index) === 0) {
402
+ // ABC is a valid side
403
+ const tet = this.append(points, b, d, c, index);
404
+ // patch in neighbours
405
+ if (a_neighbour !== INVALID_NEIGHBOUR) {
406
+ this.setNeighbour(tet, 3, a_neighbour);
407
+ this.setNeighbour(a_neighbour_index, a_neighbour & 3, tet);
408
+ }
409
+ }
410
+ if (b_neighbour === INVALID_NEIGHBOUR || cavity_indices.indexOf(b_neighbour_index) === 0) {
411
+ // ABC is a valid side
412
+ const tet = this.append(points, c, d, a, index);
413
+ if (b_neighbour !== INVALID_NEIGHBOUR) {
414
+ this.setNeighbour(tet, 3, b_neighbour);
415
+ this.setNeighbour(b_neighbour_index, b_neighbour & 3, tet);
416
+ }
417
+ }
418
+ if (c_neighbour === INVALID_NEIGHBOUR || cavity_indices.indexOf(c_neighbour_index) === 0) {
419
+ // ABC is a valid side
420
+ const tet = this.append(points, d, b, a, index);
421
+ if (c_neighbour !== INVALID_NEIGHBOUR) {
422
+ this.setNeighbour(tet, 3, c_neighbour);
423
+ this.setNeighbour(c_neighbour_index, c_neighbour & 3, tet);
424
+ }
425
+ }
426
+ if (d_neighbour === INVALID_NEIGHBOUR || cavity_indices.indexOf(d_neighbour_index) === 0) {
427
+ // ABC is a valid side
428
+ const tet = this.append(points, a, b, c, index);
429
+ if (d_neighbour !== INVALID_NEIGHBOUR) {
430
+ this.setNeighbour(tet, 3, d_neighbour);
431
+ this.setNeighbour(d_neighbour_index, d_neighbour & 3, tet);
432
+ }
433
+ }
434
+ }
435
+
274
436
 
275
437
  // 3. remove the cavity tetrahedrons
438
+ for (let i = 0; i < cavity_size; i++) {
439
+ const cavity_index = cavity_indices[i];
440
+
441
+ // clear out cavity, connectivity was already patched in the previous step
442
+ this.__occupancy.set(cavity_index, false);
443
+ }
444
+ }
445
+
446
+ /**
447
+ * optimize tetrahedrons to reduce number of thin and long tetras
448
+ * @see https://gitlab.onelab.info/gmsh/gmsh/-/blob/master/contrib/hxt/tetMesh/src/HXTSPR.c
449
+ */
450
+ optimize() {
276
451
 
277
- // 4. fill the cavity by creating new tetrahedrons connecting the point to the faces of cavity boundary
452
+ throw new Error('Not Implemented');
278
453
  }
279
454
  }
@@ -17,7 +17,8 @@ import { orient3d_fast } from "../plane/orient3d_fast.js";
17
17
  * @returns {boolean}
18
18
  */
19
19
  export function tetrahedron_contains_point(points, a, b, c, d, e) {
20
- return !(orient3d_fast(points, b, d, c, e) > 0
20
+ return !(
21
+ orient3d_fast(points, b, d, c, e) > 0
21
22
  || orient3d_fast(points, c, d, a, e) > 0
22
23
  || orient3d_fast(points, d, b, a, e) > 0
23
24
  || orient3d_fast(points, a, b, c, e) > 0
@@ -337,16 +337,12 @@ export class GLTFAssetLoader extends AssetLoader {
337
337
  return;
338
338
  }
339
339
 
340
- //find a child that is a mesh
341
- const meshes = scene.children.filter(isMesh);
342
340
 
343
- let root;
341
+ let root = scene;
344
342
 
345
- if (meshes.length !== 1) {
346
- //use the whole scene
347
- root = scene;
348
- } else {
349
- root = meshes[0];
343
+ if (children.length === 1 && isMesh(children[0])) {
344
+ // scene only contains a single mesh, use that directly
345
+ root = children[0];
350
346
  }
351
347
 
352
348
  traverseThreeObject(root, this.__prepareObjectMaterial, this);
@@ -1,3 +1,26 @@
1
- export class System {
1
+ import {EntityManager} from "./EntityManager";
2
2
 
3
+ interface Type<T> extends Function {
4
+ new(...args: any[]): T;
5
+ }
6
+
7
+ export class System<A, B = any, C = any, D = any, E = any> {
8
+ dependencies: Type<any>[]
9
+
10
+ startup(em: EntityManager, ready_callback: () => void, error_callback: () => void): void
11
+
12
+ shutdown(em: EntityManager, ready_callback: () => void, error_callback: () => void): void
13
+
14
+ link(a: A, entity: number): void
15
+ link(a: A, b: B, entity: number): void
16
+ link(a: A, b: B, c: C, entity: number): void
17
+ link(a: A, b: B, c: C, d: D, entity: number): void
18
+ link(a: A, b: B, c: C, d: D, e: E, entity: number): void
19
+
20
+ unlink(a: A, entity: number): void
21
+ unlink(a: A, b: B, entity: number): void
22
+ unlink(a: A, b: B, c: C, d: D, entity: number): void
23
+ unlink(a: A, b: B, c: C, d: D, e: E, entity: number): void
24
+
25
+ update(time_delta: number): void
3
26
  }
@@ -1,5 +1,5 @@
1
1
  import { LightBinding } from "../LightBinding.js";
2
- import { PointLight } from "../../../../render/forward_plus/PointLight.js";
2
+ import { PointLight } from "../../../../render/forward_plus/model/PointLight.js";
3
3
 
4
4
  export class FPLightBinding extends LightBinding {
5
5
  constructor() {
@@ -2,7 +2,6 @@ import { inverseLerp } from "../../../../../../core/math/inverseLerp.js";
2
2
  import { assert } from "../../../../../../core/assert.js";
3
3
  import { max2 } from "../../../../../../core/math/max2.js";
4
4
  import { min2 } from "../../../../../../core/math/min2.js";
5
- import { mix } from "../../../../../../core/math/mix.js";
6
5
  import { ParameterLookupTableFlags } from "./ParameterLookupTableFlags.js";
7
6
  import { ParameterLookupTableSerializationAdapter } from "../emitter/serde/ParameterLookupTableSerializationAdapter.js";
8
7
  import { computeHashIntegerArray } from "../../../../../../core/collection/array/computeHashIntegerArray.js";
@@ -125,7 +124,7 @@ export class ParameterLookupTable {
125
124
  */
126
125
  sample(position, result) {
127
126
  assert.isNumber(position, 'position');
128
- assert.greaterThanOrEqual(position, 0, 'position');
127
+ // assert.greaterThanOrEqual(position, 0, 'position');
129
128
  // assert.ok(position >= 0 && position <= 1, `position must be between 0 and 1, instead was ${position}`);
130
129
 
131
130
  const itemSize = this.itemSize;
@@ -140,32 +139,28 @@ export class ParameterLookupTable {
140
139
  let highIndex = lowIndex;
141
140
  let fraction = 0;
142
141
 
143
- //find position index
144
- for (i = 0; i < numValues; i++) {
145
- //NOTE: no complex search is used, since for small data sizes linear scan is typically faster
146
- const p = positions[i];
142
+ if (position <= positions[0]) {
147
143
 
148
- if (p === position) {
144
+ // special case, seek position is at or before the start of the table
145
+ lowIndex = 0;
146
+ highIndex = 0;
149
147
 
150
- // found the exact match
151
- lowIndex = i;
152
- highIndex = i;
153
- fraction = 0;
154
- break;
148
+ } else {
149
+
150
+ //find position index
151
+ for (i = 1; i < numValues; i++) {
152
+ //NOTE: no complex search is used, since for small data sizes linear scan is typically faster
153
+ const p = positions[i];
155
154
 
156
- } else if (p > position) {
157
- // next position if over the seek position
158
- highIndex = i;
159
- if (i === 0) {
160
- lowIndex = i;
161
- fraction = 0;
162
- } else {
155
+ if (p >= position) {
156
+ // next position if over the seek position
157
+ highIndex = i;
163
158
  lowIndex = i - 1;
164
- const prevPosition = positions[lowIndex];
165
- fraction = inverseLerp(prevPosition, p, position);
166
- }
167
159
 
168
- break;
160
+ fraction = inverseLerp(positions[lowIndex], p, position);
161
+
162
+ break;
163
+ }
169
164
  }
170
165
  }
171
166
 
@@ -177,7 +172,8 @@ export class ParameterLookupTable {
177
172
  const lowValue = data[low_offset + i];
178
173
  const highValue = data[high_offset + i];
179
174
 
180
- const value = mix(lowValue, highValue, fraction);
175
+ // inlined for speed: mix(low, high, fraction)
176
+ const value = lowValue * (1 - fraction) + highValue * fraction;
181
177
 
182
178
  result[i] = value;
183
179
  }
@@ -117,7 +117,7 @@ export class ShaderManager {
117
117
  generateMipmaps: true,
118
118
  minFilter: LinearMipMapLinearFilter,
119
119
  magFilter: LinearFilter,
120
- anisotropy: 0
120
+ anisotropy: 0 // sprites are always facing the screen, anisotropic filtering would be pointless
121
121
  });
122
122
 
123
123
  this.spriteTexture.name = 'Sprite sheet';
@@ -609,8 +609,8 @@ async function main(engine) {
609
609
 
610
610
  engine.graphics.renderer.setClearColor('rgba(99,99,99,1)');
611
611
 
612
- // await sample_scene_nam(engine);
613
- await sample_scene_0(engine);
612
+ await sample_scene_nam(engine);
613
+ // await sample_scene_0(engine);
614
614
  }
615
615
 
616
616
  /**
@@ -26,14 +26,16 @@ export class DepthLimitedBlur {
26
26
  uniforms: UniformsUtils.clone(DepthLimitedBlurShader.uniforms),
27
27
  defines: Object.assign({}, DepthLimitedBlurShader.defines),
28
28
  vertexShader: DepthLimitedBlurShader.vertexShader,
29
- fragmentShader: DepthLimitedBlurShader.fragmentShader
29
+ fragmentShader: DepthLimitedBlurShader.fragmentShader,
30
+ blending: NoBlending
30
31
  });
31
32
 
32
33
  this.__material_blur_y = new ShaderMaterial({
33
34
  uniforms: UniformsUtils.clone(DepthLimitedBlurShader.uniforms),
34
35
  defines: Object.assign({}, DepthLimitedBlurShader.defines),
35
36
  vertexShader: DepthLimitedBlurShader.vertexShader,
36
- fragmentShader: DepthLimitedBlurShader.fragmentShader
37
+ fragmentShader: DepthLimitedBlurShader.fragmentShader,
38
+ blending: NoBlending
37
39
  });
38
40
 
39
41
 
@@ -97,7 +99,10 @@ export class DepthLimitedBlur {
97
99
  */
98
100
  execute(renderer) {
99
101
  // update intermediate buffer size based on input size
100
- if (this.__intermediate.width !== this.__rt_output.width || this.__intermediate.height !== this.__rt_output.height) {
102
+ if (
103
+ this.__intermediate.width !== this.__rt_output.width
104
+ || this.__intermediate.height !== this.__rt_output.height
105
+ ) {
101
106
  this.__intermediate.setSize(this.__rt_output.width, this.__rt_output.height);
102
107
  }
103
108
 
@@ -118,7 +123,7 @@ export class DepthLimitedBlur {
118
123
 
119
124
  renderer.setRenderTarget(this.__rt_output);
120
125
  renderer.clearColor();
121
-
126
+
122
127
  renderScreenSpace(renderer, my);
123
128
 
124
129
  // restore state
@@ -286,7 +286,13 @@ export class AmbientOcclusionPostProcessEffect extends EnginePlugin {
286
286
 
287
287
  const size = graphics.viewport.size;
288
288
 
289
- this.__render_target.setSize(size.x, size.y);
289
+ const target_resolution_x = Math.ceil(size.x * this.__resolution_scale);
290
+ const target_resolution_y = Math.ceil(size.y * this.__resolution_scale);
291
+
292
+ this.__render_target.setSize(
293
+ target_resolution_x,
294
+ target_resolution_y
295
+ );
290
296
  }
291
297
 
292
298
  async startup() {