@woosh/meep-engine 2.41.0 → 2.42.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (98) hide show
  1. package/core/assert.js +2 -2
  2. package/core/collection/array/array_swap.js +3 -3
  3. package/core/collection/map/AsyncLoadingCache.js +47 -0
  4. package/core/geom/3d/aabb/aabb3_compute_distance_above_plane_max.js +1 -1
  5. package/core/geom/3d/apply_mat4_transform_to_v3_array.js +2 -4
  6. package/core/geom/3d/sphere/sphere_radius_sqr_from_v3_array_transformed.js +28 -0
  7. package/core/geom/Quaternion.js +14 -0
  8. package/core/math/statistics/computeSampleSize_Cochran.js +3 -3
  9. package/editor/ecs/component/editors/geom/QuaternionEditor.js +12 -5
  10. package/engine/Engine.js +6 -1
  11. package/engine/EngineBootstrapper.js +2 -1
  12. package/engine/EngineHarness.js +13 -3
  13. package/engine/asset/AssetManager.js +97 -7
  14. package/engine/development/performance/AbstractMetric.js +1 -0
  15. package/engine/development/performance/RingBufferMetric.js +25 -4
  16. package/engine/ecs/EntityBuilder.js +29 -4
  17. package/engine/ecs/transform/Transform.js +23 -3
  18. package/engine/graphics/ecs/camera/topdown/TopDownCameraControllerSystem.js +17 -1
  19. package/engine/graphics/ecs/decal/v2/Decal.d.ts +11 -0
  20. package/engine/graphics/ecs/decal/v2/Decal.js +50 -0
  21. package/engine/graphics/ecs/decal/v2/FPDecalSystem.d.ts +8 -0
  22. package/engine/graphics/ecs/decal/v2/FPDecalSystem.js +201 -0
  23. package/engine/graphics/ecs/decal/v2/prototypeDecalSystem.js +278 -0
  24. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +8 -1
  25. package/engine/graphics/ecs/mesh-v2/allocate_v3.js +37 -0
  26. package/engine/graphics/ecs/mesh-v2/build_three_object.js +4 -0
  27. package/engine/graphics/geometry/MikkT/AddTriToGroup.js +10 -0
  28. package/engine/graphics/geometry/MikkT/AssignRecur.js +84 -0
  29. package/engine/graphics/geometry/MikkT/AvgTSpace.js +38 -0
  30. package/engine/graphics/geometry/MikkT/Build4RuleGroups.js +96 -0
  31. package/engine/graphics/geometry/MikkT/BuildNeighborsFast.js +137 -0
  32. package/engine/graphics/geometry/MikkT/CalcTexArea.js +31 -0
  33. package/engine/graphics/geometry/MikkT/CompareSubGroups.js +26 -0
  34. package/engine/graphics/geometry/MikkT/DegenEpilogue.js +220 -0
  35. package/engine/graphics/geometry/MikkT/DegenPrologue.js +115 -0
  36. package/engine/graphics/geometry/MikkT/EvalTspace.js +128 -0
  37. package/engine/graphics/geometry/MikkT/GenerateInitialVerticesIndexList.js +48 -0
  38. package/engine/graphics/geometry/MikkT/GenerateSharedVerticesIndexList.js +184 -0
  39. package/engine/graphics/geometry/MikkT/GenerateTSpaces.js +226 -0
  40. package/engine/graphics/geometry/MikkT/GetEdge.js +45 -0
  41. package/engine/graphics/geometry/MikkT/GetNormal.js +16 -0
  42. package/engine/graphics/geometry/MikkT/GetPosition.js +25 -0
  43. package/engine/graphics/geometry/MikkT/GetTexCoord.js +18 -0
  44. package/engine/graphics/geometry/MikkT/InitTriInfo.js +180 -0
  45. package/engine/graphics/geometry/MikkT/Length.js +10 -0
  46. package/engine/graphics/geometry/MikkT/MakeIndex.js +18 -0
  47. package/engine/graphics/geometry/MikkT/MikkTSpace.js +197 -2068
  48. package/engine/graphics/geometry/MikkT/NormalizeSafe.js +21 -0
  49. package/engine/graphics/geometry/MikkT/NotZero.js +10 -0
  50. package/engine/graphics/geometry/MikkT/QuickSort.js +54 -0
  51. package/engine/graphics/geometry/MikkT/QuickSortEdges.js +71 -0
  52. package/engine/graphics/geometry/MikkT/SSubGroup.js +15 -0
  53. package/engine/graphics/geometry/MikkT/STSpace.js +36 -0
  54. package/engine/graphics/geometry/MikkT/constants/GROUP_WITH_ANY.js +1 -0
  55. package/engine/graphics/geometry/MikkT/constants/INTERNAL_RND_SORT_SEED.js +1 -0
  56. package/engine/graphics/geometry/MikkT/constants/MARK_DEGENERATE.js +1 -0
  57. package/engine/graphics/geometry/MikkT/constants/ORIENT_PRESERVING.js +1 -0
  58. package/engine/graphics/geometry/MikkT/constants/QUAD_ONE_DEGEN_TRI.js +1 -0
  59. package/engine/graphics/geometry/MikkT/m_getNormal.js +16 -0
  60. package/engine/graphics/geometry/MikkT/m_getNumFaces.js +8 -0
  61. package/engine/graphics/geometry/MikkT/m_getNumVerticesOfFace.js +11 -0
  62. package/engine/graphics/geometry/MikkT/m_getPosition.js +20 -0
  63. package/engine/graphics/geometry/MikkT/m_getTexCoord.js +16 -0
  64. package/engine/graphics/geometry/MikkT/m_setTSpace.js +35 -0
  65. package/engine/graphics/geometry/MikkT/m_setTSpaceBasic.js +22 -0
  66. package/engine/graphics/geometry/MikkT/malloc.js +16 -0
  67. package/engine/graphics/geometry/MikkT/v3_scale_dot_sub_normalize.js +52 -0
  68. package/engine/graphics/geometry/buffered/computeGeometryEquality.js +1 -1
  69. package/engine/graphics/geometry/buffered/computeGeometryHash.js +1 -1
  70. package/engine/graphics/impostors/octahedral/ImpostorBaker.js +28 -14
  71. package/engine/graphics/impostors/octahedral/ImpostorDescription.js +6 -0
  72. package/engine/graphics/impostors/octahedral/README.md +1 -0
  73. package/engine/graphics/impostors/octahedral/bake/compute_bounding_sphere.js +25 -22
  74. package/engine/graphics/impostors/octahedral/bake/compute_bounding_sphere_radius_only.js +37 -0
  75. package/engine/graphics/impostors/octahedral/bake/prepare_bake_material.js +30 -1
  76. package/engine/graphics/impostors/octahedral/grid/HemiOctahedralUvEncoder.js +1 -1
  77. package/engine/graphics/impostors/octahedral/prototypeBaker.js +121 -22
  78. package/engine/graphics/impostors/octahedral/shader/BakeShaderStandard.js +46 -7
  79. package/engine/graphics/impostors/octahedral/shader/ImpostorShaderV0.js +349 -0
  80. package/engine/graphics/impostors/octahedral/shader/ImpostorShaderV1.js +74 -0
  81. package/engine/graphics/impostors/octahedral/shader/glsl/v1/common.glsl +209 -0
  82. package/engine/graphics/impostors/octahedral/shader/glsl/v1/flagment.glsl +80 -0
  83. package/engine/graphics/impostors/octahedral/shader/glsl/v1/vertex.glsl +350 -0
  84. package/engine/graphics/micron/render/v1/getTransformedPositionsCached.js +1 -1
  85. package/engine/graphics/render/forward_plus/LightManager.js +17 -7
  86. package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +7 -1
  87. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +13 -5
  88. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_PREAMBLE.js +3 -1
  89. package/engine/graphics/render/forward_plus/model/Decal.js +19 -2
  90. package/engine/graphics/render/forward_plus/plugin/MaterialTransformer.js +14 -2
  91. package/engine/graphics/render/forward_plus/query/query_bvh_frustum_from_texture.js +2 -2
  92. package/engine/graphics/texture/sampler/Sampler2D.js +10 -10
  93. package/engine/graphics/texture/sampler/prototypeSamplerFiltering.js +117 -11
  94. package/engine/graphics/texture/sampler/resize/sampler2d_downsample_mipmap.js +66 -0
  95. package/engine/graphics/texture/sampler/sampler2_d_scale_down_lanczos.js +2 -2
  96. package/engine/intelligence/behavior/util/RotationBehavior.js +69 -0
  97. package/generation/example/filters/SampleGroundMoistureFilter.js +5 -5
  98. package/package.json +1 -1
package/core/assert.js CHANGED
@@ -167,8 +167,8 @@ function arrayHasNo(haystack, needle, message = 'Array contains the item') {
167
167
 
168
168
  /**
169
169
  * @template T
170
- * @param {T[]|ArrayLike<T>|Float32Array} a
171
- * @param {T[]|ArrayLike<T>|Float32Array} b
170
+ * @param {T[]|ArrayLike<T>|Float32Array|Int32Array} a
171
+ * @param {T[]|ArrayLike<T>|Float32Array|Int32Array} b
172
172
  * @param {string} [message]
173
173
  */
174
174
  function arrayEqual(a, b, message = 'Arrays are not equal') {
@@ -1,8 +1,8 @@
1
1
  /**
2
- *
3
- * @param {T[]|ArrayLike<T>|TypedArray|Uint8ClampedArray|Uint8Array} a
2
+ * @template T
3
+ * @param {T[]|ArrayLike<T>|TypedArray|Uint8ClampedArray|Uint8Array|Float32Array} a
4
4
  * @param {number} a_offset
5
- * @param {T[]|ArrayLike<T>|TypedArray|Uint8ClampedArray|Uint8Array} b
5
+ * @param {T[]|ArrayLike<T>|TypedArray|Uint8ClampedArray|Uint8Array|Float32Array} b
6
6
  * @param {number} b_offset
7
7
  * @param {number} length How many elements should be moved
8
8
  */
@@ -0,0 +1,47 @@
1
+ import { AbstractAsyncMap } from "./AbstractAsyncMap.js";
2
+
3
+ /**
4
+ * Cache wrapper that loads data when it's not found in the cache
5
+ */
6
+ export class AsyncLoadingCache extends AbstractAsyncMap {
7
+ /**
8
+ *
9
+ * @param {Map} cache
10
+ * @param {function(K):Promise<T>} loader
11
+ */
12
+ constructor(cache, loader) {
13
+ super();
14
+
15
+ this.__cache = cache;
16
+ this.__loader = loader;
17
+ this.__pending = new Map();
18
+ }
19
+
20
+ async get(key) {
21
+
22
+ const cached = this.__cache.get(key);
23
+
24
+ if (cached !== undefined) {
25
+ return cached;
26
+ }
27
+
28
+ let promise = this.__pending.get(key);
29
+ let original_loader = false;
30
+
31
+ if (promise === undefined) {
32
+ promise = this.__loader(key);
33
+
34
+ this.__pending.set(key, promise);
35
+
36
+ original_loader = true;
37
+ }
38
+
39
+ const value = await promise;
40
+
41
+ if (original_loader) {
42
+ this.__cache.get(key, value);
43
+ }
44
+
45
+ return value;
46
+ }
47
+ }
@@ -18,7 +18,7 @@ export function aabb3_computeDistanceAbovePlane_max(
18
18
  _x1, _y1, _z1
19
19
  ) {
20
20
 
21
- // pick a corner point nearest along the plane normal
21
+ // pick a corner point that is the closest along the plane normal
22
22
  const px = plane_normal_x > 0 ? _x1 : _x0;
23
23
  const py = plane_normal_y > 0 ? _y1 : _y0;
24
24
  const pz = plane_normal_z > 0 ? _z1 : _z0;
@@ -6,11 +6,9 @@
6
6
  * @param {number[]|Float32Array|Float64Array} destination
7
7
  * @param {number} destination_offset
8
8
  * @param {number} count
9
- * @param {THREE.Matrix4} matrix
9
+ * @param {mat4|number[]|Float32Array} mat4
10
10
  */
11
- export function apply_mat4_transform_to_v3_array(source, source_offset, destination, destination_offset, count, matrix) {
12
-
13
- const mat4 = matrix.elements;
11
+ export function apply_mat4_transform_to_v3_array(source, source_offset, destination, destination_offset, count, mat4) {
14
12
 
15
13
  const a0 = mat4[0];
16
14
  const a1 = mat4[1];
@@ -0,0 +1,28 @@
1
+ /**
2
+ *
3
+ * @param {number[]|ArrayLike<number>} input
4
+ * @param {number} input_length
5
+ * @param {ArrayLike<number>|number[]|Float32Array} e 4x4 transformation matrix
6
+ * @returns {number}
7
+ */
8
+ export function sphere_radius_sqr_from_v3_array_transformed(input, input_length, e) {
9
+ let result = 0;
10
+
11
+ for (let i = 0; i < input_length; i += 3) {
12
+ const x = input[i];
13
+ const y = input[i + 1];
14
+ const z = input[i + 2];
15
+
16
+ const _x = e[0] * x + e[4] * y + e[8] * z + e[12];
17
+ const _y = e[1] * x + e[5] * y + e[9] * z + e[13];
18
+ const _z = e[2] * x + e[6] * y + e[10] * z + e[14];
19
+
20
+ const l2 = _x * _x + _y * _y + _z * _z;
21
+
22
+ if (l2 > result) {
23
+ result = l2;
24
+ }
25
+ }
26
+
27
+ return result;
28
+ }
@@ -1705,6 +1705,20 @@ class Quaternion {
1705
1705
  return result;
1706
1706
  }
1707
1707
 
1708
+ /**
1709
+ *
1710
+ * @param {number} x
1711
+ * @param {number} y
1712
+ * @param {number} z
1713
+ * @returns {Quaternion}
1714
+ */
1715
+ static fromEulerAngles(x, y, z) {
1716
+ const r = new Quaternion();
1717
+
1718
+ r.fromEulerAnglesXYZ(x, y, z);
1719
+
1720
+ return r;
1721
+ }
1708
1722
 
1709
1723
  /**
1710
1724
  * Behaves similarly to Unity's Quaternion `RotateToward` method
@@ -12,9 +12,9 @@
12
12
  * 99% | 2.57583
13
13
  * 99.9% | 3.29053
14
14
  *
15
- * @param z Standard distribution value. # of standard deviations. See statistical tables
16
- * @param p Fraction of the population with attribute in question. For example if we want to estimate what time people have breakfast and we know that only 30% of people have have breakfast overall, p would be 0.3
17
- * @param e Error tolerance, 0.05 represents 5% error tolerance
15
+ * @param {number} z Standard distribution value. # of standard deviations. See statistical tables
16
+ * @param {number} p Fraction of the population with attribute in question. For example if we want to estimate what time people have breakfast and we know that only 30% of people have have breakfast overall, p would be 0.3
17
+ * @param {number} e Error tolerance, 0.05 represents 5% error tolerance
18
18
  * @returns {number} Sample size
19
19
  */
20
20
  export function computeSampleSize_Cochran(z, p, e) {
@@ -1,7 +1,8 @@
1
1
  import { TypeEditor } from "../../TypeEditor.js";
2
2
  import Vector3 from "../../../../../core/geom/Vector3.js";
3
- import { DEG2RAD, RAD2DEG } from "three/src/math/MathUtils.js";
4
3
  import { makeV3_editor } from "../common/makeV3_editor.js";
4
+ import { DEG_TO_RAD } from "../../../../../core/math/DEG_TO_RAD.js";
5
+ import { RAD_TO_DEG } from "../../../../../core/math/RAD_TO_DEG.js";
5
6
 
6
7
  export class QuaternionEditor extends TypeEditor {
7
8
  build(parent, field, registry) {
@@ -20,7 +21,13 @@ export class QuaternionEditor extends TypeEditor {
20
21
  }
21
22
 
22
23
  lock = true;
23
- q.__setFromEuler(euler.x * DEG2RAD, euler.y * DEG2RAD, euler.z * DEG2RAD);
24
+
25
+ q.__setFromEuler(
26
+ euler.x * DEG_TO_RAD,
27
+ euler.y * DEG_TO_RAD,
28
+ euler.z * DEG_TO_RAD
29
+ );
30
+
24
31
  lock = false;
25
32
  };
26
33
 
@@ -36,9 +43,9 @@ export class QuaternionEditor extends TypeEditor {
36
43
  q.toEulerAnglesXYZ(t);
37
44
 
38
45
  euler.set(
39
- t.x * RAD2DEG,
40
- t.y * RAD2DEG,
41
- t.z * RAD2DEG,
46
+ t.x * RAD_TO_DEG,
47
+ t.y * RAD_TO_DEG,
48
+ t.z * RAD_TO_DEG,
42
49
  );
43
50
 
44
51
  lock = false;
package/engine/Engine.js CHANGED
@@ -84,7 +84,7 @@ class Engine {
84
84
  *
85
85
  * @type {ConcurrentExecutor}
86
86
  */
87
- this.executor = new ConcurrentExecutor(0, 10);
87
+ this.executor = new ConcurrentExecutor(1, 10);
88
88
 
89
89
  this.__using_external_entity_manager = entityManager !== undefined;
90
90
 
@@ -412,6 +412,9 @@ class Engine {
412
412
  start() {
413
413
  const self = this;
414
414
 
415
+
416
+ this.assetManager.startup();
417
+
415
418
  function promiseEntityManager() {
416
419
  return new Promise(function (resolve, reject) {
417
420
  //initialize entity manager
@@ -488,6 +491,8 @@ class Engine {
488
491
 
489
492
 
490
493
  // TODO shutdown executor
494
+
495
+ await this.assetManager.shutdown();
491
496
  }
492
497
 
493
498
  exit() {
@@ -30,7 +30,8 @@ export class EngineBootstrapper {
30
30
 
31
31
  this.rootNode.appendChild(v.el);
32
32
 
33
- const started = engine.start();
33
+ // NOTE: the engine used to be started here, this was moved out
34
+ const started = Promise.resolve();
34
35
 
35
36
  const vExecutionEngineState = new LabelView('');
36
37
  const vAssetManagerState = new LabelView('');
@@ -153,6 +153,7 @@ export class EngineHarness {
153
153
  * @param {boolean} [autoClip]
154
154
  * @param {number} [distanceMin]
155
155
  * @param {number} [distanceMax]
156
+ * @param {number} [fieldOfView] in degrees
156
157
  * @returns {EntityBuilder}
157
158
  */
158
159
  static buildCamera(
@@ -163,9 +164,10 @@ export class EngineHarness {
163
164
  distance = 10,
164
165
  pitch = 1.4,
165
166
  yaw = 0,
166
- autoClip = true,
167
+ autoClip = false,
167
168
  distanceMin = 0,
168
- distanceMax = 1000
169
+ distanceMax = 1000,
170
+ fieldOfView = 45
169
171
  }
170
172
  ) {
171
173
  const em = engine.entityManager;
@@ -199,6 +201,8 @@ export class EngineHarness {
199
201
 
200
202
  camera.active.set(true);
201
203
  camera.autoClip = autoClip;
204
+ camera.clip_far = distanceMax;
205
+ camera.fov.set(fieldOfView);
202
206
 
203
207
  const entityBuilder = new EntityBuilder();
204
208
 
@@ -229,6 +233,8 @@ export class EngineHarness {
229
233
  * @param {boolean} [enableWater]
230
234
  * @param {boolean} [enableTerrain]
231
235
  * @param {boolean} [enableLights=true]
236
+ * @param {number} [cameraFieldOfView]
237
+ * @param {number} [cameraFarDistance]
232
238
  * @param {boolean} [cameraController=true]
233
239
  */
234
240
  static async buildBasics({
@@ -244,6 +250,8 @@ export class EngineHarness {
244
250
  enableWater = true,
245
251
  enableTerrain = true,
246
252
  enableLights = true,
253
+ cameraFieldOfView,
254
+ cameraFarDistance,
247
255
  cameraController = true
248
256
  }) {
249
257
 
@@ -256,7 +264,9 @@ export class EngineHarness {
256
264
  target: focus,
257
265
  pitch,
258
266
  yaw,
259
- distance
267
+ distance,
268
+ fieldOfView: cameraFieldOfView,
269
+ distanceMax: cameraFarDistance
260
270
  });
261
271
 
262
272
  const cameraEntity = camera.entity;
@@ -13,6 +13,9 @@ import { CrossOriginConfig } from "./CORS/CrossOriginConfig.js";
13
13
  import { array_remove_element } from "../../core/collection/array/array_remove_element.js";
14
14
  import { AssetDescription } from "./AssetDescription.js";
15
15
  import { noop } from "../../core/function/Functions.js";
16
+ import { Deque } from "../../core/collection/queue/Deque.js";
17
+ import Task from "../../core/process/task/Task.js";
18
+ import { TaskSignal } from "../../core/process/task/TaskSignal.js";
16
19
 
17
20
  class AssetRequest {
18
21
  /**
@@ -68,6 +71,18 @@ class PendingAsset {
68
71
  }
69
72
  }
70
73
 
74
+ class Response {
75
+ /**
76
+ *
77
+ * @param {Asset} asset
78
+ * @param {AssetRequest} request
79
+ */
80
+ constructor(asset, request) {
81
+ this.asset = asset;
82
+ this.request = request;
83
+ }
84
+ }
85
+
71
86
 
72
87
  /**
73
88
  * @class
@@ -139,6 +154,60 @@ export class AssetManager {
139
154
  * @type {boolean}
140
155
  */
141
156
  this.isAssetManager = true;
157
+
158
+ /**
159
+ *
160
+ * @type {Deque<Response>}
161
+ * @private
162
+ */
163
+ this.__response_queue = new Deque();
164
+
165
+ /**
166
+ *
167
+ * @type {Task}
168
+ * @private
169
+ */
170
+ this.__response_processor = new Task({
171
+ name: "Asset Manager Response processor",
172
+ cycleFunction: () => {
173
+ if (this.__response_queue.isEmpty()) {
174
+ return TaskSignal.Yield;
175
+ }
176
+
177
+ const response = this.__response_queue.removeFirst();
178
+
179
+ this.__process_response(response);
180
+
181
+ return TaskSignal.Continue;
182
+ }
183
+ });
184
+
185
+ this.__is_running = false;
186
+ }
187
+
188
+ startup() {
189
+ if (this.__is_running) {
190
+ return;
191
+ }
192
+
193
+ this.__engine.executor.run(this.__response_processor);
194
+
195
+ this.__is_running = true;
196
+ }
197
+
198
+ async shutdown(immediate = false) {
199
+ if (!this.__is_running) {
200
+ return;
201
+ }
202
+
203
+ if (!immediate) {
204
+ // wait until all responses have been processed
205
+ await Promise.allSettled([Task.promise(this.__response_processor)]);
206
+ }
207
+
208
+ this.__engine.executor.removeTask(this.__response_processor);
209
+
210
+ this.__is_running = false;
142
211
  }
143
212
 
144
213
  /**
@@ -215,6 +284,30 @@ export class AssetManager {
215
284
  this.assets.set(assetDescription, asset);
216
285
  }
217
286
 
287
+ /**
288
+ *
289
+ * @param {Response} response
290
+ * @private
291
+ */
292
+ __process_response(response) {
293
+
294
+ try {
295
+ response.request.successCallback(response.asset);
296
+ } catch (e) {
297
+ console.error("Failed to execute asset success callback", e);
298
+ }
299
+ }
300
+
301
+ /**
302
+ *
303
+ * @param {Asset} asset
304
+ * @param {AssetRequest} request
305
+ * @private
306
+ */
307
+ __schedule_response(asset, request) {
308
+ this.__response_queue.add(new Response(asset, request));
309
+ }
310
+
218
311
  /**
219
312
  *
220
313
  * @param {string} path
@@ -299,18 +392,15 @@ export class AssetManager {
299
392
  //register asset
300
393
  assets.set(assetDescription, asset);
301
394
 
395
+ //clear callbacks
396
+ requestMap.delete(assetDescription);
397
+
302
398
  // process callbacks
303
399
  for (let i = 0; i < requests.length; i++) {
304
400
  const request = requests[i];
305
- try {
306
- request.successCallback(asset);
307
- } catch (e) {
308
- console.error("Failed to execute asset success callback", e);
309
- }
401
+ this.__schedule_response(asset, request);
310
402
  }
311
403
 
312
- //clear callbacks
313
- requestMap.delete(assetDescription);
314
404
  }
315
405
 
316
406
  function failure(error) {
@@ -17,6 +17,7 @@ export class AbstractMetric {
17
17
  /**
18
18
  *
19
19
  * @param {MetricStatistics} result
20
+ * @returns {boolean} whether metric was successfully computed or not
20
21
  */
21
22
  computeStats(result) {
22
23
  throw new Error('Not implemented');
@@ -20,16 +20,37 @@ export class RingBufferMetric extends AbstractMetric {
20
20
  this.__data.push(value);
21
21
  }
22
22
 
23
+ /**
24
+ *
25
+ * @param {MetricStatistics} result
26
+ * @returns {boolean}
27
+ */
23
28
  computeStats(result) {
24
29
 
25
30
  const buffer = this.__data;
26
31
  const array = buffer.data;
27
32
  const data_count = buffer.count;
28
33
 
29
- result.mean = computeStatisticalMean(array, 0, data_count);
30
- result.meadian = computeStatisticalPartialMedian(array, 0, data_count);
31
- result.max = computeArrayMax(array, 0, data_count);
32
- result.min = computeArrayMin(array, 0, data_count);
34
+ if (data_count === 0) {
35
+
36
+ // no data
37
+ result.mean = 0;
38
+ result.meadian = 0;
39
+ result.min = 0;
40
+ result.max = 0;
41
+
42
+ return false;
43
+
44
+ } else {
45
+
46
+ result.mean = computeStatisticalMean(array, 0, data_count);
47
+ result.meadian = computeStatisticalPartialMedian(array, 0, data_count);
48
+ result.max = computeArrayMax(array, 0, data_count);
49
+ result.min = computeArrayMin(array, 0, data_count);
50
+
51
+ return true;
52
+
53
+ }
33
54
  }
34
55
 
35
56
  clear() {
@@ -8,11 +8,30 @@ import { isDefined } from "../../core/process/matcher/Matchers.js";
8
8
  * @enum
9
9
  */
10
10
  export const EntityBuilderFlags = {
11
+ /**
12
+ * Whether the entity is built, set internally
13
+ */
11
14
  Built: 1,
12
- RegisterComponents: 2
15
+ /**
16
+ * If component type is not registered on the {@link EntityComponentDataset} when calling {@link #build} - will register the component first
17
+ */
18
+ RegisterComponents: 2,
19
+ /**
20
+ * Entity builder will watch destruction of the entity (subscribe to event), this will make the EntityBuilder automatically
21
+ * recognize when the corresponding entity is destroyed outside the EntityBuilder API
22
+ * NOTE: useful to turn off to save a bit of memory, as those event listeners take up a bit of space. Feel free to turn this flag off when you don't care to keep the reference to the EntityBuilder
23
+ */
24
+ WatchDestruction: 4
13
25
  };
14
26
 
15
- const DEFAULT_FLAGS = EntityBuilderFlags.RegisterComponents;
27
+ /**
28
+ * Set of default flags
29
+ * @type {number}
30
+ */
31
+ const DEFAULT_FLAGS =
32
+ EntityBuilderFlags.RegisterComponents
33
+ | EntityBuilderFlags.WatchDestruction
34
+ ;
16
35
 
17
36
  /**
18
37
  * Representation of an entity, helps build entities and keep track of them without having to access {@link EntityComponentDataset} directly
@@ -53,6 +72,10 @@ class EntityBuilder {
53
72
  */
54
73
  this.dataset = null;
55
74
 
75
+ /**
76
+ *
77
+ * @type {EntityBuilderFlags|number}
78
+ */
56
79
  this.flags = DEFAULT_FLAGS;
57
80
 
58
81
  /**
@@ -132,7 +155,7 @@ class EntityBuilder {
132
155
  throw new Error("Can not add " + componentInstance + " to EntityBuilder");
133
156
  }
134
157
 
135
- assert.notOk(this.hasComponent(Object.getPrototypeOf(componentInstance).constructor) , 'Component of this type already exists');
158
+ assert.notOk(this.hasComponent(Object.getPrototypeOf(componentInstance).constructor), 'Component of this type already exists');
136
159
 
137
160
  this.element.push(componentInstance);
138
161
 
@@ -378,7 +401,9 @@ class EntityBuilder {
378
401
 
379
402
  this.setFlag(EntityBuilderFlags.Built);
380
403
 
381
- dataset.addEntityEventListener(entity, EventType.EntityRemoved, this.__handleEntityDestroyed, this);
404
+ if (this.getFlag(EntityBuilderFlags.WatchDestruction)) {
405
+ dataset.addEntityEventListener(entity, EventType.EntityRemoved, this.__handleEntityDestroyed, this);
406
+ }
382
407
 
383
408
  this.on.built.send2(entity, dataset);
384
409
  return entity;
@@ -63,9 +63,29 @@ export class Transform {
63
63
  this.flags = FLAGS_DEFAULT;
64
64
 
65
65
  // watch changes
66
- this.position.onChanged.add(this.__handle_component_change, this);
67
- this.rotation.onChanged.add(this.__handle_component_change, this);
68
- this.scale.onChanged.add(this.__handle_component_change, this);
66
+ this.subscribeAllChanges(this.__handle_component_change, this);
67
+ }
68
+
69
+ /**
70
+ *
71
+ * @param {function} handler
72
+ * @param {*} [thisArg]
73
+ */
74
+ subscribeAllChanges(handler, thisArg) {
75
+ this.position.onChanged.add(handler, thisArg);
76
+ this.rotation.onChanged.add(handler, thisArg);
77
+ this.scale.onChanged.add(handler, thisArg);
78
+ }
79
+
80
+ /**
81
+ *
82
+ * @param {function} handler
83
+ * @param {*} [thisArg]
84
+ */
85
+ unsubscribeAllChanges(handler, thisArg) {
86
+ this.position.onChanged.remove(handler, thisArg);
87
+ this.rotation.onChanged.remove(handler, thisArg);
88
+ this.scale.onChanged.scale(handler, thisArg);
69
89
  }
70
90
 
71
91
  /**
@@ -24,6 +24,22 @@ class TopDownCameraControllerSystem extends System {
24
24
 
25
25
  }
26
26
 
27
+ /**
28
+ *
29
+ * @param {TopDownCameraController} ctrl
30
+ * @param {number} entity
31
+ * @private
32
+ */
33
+ __visit_camera_entity(ctrl, entity){
34
+ const transform = this.entityManager.dataset.getComponent(entity,Transform);
35
+
36
+ if(transform === undefined){
37
+ return;
38
+ }
39
+
40
+ computeTopDownTransform(ctrl,transform);
41
+ }
42
+
27
43
  update(timeDelta) {
28
44
  const em = this.entityManager;
29
45
 
@@ -34,7 +50,7 @@ class TopDownCameraControllerSystem extends System {
34
50
  const dataset = em.dataset;
35
51
 
36
52
  if (this.enabled.get() && dataset !== null) {
37
- dataset.traverseEntities([TopDownCameraController, Transform], computeTopDownTransform);
53
+ dataset.traverseComponents(TopDownCameraController, this.__visit_camera_entity, this);
38
54
  }
39
55
 
40
56
  }
@@ -0,0 +1,11 @@
1
+ export class Decal {
2
+ /**
3
+ * Path to image
4
+ */
5
+ uri: string
6
+
7
+ /**
8
+ * Draw order, higher values will be drawn on top of lower values when there's an overlap
9
+ */
10
+ priority: number
11
+ }
@@ -0,0 +1,50 @@
1
+ export class Decal {
2
+ constructor() {
3
+ /**
4
+ * Asset URL
5
+ * @type {string}
6
+ */
7
+ this.uri = "";
8
+
9
+ /**
10
+ * Controls draw order
11
+ * @type {number}
12
+ */
13
+ this.priority = 0;
14
+
15
+ /**
16
+ * Internal transient reference to loaded asset
17
+ * @type {Sampler2D|null}
18
+ * @private
19
+ */
20
+ this.__cached_sampler = null;
21
+ /**
22
+ *
23
+ * @type {String|null}
24
+ * @private
25
+ */
26
+ this.__cached_uri = null;
27
+ }
28
+
29
+ toJSON() {
30
+ return {
31
+ uri: this.uri,
32
+ priority: this.priority
33
+ };
34
+ }
35
+
36
+ fromJSON({
37
+ uri, priority = 0
38
+ }) {
39
+ this.uri = uri;
40
+ this.priority = priority;
41
+
42
+ if (uri !== this.__cached_uri) {
43
+ // reset sampler
44
+ this.__cached_sampler = null;
45
+ this.__cached_uri = null;
46
+ }
47
+ }
48
+ }
49
+
50
+ Decal.typeName = 'Decal';