@woosh/meep-engine 2.47.31 → 2.47.33
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/build/meep.cjs +149 -38
- package/build/meep.min.js +1 -1
- package/build/meep.module.js +149 -38
- package/package.json +1 -1
- package/src/core/cache/Cache.js +12 -0
- package/src/core/collection/HashMap.js +33 -5
- package/src/core/collection/HashMap.spec.js +30 -4
- package/src/engine/asset/loaders/material/StaticMaterialCache.js +18 -0
- package/src/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +30 -34
- package/src/engine/graphics/ecs/mesh-v2/render/adapters/SGCacheKey.js +59 -0
- package/src/engine/intelligence/behavior/util/syncExecuteBehaviorToCompletion.js +34 -0
package/build/meep.module.js
CHANGED
|
@@ -60433,11 +60433,13 @@ class MapEntry {
|
|
|
60433
60433
|
* @param {number} hash
|
|
60434
60434
|
*/
|
|
60435
60435
|
constructor(key, value, hash) {
|
|
60436
|
+
|
|
60436
60437
|
/**
|
|
60437
60438
|
*
|
|
60438
60439
|
* @type {K}
|
|
60439
60440
|
*/
|
|
60440
60441
|
this.key = key;
|
|
60442
|
+
|
|
60441
60443
|
/**
|
|
60442
60444
|
*
|
|
60443
60445
|
* @type {V}
|
|
@@ -60449,6 +60451,7 @@ class MapEntry {
|
|
|
60449
60451
|
* @type {number}
|
|
60450
60452
|
*/
|
|
60451
60453
|
this.hash = hash;
|
|
60454
|
+
|
|
60452
60455
|
}
|
|
60453
60456
|
}
|
|
60454
60457
|
|
|
@@ -60464,6 +60467,13 @@ const DEFAULT_INITIAL_CAPACITY = 16;
|
|
|
60464
60467
|
*/
|
|
60465
60468
|
const DEFAULT_LOAD_FACTOR = 0.75;
|
|
60466
60469
|
|
|
60470
|
+
/**
|
|
60471
|
+
* Multiply previous size by this number when growing the table
|
|
60472
|
+
* @readonly
|
|
60473
|
+
* @type {number}
|
|
60474
|
+
*/
|
|
60475
|
+
const GROWTH_FACTOR = 2;
|
|
60476
|
+
|
|
60467
60477
|
/**
|
|
60468
60478
|
* Implements part of {@link Map} interface
|
|
60469
60479
|
* @copyright Alex Goldring (c) 2023
|
|
@@ -60488,7 +60498,9 @@ class HashMap {
|
|
|
60488
60498
|
assert.isFunction(keyHashFunction, 'keyHashFunction');
|
|
60489
60499
|
assert.isFunction(keyEqualityFunction, 'keyEqualityFunction');
|
|
60490
60500
|
assert.isNonNegativeInteger(capacity, 'capacity');
|
|
60501
|
+
|
|
60491
60502
|
assert.isNumber(loadFactor, 'loadFactor');
|
|
60503
|
+
assert.greaterThan(loadFactor, 0, 'loadFactor must be > 0');
|
|
60492
60504
|
|
|
60493
60505
|
/**
|
|
60494
60506
|
*
|
|
@@ -60601,6 +60613,8 @@ class HashMap {
|
|
|
60601
60613
|
* @private
|
|
60602
60614
|
*/
|
|
60603
60615
|
__compute_bucket_index(hash) {
|
|
60616
|
+
assert.isInteger(hash, 'hash');
|
|
60617
|
+
|
|
60604
60618
|
// mix the input hash to minimize potential impact of poor hash function spread
|
|
60605
60619
|
const mixed_hash = (hash >>> 16) ^ hash;
|
|
60606
60620
|
|
|
@@ -60664,18 +60678,19 @@ class HashMap {
|
|
|
60664
60678
|
|
|
60665
60679
|
}
|
|
60666
60680
|
|
|
60667
|
-
bucket.push(new MapEntry(key, value,raw_hash));
|
|
60681
|
+
bucket.push(new MapEntry(key, value, raw_hash));
|
|
60668
60682
|
|
|
60669
60683
|
const old_size = this.size;
|
|
60670
60684
|
const new_size = old_size + 1;
|
|
60671
60685
|
this.size = new_size;
|
|
60672
60686
|
|
|
60673
60687
|
// compute actual current load
|
|
60674
|
-
const
|
|
60688
|
+
const bucket_count = this.__bucket_count;
|
|
60689
|
+
const load = new_size / bucket_count;
|
|
60675
60690
|
|
|
60676
60691
|
if (load > this.__load_factor) {
|
|
60677
60692
|
// current load is too high, increase table size
|
|
60678
|
-
this.setBucketCount(
|
|
60693
|
+
this.setBucketCount(bucket_count * GROWTH_FACTOR);
|
|
60679
60694
|
}
|
|
60680
60695
|
}
|
|
60681
60696
|
|
|
@@ -60800,8 +60815,11 @@ class HashMap {
|
|
|
60800
60815
|
/**
|
|
60801
60816
|
*
|
|
60802
60817
|
* @param {function(message:string, key:K, value:V)} callback
|
|
60818
|
+
* @param {*} [thisArg]
|
|
60819
|
+
* @returns {boolean}
|
|
60803
60820
|
*/
|
|
60804
|
-
verifyHashes(callback) {
|
|
60821
|
+
verifyHashes(callback, thisArg) {
|
|
60822
|
+
let all_hashes_valid = true;
|
|
60805
60823
|
|
|
60806
60824
|
let h, i;
|
|
60807
60825
|
|
|
@@ -60827,15 +60845,25 @@ class HashMap {
|
|
|
60827
60845
|
//check hash
|
|
60828
60846
|
const raw_hash = this.keyHashFunction(entry.key);
|
|
60829
60847
|
|
|
60848
|
+
if (entry.hash !== raw_hash) {
|
|
60849
|
+
callback.call(thisArg, `Hash stored on the entry(=${entry.hash}) is different from the computed key hash(=${raw_hash}).`, entry.key, entry.value);
|
|
60850
|
+
|
|
60851
|
+
all_hashes_valid = false;
|
|
60852
|
+
}
|
|
60853
|
+
|
|
60830
60854
|
const actual_bucket_index = this.__compute_bucket_index(raw_hash);
|
|
60831
60855
|
|
|
60832
60856
|
const stored_bucket_index = parseInt(h);
|
|
60833
60857
|
|
|
60834
60858
|
if (actual_bucket_index !== stored_bucket_index) {
|
|
60835
|
-
callback(`Hash of key has changed. old=${stored_bucket_index}, new=${actual_bucket_index}`, entry.key, entry.value);
|
|
60859
|
+
callback.call(thisArg, `Hash of key has changed. old=${stored_bucket_index}, new=${actual_bucket_index}`, entry.key, entry.value);
|
|
60860
|
+
|
|
60861
|
+
all_hashes_valid = false;
|
|
60836
60862
|
}
|
|
60837
60863
|
}
|
|
60838
60864
|
}
|
|
60865
|
+
|
|
60866
|
+
return all_hashes_valid;
|
|
60839
60867
|
}
|
|
60840
60868
|
|
|
60841
60869
|
/**
|
|
@@ -61499,6 +61527,18 @@ class Cache {
|
|
|
61499
61527
|
|
|
61500
61528
|
this.weight = 0;
|
|
61501
61529
|
}
|
|
61530
|
+
|
|
61531
|
+
/**
|
|
61532
|
+
*
|
|
61533
|
+
* @param {function(message:string, key:Key, value:Value)} callback
|
|
61534
|
+
* @param {*} [thisArg]
|
|
61535
|
+
* @returns {boolean}
|
|
61536
|
+
*/
|
|
61537
|
+
validate(callback, thisArg) {
|
|
61538
|
+
return this.data.verifyHashes((msg, key, entry) => {
|
|
61539
|
+
callback.call(thisArg, msg, key, entry.value);
|
|
61540
|
+
});
|
|
61541
|
+
}
|
|
61502
61542
|
}
|
|
61503
61543
|
|
|
61504
61544
|
/**
|
|
@@ -77809,6 +77849,24 @@ class StaticMaterialCache {
|
|
|
77809
77849
|
return material;
|
|
77810
77850
|
|
|
77811
77851
|
}
|
|
77852
|
+
|
|
77853
|
+
/**
|
|
77854
|
+
*
|
|
77855
|
+
* @param {function(string)} error_consumer
|
|
77856
|
+
* @param {*} [thisArg]
|
|
77857
|
+
* @returns {boolean}
|
|
77858
|
+
*/
|
|
77859
|
+
validate(error_consumer, thisArg) {
|
|
77860
|
+
const materials_valid = this.materialCache.validate((message, key, value) => {
|
|
77861
|
+
error_consumer.call(thisArg, `Material error: ${message}. Material ${key}`);
|
|
77862
|
+
});
|
|
77863
|
+
const textures_valid = this.textureCache.validate((message, key, value) => {
|
|
77864
|
+
error_consumer.call(thisArg, `Texture error: ${message}. Texture ${key}`);
|
|
77865
|
+
});
|
|
77866
|
+
|
|
77867
|
+
return materials_valid
|
|
77868
|
+
&& textures_valid;
|
|
77869
|
+
}
|
|
77812
77870
|
}
|
|
77813
77871
|
|
|
77814
77872
|
/**
|
|
@@ -78461,42 +78519,90 @@ class InstancedMeshGroup {
|
|
|
78461
78519
|
}
|
|
78462
78520
|
}
|
|
78463
78521
|
|
|
78522
|
+
class SGCacheKey {
|
|
78523
|
+
geometry = -1;
|
|
78524
|
+
material = -1;
|
|
78525
|
+
/**
|
|
78526
|
+
*
|
|
78527
|
+
* @type {DrawMode}
|
|
78528
|
+
*/
|
|
78529
|
+
mode = DrawMode.Triangles;
|
|
78530
|
+
|
|
78531
|
+
flags = 0;
|
|
78532
|
+
|
|
78533
|
+
hash(){
|
|
78534
|
+
return this.geometry ^ this.material;
|
|
78535
|
+
}
|
|
78536
|
+
|
|
78537
|
+
/**
|
|
78538
|
+
*
|
|
78539
|
+
* @param {SGCacheKey} other
|
|
78540
|
+
* @returns {boolean}
|
|
78541
|
+
*/
|
|
78542
|
+
equals(other){
|
|
78543
|
+
return this.geometry === other.geometry
|
|
78544
|
+
&& this.material === other.material
|
|
78545
|
+
&& this.mode === other.mode
|
|
78546
|
+
&& this.flags === other.flags;
|
|
78547
|
+
}
|
|
78548
|
+
|
|
78549
|
+
/**
|
|
78550
|
+
*
|
|
78551
|
+
* @param {SGCacheKey} other
|
|
78552
|
+
*/
|
|
78553
|
+
copy(other){
|
|
78554
|
+
this.geometry = other.geometry;
|
|
78555
|
+
this.material = other.material;
|
|
78556
|
+
this.mode = other.mode;
|
|
78557
|
+
this.flags = other.flags;
|
|
78558
|
+
}
|
|
78559
|
+
|
|
78560
|
+
clone(){
|
|
78561
|
+
const r = new SGCacheKey();
|
|
78562
|
+
|
|
78563
|
+
r.copy(this);
|
|
78564
|
+
|
|
78565
|
+
return r;
|
|
78566
|
+
}
|
|
78567
|
+
|
|
78568
|
+
/**
|
|
78569
|
+
*
|
|
78570
|
+
* @param {ShadedGeometry} sg
|
|
78571
|
+
*/
|
|
78572
|
+
fromSG(sg){
|
|
78573
|
+
this.geometry = sg.geometry.id;
|
|
78574
|
+
this.material = sg.material.id;
|
|
78575
|
+
this.mode = sg.mode;
|
|
78576
|
+
this.flags = sg.flags;
|
|
78577
|
+
}
|
|
78578
|
+
}
|
|
78579
|
+
|
|
78464
78580
|
const INSTANCED_EQUALITY_FLAGS = ShadedGeometryFlags.CastShadow
|
|
78465
78581
|
| ShadedGeometryFlags.ReceiveShadow
|
|
78466
78582
|
;
|
|
78467
78583
|
|
|
78584
|
+
/**
|
|
78585
|
+
* @readonly
|
|
78586
|
+
* @type {SGCacheKey}
|
|
78587
|
+
*/
|
|
78588
|
+
const scratch_key = new SGCacheKey();
|
|
78589
|
+
|
|
78468
78590
|
class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
78469
|
-
constructor() {
|
|
78470
|
-
super();
|
|
78471
78591
|
|
|
78472
|
-
|
|
78473
|
-
|
|
78474
|
-
|
|
78475
|
-
|
|
78476
|
-
|
|
78477
|
-
|
|
78478
|
-
|
|
78479
|
-
|
|
78480
|
-
|
|
78481
|
-
|
|
78482
|
-
|
|
78483
|
-
|
|
78484
|
-
|
|
78485
|
-
return a.material.id === b.material.id
|
|
78486
|
-
&& a.geometry.id === b.geometry.id
|
|
78487
|
-
&& a.mode === b.mode
|
|
78488
|
-
&& (a.flags & INSTANCED_EQUALITY_FLAGS) === (b.flags & INSTANCED_EQUALITY_FLAGS)
|
|
78489
|
-
;
|
|
78490
|
-
}
|
|
78491
|
-
});
|
|
78592
|
+
/**
|
|
78593
|
+
*
|
|
78594
|
+
* @type {HashMap<SGCacheKey, InstancedMeshGroup>}
|
|
78595
|
+
* @private
|
|
78596
|
+
*/
|
|
78597
|
+
__instanced_meshes = new HashMap();
|
|
78598
|
+
|
|
78599
|
+
/**
|
|
78600
|
+
*
|
|
78601
|
+
* @type {Set<InstancedMeshGroup>}
|
|
78602
|
+
* @private
|
|
78603
|
+
*/
|
|
78604
|
+
__active_meshes = new Set();
|
|
78492
78605
|
|
|
78493
|
-
/**
|
|
78494
|
-
*
|
|
78495
|
-
* @type {Set<InstancedMeshGroup>}
|
|
78496
|
-
* @private
|
|
78497
|
-
*/
|
|
78498
|
-
this.__active_meshes = new Set();
|
|
78499
|
-
}
|
|
78500
78606
|
|
|
78501
78607
|
score(geometry, material, instance_count) {
|
|
78502
78608
|
if (instance_count > 16) {
|
|
@@ -78515,7 +78621,12 @@ class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
|
78515
78621
|
* @private
|
|
78516
78622
|
*/
|
|
78517
78623
|
__get_instanced_mesh(sg) {
|
|
78518
|
-
|
|
78624
|
+
scratch_key.fromSG(sg);
|
|
78625
|
+
|
|
78626
|
+
// mask out some flags
|
|
78627
|
+
scratch_key.flags &= INSTANCED_EQUALITY_FLAGS;
|
|
78628
|
+
|
|
78629
|
+
let im = this.__instanced_meshes.get(scratch_key);
|
|
78519
78630
|
|
|
78520
78631
|
|
|
78521
78632
|
if (im !== undefined) {
|
|
@@ -78536,7 +78647,7 @@ class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
|
78536
78647
|
im.mesh.castShadow = sg.getFlag(ShadedGeometryFlags.CastShadow);
|
|
78537
78648
|
im.mesh.receiveShadow = sg.getFlag(ShadedGeometryFlags.ReceiveShadow);
|
|
78538
78649
|
|
|
78539
|
-
this.__instanced_meshes.set(
|
|
78650
|
+
this.__instanced_meshes.set(scratch_key.clone(), im);
|
|
78540
78651
|
|
|
78541
78652
|
return im;
|
|
78542
78653
|
|
|
@@ -78588,8 +78699,8 @@ class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
|
78588
78699
|
|
|
78589
78700
|
const meshes = this.__active_meshes;
|
|
78590
78701
|
|
|
78591
|
-
for (const
|
|
78592
|
-
|
|
78702
|
+
for (const mesh of meshes) {
|
|
78703
|
+
mesh.setCount(0);
|
|
78593
78704
|
}
|
|
78594
78705
|
|
|
78595
78706
|
meshes.clear();
|
package/package.json
CHANGED
package/src/core/cache/Cache.js
CHANGED
|
@@ -440,6 +440,18 @@ export class Cache {
|
|
|
440
440
|
|
|
441
441
|
this.weight = 0;
|
|
442
442
|
}
|
|
443
|
+
|
|
444
|
+
/**
|
|
445
|
+
*
|
|
446
|
+
* @param {function(message:string, key:Key, value:Value)} callback
|
|
447
|
+
* @param {*} [thisArg]
|
|
448
|
+
* @returns {boolean}
|
|
449
|
+
*/
|
|
450
|
+
validate(callback, thisArg) {
|
|
451
|
+
return this.data.verifyHashes((msg, key, entry) => {
|
|
452
|
+
callback.call(thisArg, msg, key, entry.value);
|
|
453
|
+
});
|
|
454
|
+
}
|
|
443
455
|
}
|
|
444
456
|
|
|
445
457
|
/**
|
|
@@ -14,11 +14,13 @@ class MapEntry {
|
|
|
14
14
|
* @param {number} hash
|
|
15
15
|
*/
|
|
16
16
|
constructor(key, value, hash) {
|
|
17
|
+
|
|
17
18
|
/**
|
|
18
19
|
*
|
|
19
20
|
* @type {K}
|
|
20
21
|
*/
|
|
21
22
|
this.key = key;
|
|
23
|
+
|
|
22
24
|
/**
|
|
23
25
|
*
|
|
24
26
|
* @type {V}
|
|
@@ -30,6 +32,7 @@ class MapEntry {
|
|
|
30
32
|
* @type {number}
|
|
31
33
|
*/
|
|
32
34
|
this.hash = hash;
|
|
35
|
+
|
|
33
36
|
}
|
|
34
37
|
}
|
|
35
38
|
|
|
@@ -45,6 +48,13 @@ const DEFAULT_INITIAL_CAPACITY = 16;
|
|
|
45
48
|
*/
|
|
46
49
|
const DEFAULT_LOAD_FACTOR = 0.75;
|
|
47
50
|
|
|
51
|
+
/**
|
|
52
|
+
* Multiply previous size by this number when growing the table
|
|
53
|
+
* @readonly
|
|
54
|
+
* @type {number}
|
|
55
|
+
*/
|
|
56
|
+
const GROWTH_FACTOR = 2;
|
|
57
|
+
|
|
48
58
|
/**
|
|
49
59
|
* Implements part of {@link Map} interface
|
|
50
60
|
* @copyright Alex Goldring (c) 2023
|
|
@@ -69,7 +79,9 @@ export class HashMap {
|
|
|
69
79
|
assert.isFunction(keyHashFunction, 'keyHashFunction');
|
|
70
80
|
assert.isFunction(keyEqualityFunction, 'keyEqualityFunction');
|
|
71
81
|
assert.isNonNegativeInteger(capacity, 'capacity');
|
|
82
|
+
|
|
72
83
|
assert.isNumber(loadFactor, 'loadFactor');
|
|
84
|
+
assert.greaterThan(loadFactor, 0, 'loadFactor must be > 0');
|
|
73
85
|
|
|
74
86
|
/**
|
|
75
87
|
*
|
|
@@ -182,6 +194,8 @@ export class HashMap {
|
|
|
182
194
|
* @private
|
|
183
195
|
*/
|
|
184
196
|
__compute_bucket_index(hash) {
|
|
197
|
+
assert.isInteger(hash, 'hash');
|
|
198
|
+
|
|
185
199
|
// mix the input hash to minimize potential impact of poor hash function spread
|
|
186
200
|
const mixed_hash = (hash >>> 16) ^ hash;
|
|
187
201
|
|
|
@@ -245,18 +259,19 @@ export class HashMap {
|
|
|
245
259
|
|
|
246
260
|
}
|
|
247
261
|
|
|
248
|
-
bucket.push(new MapEntry(key, value,raw_hash));
|
|
262
|
+
bucket.push(new MapEntry(key, value, raw_hash));
|
|
249
263
|
|
|
250
264
|
const old_size = this.size;
|
|
251
265
|
const new_size = old_size + 1;
|
|
252
266
|
this.size = new_size;
|
|
253
267
|
|
|
254
268
|
// compute actual current load
|
|
255
|
-
const
|
|
269
|
+
const bucket_count = this.__bucket_count;
|
|
270
|
+
const load = new_size / bucket_count;
|
|
256
271
|
|
|
257
272
|
if (load > this.__load_factor) {
|
|
258
273
|
// current load is too high, increase table size
|
|
259
|
-
this.setBucketCount(
|
|
274
|
+
this.setBucketCount(bucket_count * GROWTH_FACTOR);
|
|
260
275
|
}
|
|
261
276
|
}
|
|
262
277
|
|
|
@@ -381,8 +396,11 @@ export class HashMap {
|
|
|
381
396
|
/**
|
|
382
397
|
*
|
|
383
398
|
* @param {function(message:string, key:K, value:V)} callback
|
|
399
|
+
* @param {*} [thisArg]
|
|
400
|
+
* @returns {boolean}
|
|
384
401
|
*/
|
|
385
|
-
verifyHashes(callback) {
|
|
402
|
+
verifyHashes(callback, thisArg) {
|
|
403
|
+
let all_hashes_valid = true;
|
|
386
404
|
|
|
387
405
|
let h, i;
|
|
388
406
|
|
|
@@ -408,15 +426,25 @@ export class HashMap {
|
|
|
408
426
|
//check hash
|
|
409
427
|
const raw_hash = this.keyHashFunction(entry.key);
|
|
410
428
|
|
|
429
|
+
if (entry.hash !== raw_hash) {
|
|
430
|
+
callback.call(thisArg, `Hash stored on the entry(=${entry.hash}) is different from the computed key hash(=${raw_hash}).`, entry.key, entry.value)
|
|
431
|
+
|
|
432
|
+
all_hashes_valid = false;
|
|
433
|
+
}
|
|
434
|
+
|
|
411
435
|
const actual_bucket_index = this.__compute_bucket_index(raw_hash);
|
|
412
436
|
|
|
413
437
|
const stored_bucket_index = parseInt(h);
|
|
414
438
|
|
|
415
439
|
if (actual_bucket_index !== stored_bucket_index) {
|
|
416
|
-
callback(`Hash of key has changed. old=${stored_bucket_index}, new=${actual_bucket_index}`, entry.key, entry.value);
|
|
440
|
+
callback.call(thisArg, `Hash of key has changed. old=${stored_bucket_index}, new=${actual_bucket_index}`, entry.key, entry.value);
|
|
441
|
+
|
|
442
|
+
all_hashes_valid = false;
|
|
417
443
|
}
|
|
418
444
|
}
|
|
419
445
|
}
|
|
446
|
+
|
|
447
|
+
return all_hashes_valid;
|
|
420
448
|
}
|
|
421
449
|
|
|
422
450
|
/**
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { HashMap } from "./HashMap.js";
|
|
2
|
+
import { passThrough, strictEquals } from "../function/Functions.js";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
*
|
|
5
6
|
* @returns {HashMap}
|
|
6
7
|
*/
|
|
7
|
-
function
|
|
8
|
+
function makeSample() {
|
|
8
9
|
return new HashMap({
|
|
9
10
|
keyHashFunction(k) {
|
|
10
11
|
return 1;
|
|
@@ -16,7 +17,7 @@ function makeA() {
|
|
|
16
17
|
}
|
|
17
18
|
|
|
18
19
|
test('put and get same value', () => {
|
|
19
|
-
const m =
|
|
20
|
+
const m = makeSample();
|
|
20
21
|
|
|
21
22
|
m.set("hey", 42);
|
|
22
23
|
|
|
@@ -26,7 +27,7 @@ test('put and get same value', () => {
|
|
|
26
27
|
});
|
|
27
28
|
|
|
28
29
|
test("map has a key", () => {
|
|
29
|
-
const m =
|
|
30
|
+
const m = makeSample();
|
|
30
31
|
|
|
31
32
|
expect(m.has('cat')).toBe(false);
|
|
32
33
|
|
|
@@ -38,7 +39,7 @@ test("map has a key", () => {
|
|
|
38
39
|
test("put 2 values with same hash", () => {
|
|
39
40
|
|
|
40
41
|
|
|
41
|
-
const m =
|
|
42
|
+
const m = makeSample();
|
|
42
43
|
|
|
43
44
|
m.set("a", 7);
|
|
44
45
|
m.set("b", 42);
|
|
@@ -48,3 +49,28 @@ test("put 2 values with same hash", () => {
|
|
|
48
49
|
expect(m.get("a")).toBe(7);
|
|
49
50
|
expect(m.get("b")).toBe(42);
|
|
50
51
|
});
|
|
52
|
+
|
|
53
|
+
test("retrieval works as intended after hash map grows", () => {
|
|
54
|
+
const m = new HashMap({
|
|
55
|
+
keyHashFunction: passThrough,
|
|
56
|
+
keyEqualityFunction: strictEquals,
|
|
57
|
+
capacity: 1,
|
|
58
|
+
loadFactor: 1
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
m.set(1, "a");
|
|
62
|
+
|
|
63
|
+
expect(m.get(1)).toEqual("a")
|
|
64
|
+
|
|
65
|
+
m.set(150, "b");
|
|
66
|
+
|
|
67
|
+
expect(m.get(1)).toEqual("a");
|
|
68
|
+
expect(m.get(150)).toEqual("b");
|
|
69
|
+
|
|
70
|
+
m.set(-7, "c");
|
|
71
|
+
|
|
72
|
+
expect(m.get(1)).toEqual("a");
|
|
73
|
+
expect(m.get(150)).toEqual("b");
|
|
74
|
+
expect(m.get(-7)).toEqual("c");
|
|
75
|
+
|
|
76
|
+
});
|
|
@@ -96,6 +96,24 @@ export class StaticMaterialCache {
|
|
|
96
96
|
return material;
|
|
97
97
|
|
|
98
98
|
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
*
|
|
102
|
+
* @param {function(string)} error_consumer
|
|
103
|
+
* @param {*} [thisArg]
|
|
104
|
+
* @returns {boolean}
|
|
105
|
+
*/
|
|
106
|
+
validate(error_consumer, thisArg) {
|
|
107
|
+
const materials_valid = this.materialCache.validate((message, key, value) => {
|
|
108
|
+
error_consumer.call(thisArg, `Material error: ${message}. Material ${key}`);
|
|
109
|
+
});
|
|
110
|
+
const textures_valid = this.textureCache.validate((message, key, value) => {
|
|
111
|
+
error_consumer.call(thisArg, `Texture error: ${message}. Texture ${key}`);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return materials_valid
|
|
115
|
+
&& textures_valid;
|
|
116
|
+
}
|
|
99
117
|
}
|
|
100
118
|
|
|
101
119
|
/**
|
|
@@ -3,43 +3,34 @@ import { HashMap } from "../../../../../../core/collection/HashMap.js";
|
|
|
3
3
|
import { InstancedMeshGroup } from "../../../../geometry/instancing/InstancedMeshGroup.js";
|
|
4
4
|
import { ShadedGeometryFlags } from "../../ShadedGeometryFlags.js";
|
|
5
5
|
import { StreamDrawUsage } from "three";
|
|
6
|
+
import { SGCacheKey } from "./SGCacheKey.js";
|
|
6
7
|
|
|
7
8
|
const INSTANCED_EQUALITY_FLAGS = ShadedGeometryFlags.CastShadow
|
|
8
9
|
| ShadedGeometryFlags.ReceiveShadow
|
|
9
10
|
;
|
|
10
11
|
|
|
12
|
+
/**
|
|
13
|
+
* @readonly
|
|
14
|
+
* @type {SGCacheKey}
|
|
15
|
+
*/
|
|
16
|
+
const scratch_key = new SGCacheKey();
|
|
17
|
+
|
|
11
18
|
export class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
12
|
-
constructor() {
|
|
13
|
-
super();
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
*
|
|
17
|
-
* @type {HashMap<ShadedGeometry, InstancedMeshGroup>}
|
|
18
|
-
* @private
|
|
19
|
-
*/
|
|
20
|
-
this.__instanced_meshes = new HashMap({
|
|
21
|
-
keyHashFunction(sg) {
|
|
22
|
-
return sg.geometry.id
|
|
23
|
-
^ sg.material.id
|
|
24
|
-
^ sg.mode
|
|
25
|
-
;
|
|
26
|
-
},
|
|
27
|
-
keyEqualityFunction(a, b) {
|
|
28
|
-
return a.material.id === b.material.id
|
|
29
|
-
&& a.geometry.id === b.geometry.id
|
|
30
|
-
&& a.mode === b.mode
|
|
31
|
-
&& (a.flags & INSTANCED_EQUALITY_FLAGS) === (b.flags & INSTANCED_EQUALITY_FLAGS)
|
|
32
|
-
;
|
|
33
|
-
}
|
|
34
|
-
});
|
|
35
19
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
20
|
+
/**
|
|
21
|
+
*
|
|
22
|
+
* @type {HashMap<SGCacheKey, InstancedMeshGroup>}
|
|
23
|
+
* @private
|
|
24
|
+
*/
|
|
25
|
+
__instanced_meshes = new HashMap();
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
*
|
|
29
|
+
* @type {Set<InstancedMeshGroup>}
|
|
30
|
+
* @private
|
|
31
|
+
*/
|
|
32
|
+
__active_meshes = new Set();
|
|
33
|
+
|
|
43
34
|
|
|
44
35
|
score(geometry, material, instance_count) {
|
|
45
36
|
if (instance_count > 16) {
|
|
@@ -58,7 +49,12 @@ export class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
|
58
49
|
* @private
|
|
59
50
|
*/
|
|
60
51
|
__get_instanced_mesh(sg) {
|
|
61
|
-
|
|
52
|
+
scratch_key.fromSG(sg);
|
|
53
|
+
|
|
54
|
+
// mask out some flags
|
|
55
|
+
scratch_key.flags &= INSTANCED_EQUALITY_FLAGS
|
|
56
|
+
|
|
57
|
+
let im = this.__instanced_meshes.get(scratch_key);
|
|
62
58
|
|
|
63
59
|
|
|
64
60
|
if (im !== undefined) {
|
|
@@ -79,7 +75,7 @@ export class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
|
79
75
|
im.mesh.castShadow = sg.getFlag(ShadedGeometryFlags.CastShadow);
|
|
80
76
|
im.mesh.receiveShadow = sg.getFlag(ShadedGeometryFlags.ReceiveShadow);
|
|
81
77
|
|
|
82
|
-
this.__instanced_meshes.set(
|
|
78
|
+
this.__instanced_meshes.set(scratch_key.clone(), im);
|
|
83
79
|
|
|
84
80
|
return im;
|
|
85
81
|
|
|
@@ -131,8 +127,8 @@ export class InstancedRendererAdapter extends AbstractRenderAdapter {
|
|
|
131
127
|
|
|
132
128
|
const meshes = this.__active_meshes;
|
|
133
129
|
|
|
134
|
-
for (const
|
|
135
|
-
|
|
130
|
+
for (const mesh of meshes) {
|
|
131
|
+
mesh.setCount(0);
|
|
136
132
|
}
|
|
137
133
|
|
|
138
134
|
meshes.clear();
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { DrawMode } from "../../DrawMode.js";
|
|
2
|
+
|
|
3
|
+
export class SGCacheKey {
|
|
4
|
+
geometry = -1;
|
|
5
|
+
material = -1;
|
|
6
|
+
/**
|
|
7
|
+
*
|
|
8
|
+
* @type {DrawMode}
|
|
9
|
+
*/
|
|
10
|
+
mode = DrawMode.Triangles;
|
|
11
|
+
|
|
12
|
+
flags = 0;
|
|
13
|
+
|
|
14
|
+
hash(){
|
|
15
|
+
return this.geometry ^ this.material;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
*
|
|
20
|
+
* @param {SGCacheKey} other
|
|
21
|
+
* @returns {boolean}
|
|
22
|
+
*/
|
|
23
|
+
equals(other){
|
|
24
|
+
return this.geometry === other.geometry
|
|
25
|
+
&& this.material === other.material
|
|
26
|
+
&& this.mode === other.mode
|
|
27
|
+
&& this.flags === other.flags;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
*
|
|
32
|
+
* @param {SGCacheKey} other
|
|
33
|
+
*/
|
|
34
|
+
copy(other){
|
|
35
|
+
this.geometry = other.geometry;
|
|
36
|
+
this.material = other.material;
|
|
37
|
+
this.mode = other.mode;
|
|
38
|
+
this.flags = other.flags;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
clone(){
|
|
42
|
+
const r = new SGCacheKey();
|
|
43
|
+
|
|
44
|
+
r.copy(this);
|
|
45
|
+
|
|
46
|
+
return r;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
*
|
|
51
|
+
* @param {ShadedGeometry} sg
|
|
52
|
+
*/
|
|
53
|
+
fromSG(sg){
|
|
54
|
+
this.geometry = sg.geometry.id;
|
|
55
|
+
this.material = sg.material.id;
|
|
56
|
+
this.mode = sg.mode;
|
|
57
|
+
this.flags = sg.flags;
|
|
58
|
+
}
|
|
59
|
+
}
|