@woosh/meep-engine 2.39.43 → 2.40.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 (38) hide show
  1. package/core/binary/BinaryBuffer.js +14 -1
  2. package/core/bvh2/aabb3/AABB3.js +0 -133
  3. package/core/bvh2/aabb3/aabb3_intersects_line_segment.js +1 -1
  4. package/core/geom/3d/matrix/MATRIX_4_IDENTITY.js +9 -0
  5. package/core/geom/Quaternion.js +8 -8
  6. package/core/geom/Quaternion.spec.js +13 -0
  7. package/core/geom/Vector1.d.ts +2 -0
  8. package/core/parser/simple/DataType.js +2 -2
  9. package/editor/tools/v2/BlenderCameraOrientationGizmo.js +28 -4
  10. package/editor/view/EditorView.js +25 -22
  11. package/editor/view/ecs/components/common/NumberController.js +56 -9
  12. package/engine/EngineHarness.js +2 -4
  13. package/engine/asset/loaders/image/png/PNG.js +6 -0
  14. package/engine/asset/loaders/image/png/PNGReader.js +82 -2
  15. package/engine/asset/loaders/image/png/crc.js +66 -0
  16. package/engine/ecs/speaker/VoiceSystem.js +1 -1
  17. package/engine/ecs/terrain/ecs/Terrain.js +58 -36
  18. package/engine/ecs/terrain/ecs/TerrainSystem.js +22 -4
  19. package/engine/ecs/terrain/tiles/TerrainTile.js +109 -131
  20. package/engine/ecs/terrain/tiles/TerrainTileManager.js +100 -121
  21. package/engine/graphics/ecs/camera/Camera.js +1 -1
  22. package/engine/graphics/ecs/light/binding/fp/FPLightBinding.js +4 -4
  23. package/engine/graphics/geometry/bvh/buffered/BVHGeometryRaycaster.js +0 -78
  24. package/engine/graphics/geometry/optimization/merge/merge_geometry_hierarchy.js +0 -9
  25. package/engine/graphics/render/forward_plus/LightManager.js +25 -0
  26. package/engine/graphics/render/forward_plus/data/TextureBackedMemoryRegion.js +6 -2
  27. package/engine/graphics/render/forward_plus/materials/FP_SHADER_CHUNK_ACCUMULATION.js +6 -11
  28. package/engine/graphics/render/forward_plus/prototype/prototypeLightManager.js +268 -2
  29. package/engine/graphics/texture/atlas/AtlasPatch.js +12 -6
  30. package/engine/intelligence/behavior/util/LogMessageBehavior.js +29 -0
  31. package/engine/logging/ConsoleLoggerBackend.js +15 -0
  32. package/engine/logging/Logger.js +24 -1
  33. package/engine/logging/LoggerBackend.js +1 -0
  34. package/engine/physics/fluid/FluidField.js +9 -0
  35. package/engine/physics/fluid/effector/AbstractFluidEffector.js +6 -0
  36. package/engine/physics/fluid/effector/GlobalFluidEffector.js +12 -0
  37. package/engine/physics/fluid/effector/WakeFluidEffector.js +8 -0
  38. package/package.json +1 -1
@@ -18,13 +18,14 @@ import ThreeFactory from '../../../graphics/three/ThreeFactory.js';
18
18
 
19
19
  import { LeafNode } from '../../../../core/bvh2/LeafNode.js';
20
20
 
21
- import BVHFromBufferGeometry from '../../../graphics/geometry/bvh/buffered/BVHFromBufferGeometry.js';
22
-
23
21
  import IndexedBinaryBVH from '../../../../core/bvh2/binary/IndexedBinaryBVH.js';
24
22
  import { BVHGeometryRaycaster } from "../../../graphics/geometry/bvh/buffered/BVHGeometryRaycaster.js";
25
23
  import ObservedInteger from "../../../../core/model/ObservedInteger.js";
26
24
  import { SurfacePoint3 } from "../../../../core/geom/3d/SurfacePoint3.js";
27
25
  import Signal from "../../../../core/events/signal/Signal.js";
26
+ import { mat4 } from "gl-matrix";
27
+ import { AABB3 } from "../../../../core/bvh2/aabb3/AABB3.js";
28
+ import { NumericInterval } from "../../../../core/math/interval/NumericInterval.js";
28
29
 
29
30
  function extractFaceIndexFromLeaf(leaf) {
30
31
  return leaf;
@@ -92,11 +93,17 @@ class TerrainTile {
92
93
  this.isBuildInProgress = false;
93
94
  this.referenceCount = 0;
94
95
 
96
+ /**
97
+ *
98
+ * @type {Signal<TerrainTile>}
99
+ */
95
100
  this.onBuilt = new Signal();
96
101
  this.onDestroyed = new Signal();
97
102
 
98
- this.buildCallbacks = [];
99
-
103
+ /**
104
+ * @private
105
+ * @type {{bottomLeft: boolean, top: boolean, left: boolean, bottom: boolean, bottomRight: boolean, topLeft: boolean, topRight: boolean, right: boolean}}
106
+ */
100
107
  this.stitching = {
101
108
  top: false,
102
109
  bottom: false,
@@ -110,27 +117,34 @@ class TerrainTile {
110
117
  bottomRight: false
111
118
  };
112
119
 
120
+ /**
121
+ * Initial estimate of height bounds for this tile
122
+ * Untransformed by transform matrix
123
+ * @type {NumericInterval}
124
+ * @private
125
+ */
126
+ this.__initial_height_range = new NumericInterval(Number.NEGATIVE_INFINITY, Number.POSITIVE_INFINITY);
127
+
113
128
  this.raycaster = new BVHGeometryRaycaster();
114
129
  //Binary BVH form doesn't have distinct leaf objects and stores face indices directly, this requires a special face index extractor that treats leaves as indices directly.
115
130
  this.raycaster.extractFaceIndexFromLeaf = extractFaceIndexFromLeaf;
116
131
  }
117
132
 
118
133
  /**
119
- * @returns {Promise}
134
+ *
135
+ * @return {number[]}
120
136
  */
121
- promiseBuilt() {
122
-
137
+ get transform() {
138
+ return this.mesh.matrixWorld.elements;
123
139
  }
124
140
 
125
141
  /**
126
142
  *
127
- * @param {number} x
128
- * @param {number} y
129
- * @param {function} callback
130
- * @param {function} missCallback
143
+ * @param {number[]|Float32Array|mat4} m4
131
144
  */
132
- raycastVertical(x, y, callback, missCallback) {
133
- return this.raycaster.raycastVertical(x, y, callback, missCallback)
145
+ set transform(m4) {
146
+ mat4.copy(this.mesh.matrixWorld.elements, m4);
147
+ this.computeBoundingBox();
134
148
  }
135
149
 
136
150
  /**
@@ -188,7 +202,7 @@ class TerrainTile {
188
202
  }
189
203
 
190
204
  /**
191
- *
205
+ * Stitch vertex normals along the edges of the tile set
192
206
  * @param {TerrainTile|undefined} top
193
207
  * @param {TerrainTile|undefined} bottom
194
208
  * @param {TerrainTile|undefined} left
@@ -254,7 +268,9 @@ class TerrainTile {
254
268
  bottom.getVertexNormal(i, v0);
255
269
  top.getVertexNormal(otherOffset + i, v1);
256
270
 
257
- v0.add(v1).normalize();
271
+ v0.add(v1);
272
+ v0.normalize();
273
+
258
274
  bottom.setVertexNormal(i, v0);
259
275
  top.setVertexNormal(otherOffset + i, v0);
260
276
  }
@@ -316,7 +332,9 @@ class TerrainTile {
316
332
  right.getVertexNormal(index0, v0);
317
333
  left.getVertexNormal(index1, v1);
318
334
 
319
- v0.add(v1).normalize();
335
+ v0.add(v1);
336
+ v0.normalize();
337
+
320
338
  right.setVertexNormal(index0, v0);
321
339
  left.setVertexNormal(index1, v0);
322
340
  }
@@ -361,7 +379,11 @@ class TerrainTile {
361
379
  topRight.getVertexNormal(tCornerIndex, v2);
362
380
  bottomLeft.getVertexNormal(lCornerIndex, v3);
363
381
 
364
- v0.add(v1).add(v2).add(v3).normalize();
382
+ v0.add(v1);
383
+ v0.add(v2);
384
+ v0.add(v3);
385
+ v0.normalize();
386
+
365
387
  topLeft.setVertexNormal(tlCornerIndex, v0);
366
388
  bottomRight.setVertexNormal(cornerIndex, v0);
367
389
  topRight.setVertexNormal(tCornerIndex, v0);
@@ -404,118 +426,78 @@ class TerrainTile {
404
426
  stitchSides();
405
427
  }
406
428
 
407
- stitchNormals(top, left, topLeft) {
408
- const v0 = new Vector3(),
409
- v1 = new Vector3();
410
- if (top !== null && left !== null && topLeft !== null) {
411
-
412
- const v2 = new Vector3();
413
- const v3 = new Vector3();
414
- //fix corner
415
- const tlCornerIndex = (topLeft.size.x * topLeft.resolution.getValue()) * (topLeft.size.y * topLeft.resolution.getValue()) - 1;
416
- const cornerIndex = 0;
417
- const tCornerIndex = top.size.x * top.resolution.getValue() * (top.size.y * top.resolution.getValue() - 1);
418
- const lCornerIndex = left.size.x * left.resolution.getValue() - 1;
419
-
420
- topLeft.getVertexNormal(tlCornerIndex, v0);
421
- this.getVertexNormal(cornerIndex, v1);
422
- top.getVertexNormal(tCornerIndex, v2);
423
- left.getVertexNormal(lCornerIndex, v3);
424
-
425
- v0.add(v1).add(v2).add(v3).normalize();
426
- topLeft.setVertexNormal(tlCornerIndex, v0);
427
- this.setVertexNormal(cornerIndex, v0);
428
- top.setVertexNormal(tCornerIndex, v0);
429
- left.setVertexNormal(lCornerIndex, v0);
430
- }
431
- let i, l;
432
- let otherOffset;
433
- let otherResolution;
434
- const thisResolution = this.resolution.getValue();
435
- if (top !== null) {
436
- otherResolution = top.resolution.getValue();
437
- otherOffset = top.size.x * otherResolution * (top.size.y * otherResolution - 1);
438
- for (i = 0, l = this.size.x * thisResolution; i < l; i++) {
439
- this.getVertexNormal(i, v0);
440
- top.getVertexNormal(otherOffset + i, v1);
441
-
442
- v0.add(v1).normalize();
443
- this.setVertexNormal(i, v0);
444
- top.setVertexNormal(otherOffset + i, v0);
445
- }
446
- }
447
- if (left !== null) {
448
- otherResolution = left.resolution.getValue();
449
- otherOffset = left.size.x * otherResolution - 1;
450
- const otherMultiplier = left.size.x * otherResolution;
451
- const thisMultiplier = this.size.x * thisResolution;
452
- for (i = 0, l = this.size.y * thisResolution; i < l; i++) {
453
-
454
- const index0 = i * thisMultiplier;
455
- const index1 = otherOffset + i * otherMultiplier;
456
-
457
- this.getVertexNormal(index0, v0);
458
- left.getVertexNormal(index1, v1);
459
-
460
- v0.add(v1).normalize();
461
- this.setVertexNormal(index0, v0);
462
- left.setVertexNormal(index1, v0);
463
- }
464
- }
465
- }
466
-
467
429
  computeBoundingBox() {
430
+ /**
431
+ * @type {ThreeBox3}
432
+ */
433
+ let bb;
434
+
468
435
  const geometry = this.geometry;
469
- //check for bvh
470
- const bvh = this.bvh;
471
- if (bvh !== null) {
472
- geometry.boundingBox = new ThreeBox3(new ThreeVector3(bvh.x0, bvh.y0, bvh.z0), new ThreeVector3(bvh.x1, bvh.y1, bvh.z1));
473
436
 
474
- const dX = bvh.x1 - bvh.x0;
475
- const dY = bvh.y1 - bvh.y0;
476
- const dZ = bvh.z1 - bvh.z0;
437
+ if (geometry === null) {
438
+ // no geometry present yet
477
439
 
478
- const radius = Math.sqrt(dX * dX + dY * dY + dZ * dZ) / 2;
440
+ const position = this.position;
441
+ const scale = this.scale;
442
+ const size = this.size;
479
443
 
480
- const center = new ThreeVector3(bvh.x0 + dX / 2, bvh.y0 + dY / 2, bvh.z0 + dZ / 2);
444
+ const initial_height_range = this.__initial_height_range;
481
445
 
482
- geometry.boundingSphere = new ThreeSphere(center, radius);
483
- }
484
- //pull bounding box from geometry
485
- let bb = geometry.boundingBox;
486
- if (bb === null) {
487
- geometry.computeBoundingBox();
488
- bb = geometry.boundingBox;
489
- }
490
- this.boundingBox.setBounds(bb.min.x, bb.min.y, bb.min.z, bb.max.x, bb.max.y, bb.max.z);
491
- }
446
+ const min = new ThreeVector3(position.x * scale.x, initial_height_range.min, position.y * scale.y);
447
+ const max = new ThreeVector3(min.x + size.x * scale.x, initial_height_range.max, min.z + size.y * scale.y);
492
448
 
493
- generateBufferedGeometryBVH() {
494
- const geometry = this.geometry;
495
- // console.profile('build bvh');
496
- this.bvh = BVHFromBufferGeometry.buildUnsortedBinaryBVH(geometry);
497
- // console.profileEnd('build bvh');
498
- }
449
+ bb = new ThreeBox3(
450
+ min,
451
+ max
452
+ );
499
453
 
500
- unload() {
501
- this.isBuilt = false;
454
+ } else {
502
455
 
503
- this.geometry = null;
504
- this.bvh = null;
456
+ //check for bvh
457
+ const bvh = this.bvh;
458
+ if (bvh !== null) {
459
+ geometry.boundingBox = new ThreeBox3(new ThreeVector3(bvh.x0, bvh.y0, bvh.z0), new ThreeVector3(bvh.x1, bvh.y1, bvh.z1));
460
+
461
+ const dX = bvh.x1 - bvh.x0;
462
+ const dY = bvh.y1 - bvh.y0;
463
+ const dZ = bvh.z1 - bvh.z0;
464
+
465
+ const radius = Math.sqrt(dX * dX + dY * dY + dZ * dZ) / 2;
466
+
467
+ const center = new ThreeVector3(bvh.x0 + dX / 2, bvh.y0 + dY / 2, bvh.z0 + dZ / 2);
468
+
469
+ geometry.boundingSphere = new ThreeSphere(center, radius);
470
+ }
471
+ //pull bounding box from geometry
505
472
 
506
- const stitching = this.stitching;
473
+ bb = geometry.boundingBox;
474
+ if (bb === null) {
475
+ geometry.computeBoundingBox();
476
+ bb = geometry.boundingBox;
477
+ }
478
+ }
507
479
 
508
- stitching.top = false;
509
- stitching.bottom = false;
480
+ const x0 = bb.min.x;
481
+ const y0 = bb.min.y;
482
+ const z0 = bb.min.z;
510
483
 
511
- stitching.left = false;
512
- stitching.right = false;
484
+ const x1 = bb.max.x;
485
+ const y1 = bb.max.y;
486
+ const z1 = bb.max.z;
487
+
488
+ const geometry_bb = new AABB3(
489
+ x0, y0, z0,
490
+ x1, y1, z1
491
+ );
513
492
 
514
- stitching.topLeft = false;
515
- stitching.topRight = false;
493
+ geometry_bb.applyMatrix4(this.transform);
516
494
 
517
- stitching.bottomLeft = false;
518
- stitching.bottomRight = false;
495
+ this.boundingBox.setBounds(
496
+ geometry_bb.x0, geometry_bb.y0, geometry_bb.z0,
497
+ geometry_bb.x1, geometry_bb.y1, geometry_bb.z1
498
+ );
499
+ // update changes up the tree
500
+ this.boundingBox.bubbleRefit();
519
501
  }
520
502
 
521
503
  /**
@@ -523,23 +505,17 @@ class TerrainTile {
523
505
  * @param {number} min_height
524
506
  * @param {number} max_height
525
507
  */
526
- createInitialBounds(min_height, max_height) {
527
- const offset = this.position.clone().multiply(this.scale);
528
-
529
- const size = this.size.clone().multiply(this.scale);
530
-
531
- const max = offset.clone().add(size);
532
-
533
- this.boundingBox.setBounds(
534
- offset.x, min_height, offset.y,
535
- max.x, max_height, max.y
536
- );
508
+ setInitialHeightBounds(min_height, max_height) {
509
+ this.__initial_height_range.set(min_height, max_height);
537
510
  }
538
511
 
539
512
  dispose() {
540
513
  if (this.geometry !== null) {
541
514
  this.geometry.dispose();
515
+ this.geometry = null;
542
516
  }
517
+
518
+ this.isBuilt = false;
543
519
  }
544
520
 
545
521
  build(tileData) {
@@ -549,7 +525,9 @@ class TerrainTile {
549
525
  const tileDataGeometry = tileData.geometry;
550
526
 
551
527
  const g = new ThreeBufferGeometry();
528
+
552
529
  g.setIndex(new ThreeBufferAttribute(tileDataGeometry.indices, 1));
530
+
553
531
  g.setAttribute('position', new ThreeBufferAttribute(tileDataGeometry.vertices, 3));
554
532
  g.setAttribute('normal', new ThreeBufferAttribute(tileDataGeometry.normals, 3));
555
533
  g.setAttribute('uv', new ThreeBufferAttribute(tileDataGeometry.uvs, 2));
@@ -571,11 +549,6 @@ class TerrainTile {
571
549
  }
572
550
 
573
551
 
574
- //set bounding box
575
- // console.time('bb');
576
- this.computeBoundingBox();
577
- // console.timeEnd('bb');
578
-
579
552
  const mesh = this.mesh;
580
553
 
581
554
  mesh.geometry = g;
@@ -583,6 +556,11 @@ class TerrainTile {
583
556
  mesh.receiveShadow = true;
584
557
  mesh.castShadow = true;
585
558
 
559
+ //set bounding box
560
+ // console.time('bb');
561
+ this.computeBoundingBox();
562
+ // console.timeEnd('bb');
563
+
586
564
  // console.timeEnd('total');
587
565
  // console.groupEnd();
588
566
 
@@ -15,7 +15,6 @@ import { MeshPhongMaterial } from 'three';
15
15
  import { assert } from "../../../../core/assert.js";
16
16
  import { Color } from "../../../../core/color/Color.js";
17
17
  import { noop } from "../../../../core/function/Functions.js";
18
- import Vector3 from "../../../../core/geom/Vector3.js";
19
18
  import { SurfacePoint3 } from "../../../../core/geom/3d/SurfacePoint3.js";
20
19
  import { RaycastBVHVisitor } from "../../../../core/bvh2/traversal/RaycastBVHVisitor.js";
21
20
  import { FirstRayIntersectionTerrainBVHVisitor } from "./FirstRayIntersectionTerrainBVHVisitor.js";
@@ -24,6 +23,8 @@ import { aabb2_overlapExists } from "../../../../core/geom/AABB2.js";
24
23
  import ObservedInteger from "../../../../core/model/ObservedInteger.js";
25
24
  import { NumericInterval } from "../../../../core/math/interval/NumericInterval.js";
26
25
  import { randomFloatBetween } from "../../../../core/math/random/randomFloatBetween.js";
26
+ import { mat4, vec3 } from "gl-matrix";
27
+ import { isArrayEqualStrict } from "../../../../core/collection/array/isArrayEqualStrict.js";
27
28
 
28
29
  class TerrainTileManager {
29
30
  /**
@@ -105,7 +106,30 @@ class TerrainTileManager {
105
106
  this.traverse(this.assignTileMaterial, this);
106
107
  });
107
108
 
109
+ /**
110
+ *
111
+ * @type {Float32Array}
112
+ * @private
113
+ */
114
+ this.__transform = new Float32Array(16);
115
+
116
+ }
117
+
118
+ set transform(m4) {
119
+ if (isArrayEqualStrict(m4, this.__transform)) {
120
+ // no change
121
+ return;
122
+ }
123
+
124
+ mat4.copy(this.__transform, m4);
125
+
126
+ this.traverse(t => {
127
+ t.transform = m4;
128
+ });
129
+ }
108
130
 
131
+ get transform() {
132
+ return this.__transform;
109
133
  }
110
134
 
111
135
  /**
@@ -156,74 +180,6 @@ class TerrainTileManager {
156
180
  this.initializeTiles();
157
181
  }
158
182
 
159
- /**
160
- *
161
- * @param {Vector3} origin
162
- * @param {Vector3} direction
163
- * @param {function} callback
164
- * @param {function} missCallback
165
- */
166
- raycast(origin, direction, callback, missCallback) {
167
- assert.typeOf(callback, 'function', 'callback');
168
- assert.typeOf(missCallback, 'function', 'missCallback');
169
-
170
- let bestHit = null;
171
- let bestNormal = null;
172
- let bestGeometry = null;
173
- let bestDistanceSqr = Number.POSITIVE_INFINITY;
174
-
175
- let firstStageOver = false;
176
-
177
- let tileCount = 0;
178
-
179
- function tryReturn() {
180
- if (tileCount === 0 && firstStageOver) {
181
- callback(bestHit, bestNormal, bestGeometry);
182
- }
183
- }
184
-
185
- function registerHit(hit, normal, geo) {
186
- const d = hit.distanceSqrTo(origin);
187
- if (d < bestDistanceSqr) {
188
- bestDistanceSqr = d;
189
- bestHit = hit;
190
- bestNormal = normal;
191
- bestGeometry = geo;
192
- }
193
- }
194
-
195
-
196
- /**
197
- *
198
- * @param {TerrainTile} tile
199
- */
200
- function doCast(tile) {
201
-
202
- tile.raycast(origin, direction, registerHit, missCallback);
203
- tileCount--;
204
- tryReturn();
205
- }
206
-
207
- this.bvh.traverseRayLeafIntersections(origin.x, origin.y, origin.z, direction.x, direction.y, direction.z, function (leaf) {
208
- /**
209
- *
210
- * @type {TerrainTile}
211
- */
212
- const tile = leaf.object;
213
-
214
- tileCount++;
215
-
216
- if (tile.isBuilt) {
217
- doCast(tile);
218
- } else {
219
- tile.onBuilt.addOne(doCast);
220
- }
221
- });
222
-
223
- firstStageOver = true;
224
- tryReturn();
225
- }
226
-
227
183
  /**
228
184
  *
229
185
  * @param {TerrainTile} tile
@@ -378,7 +334,8 @@ class TerrainTileManager {
378
334
  tile.scale.copy(this.scale);
379
335
  tile.resolution.copy(this.resolution);
380
336
 
381
- tile.createInitialBounds(this.heightRange.min, this.heightRange.max);
337
+ tile.setInitialHeightBounds(this.heightRange.min, this.heightRange.max);
338
+ tile.computeBoundingBox();
382
339
 
383
340
  //hook for building
384
341
  tile.ensureBuilt = ensureBuilt(x, y);
@@ -397,11 +354,8 @@ class TerrainTileManager {
397
354
  * @returns {number}
398
355
  */
399
356
  computeTileIndex(x, y) {
400
- assert.ok(Number.isInteger(x), `x must be an integer, instead was ${x}`);
401
- assert.ok(Number.isInteger(y), `x must be an integer, instead was ${y}`);
402
-
403
- assert.ok(x >= 0, `x(=${x}) must be greater or equal to 0`);
404
- assert.ok(y >= 0, `y(=${y}) must be greater or equal to 0`);
357
+ assert.isNonNegativeInteger(x, 'x');
358
+ assert.isNonNegativeInteger(y, 'y');
405
359
 
406
360
  const w = Math.ceil(this.totalSize.x / this.tileSize.x);
407
361
 
@@ -415,9 +369,16 @@ class TerrainTileManager {
415
369
  *
416
370
  * @param {number} x
417
371
  * @param {number} y
418
- * @returns {TerrainTile}
372
+ * @returns {TerrainTile|undefined}
419
373
  */
420
374
  getRaw(x, y) {
375
+ if (
376
+ x < 0
377
+ || x >= Math.ceil(this.totalSize.x / this.tileSize.x)
378
+ || y < 0
379
+ ) {
380
+ return undefined;
381
+ }
421
382
 
422
383
  const tileIndex = this.computeTileIndex(x, y);
423
384
 
@@ -442,6 +403,43 @@ class TerrainTileManager {
442
403
  return this.getRaw(tileX, tileY);
443
404
  }
444
405
 
406
+ /**
407
+ * Given world coordinates in top-down plane, where X runs along X axis and Y runs along Z axis, returns terrain tile that overlaps that 2d region
408
+ * @param {number} x
409
+ * @param {number} y
410
+ * @return {TerrainTile|undefined}
411
+ */
412
+ getTileByWorldPosition2D(x, y) {
413
+ const v3 = vec3.fromValues(x, 0, y);
414
+
415
+ const world_inverse = mat4.create();
416
+
417
+ mat4.invert(world_inverse, this.transform);
418
+
419
+
420
+ vec3.transformMat4(v3, v3, world_inverse);
421
+
422
+ return this.getRawTileByPosition(v3.x, v3.z);
423
+ }
424
+
425
+ /**
426
+ * Builds and returns the tile from world coordinates
427
+ * @param {number} x
428
+ * @param {number} y
429
+ * @returns {Promise<TerrainTile>}
430
+ */
431
+ obtainTileAtWorldPosition2D(x, y) {
432
+ const tile = this.getTileByWorldPosition2D(x, y);
433
+
434
+ return this.obtain(tile.gridPosition.x, tile.gridPosition.y);
435
+ }
436
+
437
+ /**
438
+ * @deprecated use {@link #obtain instead}
439
+ * @param {number} x
440
+ * @param {number} y
441
+ * @param {function(TerrainTile):*} callback
442
+ */
445
443
  processTile(x, y, callback) {
446
444
  const tile = this.getRaw(x, y);
447
445
 
@@ -454,9 +452,10 @@ class TerrainTileManager {
454
452
 
455
453
  /**
456
454
  *
457
- * @param {int} x
458
- * @param {int} y
459
- * @returns {Promise}
455
+ * @param {number} x coordinate
456
+ * @param {number} y coordinate
457
+ * @returns {Promise<TerrainTile>}
458
+ * @throws if no tile exists at given coordinates
460
459
  */
461
460
  obtain(x, y) {
462
461
  const tile = this.getRaw(x, y);
@@ -490,6 +489,10 @@ class TerrainTileManager {
490
489
  * @param {TerrainTile} tile
491
490
  */
492
491
  release(tile) {
492
+ if (tile.referenceCount <= 0) {
493
+ console.warn('Tile already has no references');
494
+ }
495
+
493
496
  tile.referenceCount--;
494
497
 
495
498
  if (tile.referenceCount <= 0) {
@@ -497,7 +500,7 @@ class TerrainTileManager {
497
500
  tile.dispose();
498
501
  }
499
502
 
500
-
503
+ // TODO check tile.isBuilt flag
501
504
  tile.onDestroyed.send1(tile);
502
505
  }
503
506
 
@@ -512,9 +515,17 @@ class TerrainTileManager {
512
515
  }
513
516
  }
514
517
 
518
+ /**
519
+ *
520
+ * @param {number} x
521
+ * @param {number} y
522
+ * @param {TerrainTile} tile
523
+ */
515
524
  stitchTile(x, y, tile) {
516
525
 
517
- const gridSize = this.totalSize.clone().divide(this.tileSize).floor();
526
+ const gridSize = this.totalSize.clone();
527
+ gridSize.divide(this.tileSize);
528
+ gridSize.floor();
518
529
 
519
530
  const self = this;
520
531
  //normal stitching
@@ -569,6 +580,7 @@ class TerrainTileManager {
569
580
  directionZ
570
581
  ) {
571
582
 
583
+ // console.log('+ raycastFirstSync');
572
584
  raycastBVHVisitor.collector = firstRayIntersectionTerrainBVHVisitor;
573
585
  raycastBVHVisitor.setOrigin(originX, originY, originZ);
574
586
  raycastBVHVisitor.setDirection(directionX, directionY, directionZ);
@@ -579,6 +591,8 @@ class TerrainTileManager {
579
591
 
580
592
  traverseBinaryNodeUsingVisitor(this.bvh, raycastBVHVisitor);
581
593
 
594
+ // console.log('- raycastFirstSync');
595
+
582
596
  if (firstRayIntersectionTerrainBVHVisitor.closestDistance !== Number.POSITIVE_INFINITY) {
583
597
  result.copy(firstRayIntersectionTerrainBVHVisitor.closest);
584
598
 
@@ -588,41 +602,6 @@ class TerrainTileManager {
588
602
  return false;
589
603
  }
590
604
 
591
- raycastVertical(x, y, successCallback, missCallback) {
592
- assert.typeOf(missCallback, 'function', 'missCallback');
593
-
594
- /**
595
- *
596
- * @param {TerrainTile} tile
597
- */
598
- function doCast(tile) {
599
- tile.raycastVertical(x, y, successCallback, missCallback);
600
- }
601
-
602
-
603
- let miss = true;
604
-
605
- this.bvh.traverseRayLeafIntersections(x, -10000, y, 0, 1, 0, function (leaf) {
606
- /**
607
- *
608
- * @type {TerrainTile}
609
- */
610
- const tile = leaf.object;
611
-
612
- miss = false;
613
-
614
- if (tile.isBuilt) {
615
- doCast(tile);
616
- } else {
617
- tile.onBuilt.addOne(doCast);
618
- }
619
- });
620
-
621
- if (miss) {
622
- missCallback();
623
- }
624
- }
625
-
626
605
  /**
627
606
  * TODO untested
628
607
  * @param {SurfacePoint3} contact
@@ -631,7 +610,10 @@ class TerrainTileManager {
631
610
  * @return {boolean}
632
611
  */
633
612
  raycastVerticalFirstSync(contact, x, y) {
634
- return this.raycastFirstSync(contact, x, -10000, y, 0, 1, 0);
613
+ // console.log('+ raycastVerticalFirstSync');
614
+ const r = this.raycastFirstSync(contact, x, -10000, y, 0, 1, 0);
615
+ // console.log('- raycastVerticalFirstSync');
616
+ return r;
635
617
  }
636
618
 
637
619
  /**
@@ -697,14 +679,11 @@ class TerrainTileManager {
697
679
 
698
680
  // console.timeEnd(processName);
699
681
 
700
- self.on.tileBuilt.dispatch(tile);
682
+ self.on.tileBuilt.send1(tile);
701
683
  }, reject);
702
684
  }
703
685
  }
704
686
 
705
- const v3_origin = new Vector3();
706
- const v3_direction = new Vector3();
707
-
708
687
  const raycastBVHVisitor = new RaycastBVHVisitor();
709
688
  const firstRayIntersectionTerrainBVHVisitor = new FirstRayIntersectionTerrainBVHVisitor();
710
689