@woosh/meep-engine 2.39.13 → 2.39.16

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 (33) hide show
  1. package/core/binary/int32_to_binary_string.js +18 -0
  2. package/core/cache/Cache.js +1 -1
  3. package/core/geom/3d/decompose_matrix_4_array.js +3 -1
  4. package/core/geom/3d/ray/ray3_array_apply_matrix4.js +15 -1
  5. package/core/geom/3d/topology/bounds/computeTopoMeshBoundingSphere.js +2 -1
  6. package/editor/tools/v2/TransformControls.js +1782 -0
  7. package/editor/tools/v2/prototypeTransformControls.js +79 -0
  8. package/engine/asset/AssetManager.js +82 -23
  9. package/engine/ecs/parent/EntityNode.js +41 -13
  10. package/engine/ecs/terrain/ecs/Terrain.js +49 -39
  11. package/engine/ecs/terrain/ecs/layers/TerrainLayers.js +12 -5
  12. package/engine/ecs/terrain/ecs/splat/SplatMapping.js +5 -1
  13. package/engine/ecs/transform/Transform.d.ts +2 -0
  14. package/engine/ecs/transform/Transform.js +6 -0
  15. package/engine/ecs/transform-attachment/TransformAttachment.js +13 -0
  16. package/engine/ecs/transform-attachment/TransformAttachmentSystem.js +138 -16
  17. package/engine/graphics/FrameRunner.js +8 -2
  18. package/engine/graphics/ecs/mesh-v2/DrawMode.js +4 -0
  19. package/engine/graphics/ecs/mesh-v2/ShadedGeometry.js +77 -0
  20. package/engine/graphics/ecs/mesh-v2/ShadedGeometryFlags.js +10 -0
  21. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +2 -30
  22. package/engine/graphics/ecs/mesh-v2/render/ShadedGeometryRendererContext.js +11 -0
  23. package/engine/graphics/ecs/mesh-v2/render/adapters/InstancedRendererAdapter.js +8 -32
  24. package/engine/graphics/ecs/mesh-v2/render/optimization/RuntimeDrawMethodOptimizer.js +6 -0
  25. package/engine/graphics/geometry/buffered/makeGeometryIndexed.js +23 -0
  26. package/engine/graphics/geometry/buffered/query/GeometrySpatialQueryAccelerator.js +16 -4
  27. package/engine/graphics/geometry/buffered/query/GeometryVisitor.js +8 -1
  28. package/engine/graphics/geometry/buffered/query/RaycastNearestHitComputingVisitor.js +14 -5
  29. package/engine/graphics/geometry/bvh/buffered/BinaryBVHFromBufferGeometry.js +56 -0
  30. package/engine/graphics/micron/build/hierarchy/build_merge_graph.js +1 -0
  31. package/engine/graphics/micron/build/hierarchy/computePatchMergeScore.js +18 -4
  32. package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +15 -5
  33. package/package.json +1 -1
@@ -0,0 +1,79 @@
1
+ import { EngineHarness } from "../../../engine/EngineHarness.js";
2
+ import EntityBuilder from "../../../engine/ecs/EntityBuilder.js";
3
+ import { ShadedGeometry } from "../../../engine/graphics/ecs/mesh-v2/ShadedGeometry.js";
4
+ import { BoxBufferGeometry, MeshStandardMaterial } from "three";
5
+ import { Transform } from "../../../engine/ecs/transform/Transform.js";
6
+ import { ShadedGeometrySystem } from "../../../engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js";
7
+ import { TransformControls } from "./TransformControls.js";
8
+ import { Camera } from "../../../engine/graphics/ecs/camera/Camera.js";
9
+ import { TransformAttachmentSystem } from "../../../engine/ecs/transform-attachment/TransformAttachmentSystem.js";
10
+ import InputController from "../../../engine/input/ecs/components/InputController.js";
11
+ import InputControllerSystem from "../../../engine/input/ecs/systems/InputControllerSystem.js";
12
+
13
+ const harness = new EngineHarness();
14
+
15
+ /**
16
+ *
17
+ * @param {Engine} engine
18
+ */
19
+ async function main(engine) {
20
+ EngineHarness.buildBasics({
21
+ engine,
22
+ cameraController: false
23
+ });
24
+
25
+
26
+ // create something to drag
27
+ const ecd = engine.entityManager.dataset;
28
+ const cube = new EntityBuilder()
29
+ .add(ShadedGeometry.from(new BoxBufferGeometry(1, 1, 1), new MeshStandardMaterial({
30
+ color: 0xFFAAAA
31
+ })))
32
+ .add(Transform.fromJSON({
33
+ position: {
34
+ x: 10,
35
+ y: 0,
36
+ z: 10
37
+ }
38
+ }));
39
+
40
+ const cube_entity = cube
41
+ .build(ecd);
42
+
43
+ const camera = ecd.getAnyComponent(Camera);
44
+
45
+ const controls = new TransformControls(camera.component.object, engine.gameView.el);
46
+
47
+ controls.build(ecd);
48
+ controls.attach(cube_entity);
49
+
50
+ // cube.getComponentSafe(Transform).position.onChanged.add(console.log);
51
+ // cube.getComponentSafe(Transform).scale.onChanged.add(console.warn);
52
+
53
+
54
+ new EntityBuilder().add(new InputController([{
55
+ path: 'keyboard/keys/w/down',
56
+ listener() {
57
+ controls.mode = "translate"
58
+ }
59
+ }, {
60
+ path: 'keyboard/keys/e/down',
61
+ listener() {
62
+ controls.mode = "scale"
63
+ }
64
+ }, {
65
+ path: 'keyboard/keys/r/down',
66
+ listener() {
67
+ controls.mode = "rotate"
68
+ }
69
+ }])).build(ecd);
70
+
71
+ }
72
+
73
+ harness.initialize({
74
+ configuration(config, engine) {
75
+ config.addSystem(new ShadedGeometrySystem(engine));
76
+ config.addSystem(new TransformAttachmentSystem(engine));
77
+ config.addSystem(new InputControllerSystem(engine.devices));
78
+ }
79
+ }).then(main);
@@ -14,29 +14,58 @@ import { array_remove_element } from "../../core/collection/array/array_remove_e
14
14
  import { AssetDescription } from "./AssetDescription.js";
15
15
  import { noop } from "../../core/function/Functions.js";
16
16
 
17
- /**
18
- *
19
- * @param {function(asset:Asset)} successCallback
20
- * @param {function(error:*)} failureCallback
21
- * @param {function(loaded:number, total:number):void} progressCallback
22
- * @constructor
23
- */
24
- function AssetRequest(successCallback, failureCallback, progressCallback) {
25
- this.successCallback = successCallback;
26
- this.failureCallback = failureCallback;
27
- this.pogressCallback = progressCallback;
17
+ class AssetRequest {
18
+ /**
19
+ *
20
+ * @param {function(asset:Asset)} successCallback
21
+ * @param {function(error:*)} failureCallback
22
+ * @param {function(loaded:number, total:number):void} progressCallback
23
+ * @constructor
24
+ */
25
+ constructor(successCallback, failureCallback, progressCallback) {
26
+ this.successCallback = successCallback;
27
+ this.failureCallback = failureCallback;
28
+ this.pogressCallback = progressCallback;
29
+ }
28
30
  }
29
31
 
30
-
31
32
  /**
32
- *
33
- * @param {AssetDescription} description
34
- * @constructor
33
+ * @enum {number}
35
34
  */
36
- function PendingAsset(description) {
37
- this.description = description;
38
- this.requests = [];
39
- this.progress = new BoundedValue(0, 0);
35
+ const AssetLoadState = {
36
+ Initial: 0,
37
+ Queued: 1,
38
+ Loading: 2,
39
+ Succeeded: 3,
40
+ Failed: 4
41
+ };
42
+
43
+
44
+ class PendingAsset {
45
+ /**
46
+ *
47
+ * @param {AssetDescription} description
48
+ * @constructor
49
+ */
50
+ constructor(description) {
51
+ this.description = description;
52
+ /**
53
+ *
54
+ * @type {AssetRequest[]}
55
+ */
56
+ this.requests = [];
57
+ /**
58
+ *
59
+ * @type {BoundedValue}
60
+ */
61
+ this.progress = new BoundedValue(0, 0);
62
+
63
+ /**
64
+ *
65
+ * @type {AssetLoadState|number}
66
+ */
67
+ this.state = AssetLoadState.Initial;
68
+ }
40
69
  }
41
70
 
42
71
 
@@ -232,6 +261,19 @@ export class AssetManager {
232
261
  * @param {Asset} loaded_asset
233
262
  */
234
263
  const success = async (loaded_asset) => {
264
+ if (pendingAsset.state === AssetLoadState.Succeeded) {
265
+ // incorrect state
266
+ console.warn(`Asset already resolved, duplicate success invocation. Ignored. AD:${assetDescription}`);
267
+ return;
268
+ }
269
+
270
+ if (pendingAsset.state === AssetLoadState.Failed) {
271
+ // incorrect state
272
+ console.error(`Asset already failed. Unexpected resolution signal. AD:${assetDescription}`);
273
+ }
274
+
275
+ pendingAsset.state = AssetLoadState.Succeeded;
276
+
235
277
  let asset = loaded_asset;
236
278
 
237
279
  // apply transform chain
@@ -256,25 +298,42 @@ export class AssetManager {
256
298
 
257
299
  //register asset
258
300
  assets.set(assetDescription, asset);
259
- requests.forEach(function (request) {
301
+
302
+ // process callbacks
303
+ for (let i = 0; i < requests.length; i++) {
304
+ const request = requests[i];
260
305
  try {
261
306
  request.successCallback(asset);
262
307
  } catch (e) {
263
308
  console.error("Failed to execute asset success callback", e);
264
309
  }
265
- });
310
+ }
311
+
266
312
  //clear callbacks
267
313
  requestMap.delete(assetDescription);
268
314
  }
269
315
 
270
316
  function failure(error) {
271
- requests.forEach(function (request) {
317
+ if (pendingAsset.state === AssetLoadState.Failed) {
318
+ console.warn(`Asset already failed, this is a redundant invocation. AD: ${assetDescription}`);
319
+ return;
320
+ }
321
+
322
+ if (pendingAsset.state === AssetLoadState.Succeeded) {
323
+ // incorrect state
324
+ console.error(`Asset already resolved. Unexpected failure signal. AD:${assetDescription}`);
325
+ }
326
+
327
+ pendingAsset.state = AssetLoadState.Failed;
328
+
329
+ for (let i = 0; i < requests.length; i++) {
330
+ const request = requests[i];
272
331
  try {
273
332
  request.failureCallback(error);
274
333
  } catch (e) {
275
334
  console.error("Failed to execute asset failure callback", e);
276
335
  }
277
- });
336
+ }
278
337
  //clear callbacks
279
338
  requestMap.delete(assetDescription);
280
339
 
@@ -219,16 +219,24 @@ export class EntityNode {
219
219
  addChild(node) {
220
220
  const added = array_push_if_unique(this.__children, node);
221
221
 
222
- if (added) {
223
- if (node.parent !== null) {
224
- // has a parent already, detach
225
- node.parent.removeChild(node);
226
- }
222
+ if (!added) {
223
+ // already contain this child
224
+ return false;
225
+ }
226
+
227
+ if (node.parent !== null) {
228
+ // has a parent already, detach
229
+ node.parent.removeChild(node);
230
+ }
227
231
 
228
- node.parent = this;
232
+ node.parent = this;
233
+
234
+ // live mode
235
+ if (this.__entity.isBuilt) {
236
+ // TODO attach node live
229
237
  }
230
238
 
231
- return added;
239
+ return true;
232
240
  }
233
241
 
234
242
  /**
@@ -239,13 +247,21 @@ export class EntityNode {
239
247
  removeChild(node) {
240
248
  const removed = array_remove_first(this.__children, node);
241
249
 
242
- if (removed) {
243
- assert.equal(node.parent, this, 'node is a child, but parent property points to something else instead of this node');
244
-
245
- node.parent = null;
250
+ if (!removed) {
251
+ // not present
252
+ return false;
246
253
  }
247
254
 
248
- return removed;
255
+ assert.equal(node.parent, this, 'node is a child, but parent property points to something else instead of this node');
256
+
257
+ node.parent = null;
258
+
259
+
260
+ return true;
261
+ }
262
+
263
+ get isBuilt() {
264
+ return this.__entity.isBuilt;
249
265
  }
250
266
 
251
267
  /**
@@ -278,6 +294,7 @@ export class EntityNode {
278
294
  const parent_entity_id = parent_entity_builder.entity;
279
295
  parent_entity.entity = parent_entity_id;
280
296
 
297
+ assert.ok(parent.entity.hasComponent(Transform), "parent node must have a transform but doesn't. Transform is required for attachment transform hierarchy to work correctly");
281
298
 
282
299
  const attachment = this.__safe_get_attachment();
283
300
 
@@ -306,7 +323,12 @@ export class EntityNode {
306
323
  }
307
324
 
308
325
  destroy() {
309
- assert.ok(this.__entity.isBuilt, 'Not built');
326
+ if (!this.__entity.isBuilt) {
327
+ // not built
328
+
329
+ //TODO check for dangling listeners
330
+ return;
331
+ }
310
332
 
311
333
  // remove listeners
312
334
  this.__transform.position.onChanged.remove(this.__transform_sync_down, this);
@@ -328,3 +350,9 @@ export class EntityNode {
328
350
  this.on.destroyed.send0();
329
351
  }
330
352
  }
353
+
354
+ /**
355
+ * @readonly
356
+ * @type {boolean}
357
+ */
358
+ EntityNode.prototype.isEntityNode = true;
@@ -36,6 +36,7 @@ import { IllegalStateException } from "../../../../core/fsm/exceptions/IllegalSt
36
36
  import { buildLightTexture } from "./BuildLightTexture.js";
37
37
  import { promiseSamplerHeight } from "./PromiseSamplerHeight.js";
38
38
  import { loadLegacyTerrainSplats } from "./splat/loadLegacyTerrainSplats.js";
39
+ import { WHITE_PIXEL_DATA_URL } from "../../../graphics/WHITE_PIXEL_DATA_URL.js";
39
40
 
40
41
  let idCounter = 0;
41
42
 
@@ -350,7 +351,7 @@ class Terrain {
350
351
  overlay.tileImage.onChanged.add(this.updateTileImage, this);
351
352
  }
352
353
 
353
- updateTileImage() {
354
+ async updateTileImage() {
354
355
  const tileImageURL = this.overlay.tileImage.getValue();
355
356
 
356
357
  const assetManager = this.__assetManager;
@@ -359,27 +360,25 @@ class Terrain {
359
360
  return;
360
361
  }
361
362
 
362
- assetManager.promise(tileImageURL, GameAssetType.Texture)
363
- .then((asset) => {
363
+ const asset = await assetManager.promise(tileImageURL, GameAssetType.Texture);
364
364
 
365
- if (tileImageURL !== this.overlay.tileImage.getValue()) {
366
- //url has changed, abort
367
- return;
368
- }
365
+ if (tileImageURL !== this.overlay.tileImage.getValue()) {
366
+ //url has changed, abort
367
+ return;
368
+ }
369
369
 
370
- /**
371
- *
372
- * @type {Texture}
373
- */
374
- const texture = asset.create();
370
+ /**
371
+ *
372
+ * @type {Texture}
373
+ */
374
+ const texture = asset.create();
375
375
 
376
- texture.minFilter = LinearFilter;
377
- texture.magFilter = LinearFilter;
376
+ texture.minFilter = LinearFilter;
377
+ texture.magFilter = LinearFilter;
378
378
 
379
- texture.generateMipmaps = false;
379
+ texture.generateMipmaps = false;
380
380
 
381
- this.material.uniforms.diffuseGridOverlaySprite.value = texture;
382
- });
381
+ this.material.uniforms.diffuseGridOverlaySprite.value = texture;
383
382
  }
384
383
 
385
384
  update(timeDelta) {
@@ -710,7 +709,7 @@ class Terrain {
710
709
  }
711
710
 
712
711
  /**
713
- *
712
+ * @deprecated
714
713
  * @param {AssetManager} assetManager
715
714
  */
716
715
  buildFromLegacy(assetManager) {
@@ -928,39 +927,50 @@ class Terrain {
928
927
  * @param opt
929
928
  * @param {Engine} engine
930
929
  */
931
- fromJSON(opt, engine) {
930
+ fromJSON({
931
+ resolution = 4,
932
+ preview,
933
+ size = 1,
934
+ scale = 1,
935
+ material,
936
+ heights,
937
+ layers,
938
+ splat,
939
+ overlayTileImage
940
+ } = {}, engine) {
932
941
  // mark as needing rebuilding
933
942
  this.clearFlag(TerrainFlags.Built);
934
943
 
935
- if (opt === undefined) {
936
- opt = {};
944
+ if (preview !== undefined) {
945
+ this.preview.fromJSON(preview);
937
946
  }
938
947
 
939
- if (opt.preview !== undefined) {
940
- this.preview.fromJSON(opt.preview);
941
- }
942
-
943
- this.resolution = opt.resolution !== undefined ? opt.resolution : 4;
948
+ this.resolution = resolution;
944
949
 
945
- const size = this.size;
946
-
947
- if (opt.size !== undefined) {
948
- size.fromJSON(opt.size);
949
- }
950
+ this.size.fromJSON(size);
950
951
 
951
- this.gridScale = opt.scale !== undefined ? opt.scale : 2;
952
+ this.gridScale = scale;
952
953
  //
953
954
 
954
- this.materialDesc = opt.material;
955
+ this.materialDesc = material;
955
956
 
956
- this.samplerHeight.fromJSON(opt.heights);
957
- this.layers.fromJSON(opt.layers);
958
- this.splat.fromJSON(opt.splat);
959
-
960
- if (opt.overlayTileImage !== undefined) {
957
+ if (heights !== undefined) {
958
+ this.samplerHeight.fromJSON(heights);
959
+ } else {
960
+ this.samplerHeight.data = new Float32Array(1);
961
+ this.samplerHeight.width = 1;
962
+ this.samplerHeight.height = 1;
963
+ this.samplerHeight.itemSize = 1;
964
+ this.samplerHeight.version++;
965
+ }
961
966
 
962
- this.overlay.baseTileImage(opt.overlayTileImage);
967
+ this.layers.fromJSON(layers);
968
+ this.splat.fromJSON(splat);
963
969
 
970
+ if (overlayTileImage !== undefined) {
971
+ this.overlay.baseTileImage = overlayTileImage;
972
+ } else {
973
+ this.overlay.baseTileImage = WHITE_PIXEL_DATA_URL;
964
974
  }
965
975
 
966
976
 
@@ -111,6 +111,8 @@ const scaled_texture_cache = new Cache({
111
111
  }
112
112
  });
113
113
 
114
+ const DEFAULT_RESOLUTION = 512;
115
+
114
116
  export class TerrainLayers {
115
117
  constructor() {
116
118
 
@@ -124,7 +126,7 @@ export class TerrainLayers {
124
126
  *
125
127
  * @type {Vector2}
126
128
  */
127
- this.resolution = new Vector2(512, 512);
129
+ this.resolution = new Vector2(DEFAULT_RESOLUTION, DEFAULT_RESOLUTION);
128
130
 
129
131
  /**
130
132
  *
@@ -166,7 +168,7 @@ export class TerrainLayers {
166
168
  };
167
169
  }
168
170
 
169
- fromJSON({ resolution, layers }) {
171
+ fromJSON({ resolution = DEFAULT_RESOLUTION, layers = [] } = {}) {
170
172
  this.resolution.fromJSON(resolution);
171
173
  this.layers.fromJSON(layers, TerrainLayer);
172
174
  }
@@ -232,9 +234,14 @@ export class TerrainLayers {
232
234
 
233
235
  const layerIndex = i;
234
236
 
235
- promise.then(() => {
236
- this.writeLayerDataIntoTexture(layerIndex);
237
- });
237
+ promise.then(
238
+ () => {
239
+ this.writeLayerDataIntoTexture(layerIndex);
240
+ },
241
+ (reason) => {
242
+ console.error(`Failed to load layer [${layerIndex}]"${layer.textureDiffuseURL}". Reason: ${reason}`);
243
+ }
244
+ );
238
245
 
239
246
  promises.push(promise);
240
247
  }
@@ -59,7 +59,11 @@ export class SplatMapping {
59
59
  };
60
60
  }
61
61
 
62
- fromJSON({ size, depth, data }) {
62
+ fromJSON({
63
+ size = { x: 1, y: 1 },
64
+ depth = 0,
65
+ data = []
66
+ } = {}) {
63
67
  this.resize(size.x, size.y, depth);
64
68
  array_copy(data, 0, this.weightData, 0, data.length);
65
69
 
@@ -31,4 +31,6 @@ export class Transform {
31
31
  fromMatrix4(matrix: ArrayLike<number>): void
32
32
 
33
33
  multiplyTransforms(a: Transform, b: Transform): void
34
+
35
+ makeIdentity():void
34
36
  }
@@ -285,6 +285,12 @@ export class Transform {
285
285
  toMatrix4(result) {
286
286
  compose_matrix4_array(result, this.position, this.rotation, this.scale);
287
287
  }
288
+
289
+ makeIdentity() {
290
+ this.position.copy(Vector3.zero);
291
+ this.rotation.copy(Quaternion.identity);
292
+ this.scale.copy(Vector3.one);
293
+ }
288
294
  }
289
295
 
290
296
  /**
@@ -1,4 +1,5 @@
1
1
  import { Transform } from "../transform/Transform.js";
2
+ import { int32_to_binary_string } from "../../../core/binary/int32_to_binary_string.js";
2
3
 
3
4
  export const TransformAttachmentFlags = {
4
5
  /**
@@ -31,6 +32,18 @@ export class TransformAttachment {
31
32
  this.flags = DEFAULT_FLAGS;
32
33
  }
33
34
 
35
+ toString() {
36
+ return JSON.stringify(this.toJSON());
37
+ }
38
+
39
+ toJSON() {
40
+ return {
41
+ transform: this.transform.toJSON(),
42
+ parent: this.parent,
43
+ flags: int32_to_binary_string(this.flags)
44
+ };
45
+ }
46
+
34
47
  get immediate() {
35
48
  return this.getFlag(TransformAttachmentFlags.Immediate);
36
49
  }