@loaders.gl/tiles 4.2.0-alpha.3 → 4.2.0-alpha.5

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 (73) hide show
  1. package/dist/constants.js +34 -32
  2. package/dist/dist.dev.js +723 -342
  3. package/dist/dist.min.js +9 -0
  4. package/dist/index.cjs +98 -240
  5. package/dist/index.cjs.map +7 -0
  6. package/dist/index.d.ts +10 -10
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +3 -1
  9. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts +1 -1
  10. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.d.ts.map +1 -1
  11. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.js +45 -35
  12. package/dist/tileset/format-i3s/i3s-pending-tiles-register.js +40 -22
  13. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts +1 -1
  14. package/dist/tileset/format-i3s/i3s-tile-manager.d.ts.map +1 -1
  15. package/dist/tileset/format-i3s/i3s-tile-manager.js +67 -69
  16. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts +2 -2
  17. package/dist/tileset/format-i3s/i3s-tileset-traverser.d.ts.map +1 -1
  18. package/dist/tileset/format-i3s/i3s-tileset-traverser.js +78 -57
  19. package/dist/tileset/helpers/3d-tiles-options.d.ts +1 -1
  20. package/dist/tileset/helpers/3d-tiles-options.d.ts.map +1 -1
  21. package/dist/tileset/helpers/3d-tiles-options.js +3 -4
  22. package/dist/tileset/helpers/bounding-volume.js +269 -128
  23. package/dist/tileset/helpers/frame-state.d.ts +1 -1
  24. package/dist/tileset/helpers/frame-state.d.ts.map +1 -1
  25. package/dist/tileset/helpers/frame-state.js +114 -95
  26. package/dist/tileset/helpers/i3s-lod.d.ts +2 -2
  27. package/dist/tileset/helpers/i3s-lod.d.ts.map +1 -1
  28. package/dist/tileset/helpers/i3s-lod.js +68 -39
  29. package/dist/tileset/helpers/tiles-3d-lod.js +97 -85
  30. package/dist/tileset/helpers/transform-utils.js +45 -44
  31. package/dist/tileset/helpers/zoom.d.ts +1 -1
  32. package/dist/tileset/helpers/zoom.d.ts.map +1 -1
  33. package/dist/tileset/helpers/zoom.js +70 -40
  34. package/dist/tileset/tile-3d.d.ts +5 -5
  35. package/dist/tileset/tile-3d.d.ts.map +1 -1
  36. package/dist/tileset/tile-3d.js +595 -399
  37. package/dist/tileset/tileset-3d.d.ts +5 -5
  38. package/dist/tileset/tileset-3d.d.ts.map +1 -1
  39. package/dist/tileset/tileset-3d.js +721 -597
  40. package/dist/tileset/tileset-cache.d.ts +2 -2
  41. package/dist/tileset/tileset-cache.d.ts.map +1 -1
  42. package/dist/tileset/tileset-cache.js +62 -48
  43. package/dist/tileset/tileset-traverser.d.ts +3 -3
  44. package/dist/tileset/tileset-traverser.d.ts.map +1 -1
  45. package/dist/tileset/tileset-traverser.js +281 -220
  46. package/dist/types.js +0 -1
  47. package/dist/utils/doubly-linked-list-node.js +14 -9
  48. package/dist/utils/doubly-linked-list.d.ts +1 -1
  49. package/dist/utils/doubly-linked-list.d.ts.map +1 -1
  50. package/dist/utils/doubly-linked-list.js +85 -56
  51. package/dist/utils/managed-array.js +138 -76
  52. package/package.json +10 -9
  53. package/dist/constants.js.map +0 -1
  54. package/dist/index.js.map +0 -1
  55. package/dist/tileset/format-3d-tiles/tileset-3d-traverser.js.map +0 -1
  56. package/dist/tileset/format-i3s/i3s-pending-tiles-register.js.map +0 -1
  57. package/dist/tileset/format-i3s/i3s-tile-manager.js.map +0 -1
  58. package/dist/tileset/format-i3s/i3s-tileset-traverser.js.map +0 -1
  59. package/dist/tileset/helpers/3d-tiles-options.js.map +0 -1
  60. package/dist/tileset/helpers/bounding-volume.js.map +0 -1
  61. package/dist/tileset/helpers/frame-state.js.map +0 -1
  62. package/dist/tileset/helpers/i3s-lod.js.map +0 -1
  63. package/dist/tileset/helpers/tiles-3d-lod.js.map +0 -1
  64. package/dist/tileset/helpers/transform-utils.js.map +0 -1
  65. package/dist/tileset/helpers/zoom.js.map +0 -1
  66. package/dist/tileset/tile-3d.js.map +0 -1
  67. package/dist/tileset/tileset-3d.js.map +0 -1
  68. package/dist/tileset/tileset-cache.js.map +0 -1
  69. package/dist/tileset/tileset-traverser.js.map +0 -1
  70. package/dist/types.js.map +0 -1
  71. package/dist/utils/doubly-linked-list-node.js.map +0 -1
  72. package/dist/utils/doubly-linked-list.js.map +0 -1
  73. package/dist/utils/managed-array.js.map +0 -1
@@ -3,107 +3,126 @@ import { CullingVolume, Plane } from '@math.gl/culling';
3
3
  import { Ellipsoid } from '@math.gl/geospatial';
4
4
  const scratchVector = new Vector3();
5
5
  const scratchPosition = new Vector3();
6
- const cullingVolume = new CullingVolume([new Plane(), new Plane(), new Plane(), new Plane(), new Plane(), new Plane()]);
6
+ const cullingVolume = new CullingVolume([
7
+ new Plane(),
8
+ new Plane(),
9
+ new Plane(),
10
+ new Plane(),
11
+ new Plane(),
12
+ new Plane()
13
+ ]);
14
+ // Extracts a frame state appropriate for tile culling from a deck.gl viewport
15
+ // TODO - this could likely be generalized and merged back into deck.gl for other culling scenarios
7
16
  export function getFrameState(viewport, frameNumber) {
8
- const {
9
- cameraDirection,
10
- cameraUp,
11
- height
12
- } = viewport;
13
- const {
14
- metersPerUnit
15
- } = viewport.distanceScales;
16
- const viewportCenterCartesian = worldToCartesian(viewport, viewport.center);
17
- const enuToFixedTransform = Ellipsoid.WGS84.eastNorthUpToFixedFrame(viewportCenterCartesian);
18
- const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
19
- const cameraPositionCartesian = Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, new Vector3());
20
- const cameraDirectionCartesian = new Vector3(enuToFixedTransform.transformAsVector(new Vector3(cameraDirection).scale(metersPerUnit))).normalize();
21
- const cameraUpCartesian = new Vector3(enuToFixedTransform.transformAsVector(new Vector3(cameraUp).scale(metersPerUnit))).normalize();
22
- commonSpacePlanesToWGS84(viewport);
23
- const ViewportClass = viewport.constructor;
24
- const {
25
- longitude,
26
- latitude,
27
- width,
28
- bearing,
29
- zoom
30
- } = viewport;
31
- const topDownViewport = new ViewportClass({
32
- longitude,
33
- latitude,
34
- height,
35
- width,
36
- bearing,
37
- zoom,
38
- pitch: 0
39
- });
40
- return {
41
- camera: {
42
- position: cameraPositionCartesian,
43
- direction: cameraDirectionCartesian,
44
- up: cameraUpCartesian
45
- },
46
- viewport,
47
- topDownViewport,
48
- height,
49
- cullingVolume,
50
- frameNumber,
51
- sseDenominator: 1.15
52
- };
17
+ // Traverse and and request. Update _selectedTiles so that we know what to render.
18
+ // Traverse and and request. Update _selectedTiles so that we know what to render.
19
+ const { cameraDirection, cameraUp, height } = viewport;
20
+ const { metersPerUnit } = viewport.distanceScales;
21
+ // TODO - Ellipsoid.eastNorthUpToFixedFrame() breaks on raw array, create a Vector.
22
+ // TODO - Ellipsoid.eastNorthUpToFixedFrame() takes a cartesian, is that intuitive?
23
+ const viewportCenterCartesian = worldToCartesian(viewport, viewport.center);
24
+ const enuToFixedTransform = Ellipsoid.WGS84.eastNorthUpToFixedFrame(viewportCenterCartesian);
25
+ const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
26
+ const cameraPositionCartesian = Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, new Vector3());
27
+ // These should still be normalized as the transform has scale 1 (goes from meters to meters)
28
+ const cameraDirectionCartesian = new Vector3(
29
+ // @ts-ignore
30
+ enuToFixedTransform.transformAsVector(new Vector3(cameraDirection).scale(metersPerUnit))).normalize();
31
+ const cameraUpCartesian = new Vector3(
32
+ // @ts-ignore
33
+ enuToFixedTransform.transformAsVector(new Vector3(cameraUp).scale(metersPerUnit))).normalize();
34
+ commonSpacePlanesToWGS84(viewport);
35
+ const ViewportClass = viewport.constructor;
36
+ const { longitude, latitude, width, bearing, zoom } = viewport;
37
+ // @ts-ignore
38
+ const topDownViewport = new ViewportClass({
39
+ longitude,
40
+ latitude,
41
+ height,
42
+ width,
43
+ bearing,
44
+ zoom,
45
+ pitch: 0
46
+ });
47
+ // TODO: make a file/class for frameState and document what needs to be attached to this so that traversal can function
48
+ return {
49
+ camera: {
50
+ position: cameraPositionCartesian,
51
+ direction: cameraDirectionCartesian,
52
+ up: cameraUpCartesian
53
+ },
54
+ viewport,
55
+ topDownViewport,
56
+ height,
57
+ cullingVolume,
58
+ frameNumber, // TODO: This can be the same between updates, what number is unique for between updates?
59
+ sseDenominator: 1.15 // Assumes fovy = 60 degrees
60
+ };
53
61
  }
62
+ /**
63
+ * Limit `tiles` array length with `maximumTilesSelected` number.
64
+ * The criteria for this filtering is distance of a tile center
65
+ * to the `frameState.viewport`'s longitude and latitude
66
+ * @param tiles - tiles array to filter
67
+ * @param frameState - frameState to calculate distances
68
+ * @param maximumTilesSelected - maximal amount of tiles in the output array
69
+ * @returns new tiles array
70
+ */
54
71
  export function limitSelectedTiles(tiles, frameState, maximumTilesSelected) {
55
- if (maximumTilesSelected === 0 || tiles.length <= maximumTilesSelected) {
56
- return [tiles, []];
57
- }
58
- const tuples = [];
59
- const {
60
- longitude: viewportLongitude,
61
- latitude: viewportLatitude
62
- } = frameState.viewport;
63
- for (const [index, tile] of tiles.entries()) {
64
- const [longitude, latitude] = tile.header.mbs;
65
- const deltaLon = Math.abs(viewportLongitude - longitude);
66
- const deltaLat = Math.abs(viewportLatitude - latitude);
67
- const distance = Math.sqrt(deltaLat * deltaLat + deltaLon * deltaLon);
68
- tuples.push([index, distance]);
69
- }
70
- const tuplesSorted = tuples.sort((a, b) => a[1] - b[1]);
71
- const selectedTiles = [];
72
- for (let i = 0; i < maximumTilesSelected; i++) {
73
- selectedTiles.push(tiles[tuplesSorted[i][0]]);
74
- }
75
- const unselectedTiles = [];
76
- for (let i = maximumTilesSelected; i < tuplesSorted.length; i++) {
77
- unselectedTiles.push(tiles[tuplesSorted[i][0]]);
78
- }
79
- return [selectedTiles, unselectedTiles];
72
+ if (maximumTilesSelected === 0 || tiles.length <= maximumTilesSelected) {
73
+ return [tiles, []];
74
+ }
75
+ // Accumulate distances in couples array: [tileIndex: number, distanceToViewport: number]
76
+ const tuples = [];
77
+ const { longitude: viewportLongitude, latitude: viewportLatitude } = frameState.viewport;
78
+ for (const [index, tile] of tiles.entries()) {
79
+ const [longitude, latitude] = tile.header.mbs;
80
+ const deltaLon = Math.abs(viewportLongitude - longitude);
81
+ const deltaLat = Math.abs(viewportLatitude - latitude);
82
+ const distance = Math.sqrt(deltaLat * deltaLat + deltaLon * deltaLon);
83
+ tuples.push([index, distance]);
84
+ }
85
+ const tuplesSorted = tuples.sort((a, b) => a[1] - b[1]);
86
+ const selectedTiles = [];
87
+ for (let i = 0; i < maximumTilesSelected; i++) {
88
+ selectedTiles.push(tiles[tuplesSorted[i][0]]);
89
+ }
90
+ const unselectedTiles = [];
91
+ for (let i = maximumTilesSelected; i < tuplesSorted.length; i++) {
92
+ unselectedTiles.push(tiles[tuplesSorted[i][0]]);
93
+ }
94
+ return [selectedTiles, unselectedTiles];
80
95
  }
81
96
  function commonSpacePlanesToWGS84(viewport) {
82
- const frustumPlanes = viewport.getFrustumPlanes();
83
- const nearCenterCommon = closestPointOnPlane(frustumPlanes.near, viewport.cameraPosition);
84
- const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
85
- const cameraCartesian = worldToCartesian(viewport, viewport.cameraPosition, scratchPosition);
86
- let i = 0;
87
- cullingVolume.planes[i++].fromPointNormal(nearCenterCartesian, scratchVector.copy(nearCenterCartesian).subtract(cameraCartesian));
88
- for (const dir in frustumPlanes) {
89
- if (dir === 'near') {
90
- continue;
97
+ // Extract frustum planes based on current view.
98
+ const frustumPlanes = viewport.getFrustumPlanes();
99
+ // Get the near/far plane centers
100
+ const nearCenterCommon = closestPointOnPlane(frustumPlanes.near, viewport.cameraPosition);
101
+ const nearCenterCartesian = worldToCartesian(viewport, nearCenterCommon);
102
+ const cameraCartesian = worldToCartesian(viewport, viewport.cameraPosition, scratchPosition);
103
+ let i = 0;
104
+ cullingVolume.planes[i++].fromPointNormal(nearCenterCartesian, scratchVector.copy(nearCenterCartesian).subtract(cameraCartesian));
105
+ for (const dir in frustumPlanes) {
106
+ if (dir === 'near') {
107
+ continue; // eslint-disable-line no-continue
108
+ }
109
+ const plane = frustumPlanes[dir];
110
+ const posCommon = closestPointOnPlane(plane, nearCenterCommon, scratchPosition);
111
+ const cartesianPos = worldToCartesian(viewport, posCommon, scratchPosition);
112
+ cullingVolume.planes[i++].fromPointNormal(cartesianPos,
113
+ // Want the normal to point into the frustum since that's what culling expects
114
+ scratchVector.copy(nearCenterCartesian).subtract(cartesianPos));
91
115
  }
92
- const plane = frustumPlanes[dir];
93
- const posCommon = closestPointOnPlane(plane, nearCenterCommon, scratchPosition);
94
- const cartesianPos = worldToCartesian(viewport, posCommon, scratchPosition);
95
- cullingVolume.planes[i++].fromPointNormal(cartesianPos, scratchVector.copy(nearCenterCartesian).subtract(cartesianPos));
96
- }
97
116
  }
98
- function closestPointOnPlane(plane, refPoint) {
99
- let out = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Vector3();
100
- const distanceToRef = plane.normal.dot(refPoint);
101
- out.copy(plane.normal).scale(plane.distance - distanceToRef).add(refPoint);
102
- return out;
117
+ function closestPointOnPlane(plane, refPoint, out = new Vector3()) {
118
+ const distanceToRef = plane.normal.dot(refPoint);
119
+ out
120
+ .copy(plane.normal)
121
+ .scale(plane.distance - distanceToRef)
122
+ .add(refPoint);
123
+ return out;
103
124
  }
104
- function worldToCartesian(viewport, point) {
105
- let out = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : new Vector3();
106
- const cartographicPos = viewport.unprojectPosition(point);
107
- return Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, out);
125
+ function worldToCartesian(viewport, point, out = new Vector3()) {
126
+ const cartographicPos = viewport.unprojectPosition(point);
127
+ return Ellipsoid.WGS84.cartographicToCartesian(cartographicPos, out);
108
128
  }
109
- //# sourceMappingURL=frame-state.js.map
@@ -1,5 +1,5 @@
1
- import { Tile3D } from '../tile-3d';
2
- import { FrameState } from './frame-state';
1
+ import { Tile3D } from "../tile-3d.js";
2
+ import { FrameState } from "./frame-state.js";
3
3
  /**
4
4
  * For the maxScreenThreshold error metric, maxError means that you should replace the node with it's children
5
5
  as soon as the nodes bounding sphere has a screen radius larger than maxError pixels.
@@ -1 +1 @@
1
- {"version":3,"file":"i3s-lod.d.ts","sourceRoot":"","sources":["../../../src/tileset/helpers/i3s-lod.ts"],"names":[],"mappings":"AAMA,OAAO,EAAC,MAAM,EAAC,MAAM,YAAY,CAAC;AAClC,OAAO,EAAC,UAAU,EAAC,MAAM,eAAe,CAAC;AAUzC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAczF;AAED;;;;;GAKG;AAEH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CA6C/E"}
1
+ {"version":3,"file":"i3s-lod.d.ts","sourceRoot":"","sources":["../../../src/tileset/helpers/i3s-lod.ts"],"names":[],"mappings":"AAMA,OAAO,EAAC,MAAM,EAAC,sBAAmB;AAClC,OAAO,EAAC,UAAU,EAAC,yBAAsB;AAUzC;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,KAAK,GAAG,KAAK,GAAG,MAAM,CAczF;AAED;;;;;GAKG;AAEH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,UAAU,GAAG,MAAM,CA6C/E"}
@@ -1,3 +1,6 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
1
4
  import { Matrix4, Vector3 } from '@math.gl/core';
2
5
  import { Ellipsoid } from '@math.gl/geospatial';
3
6
  const cameraPositionCartesian = new Vector3();
@@ -7,47 +10,73 @@ const extraVertexEnu = new Vector3();
7
10
  const projectedOriginVector = new Vector3();
8
11
  const enuToCartesianMatrix = new Matrix4();
9
12
  const cartesianToEnuMatrix = new Matrix4();
13
+ /**
14
+ * For the maxScreenThreshold error metric, maxError means that you should replace the node with it's children
15
+ as soon as the nodes bounding sphere has a screen radius larger than maxError pixels.
16
+ In this sense a value of 0 means you should always load it's children,
17
+ or if it's a leaf node, you should always display it.
18
+ * @param tile
19
+ * @param frameState
20
+ * @returns
21
+ */
10
22
  export function getLodStatus(tile, frameState) {
11
- if (tile.lodMetricValue === 0 || isNaN(tile.lodMetricValue)) {
12
- return 'DIG';
13
- }
14
- const screenSize = 2 * getProjectedRadius(tile, frameState);
15
- if (screenSize < 2) {
23
+ if (tile.lodMetricValue === 0 || isNaN(tile.lodMetricValue)) {
24
+ return 'DIG';
25
+ }
26
+ const screenSize = 2 * getProjectedRadius(tile, frameState);
27
+ if (screenSize < 2) {
28
+ return 'OUT';
29
+ }
30
+ if (!tile.header.children || screenSize <= tile.lodMetricValue) {
31
+ return 'DRAW';
32
+ }
33
+ else if (tile.header.children) {
34
+ return 'DIG';
35
+ }
16
36
  return 'OUT';
17
- }
18
- if (!tile.header.children || screenSize <= tile.lodMetricValue) {
19
- return 'DRAW';
20
- } else if (tile.header.children) {
21
- return 'DIG';
22
- }
23
- return 'OUT';
24
37
  }
38
+ /**
39
+ * Calculate size of MBS radius projected on the screen plane
40
+ * @param tile
41
+ * @param frameState
42
+ * @returns
43
+ */
44
+ // eslint-disable-next-line max-statements
25
45
  export function getProjectedRadius(tile, frameState) {
26
- const {
27
- topDownViewport: viewport
28
- } = frameState;
29
- const mbsLat = tile.header.mbs[1];
30
- const mbsLon = tile.header.mbs[0];
31
- const mbsZ = tile.header.mbs[2];
32
- const mbsR = tile.header.mbs[3];
33
- const mbsCenterCartesian = [...tile.boundingVolume.center];
34
- const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
35
- Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, cameraPositionCartesian);
36
- toEye.copy(cameraPositionCartesian).subtract(mbsCenterCartesian).normalize();
37
- Ellipsoid.WGS84.eastNorthUpToFixedFrame(mbsCenterCartesian, enuToCartesianMatrix);
38
- cartesianToEnuMatrix.copy(enuToCartesianMatrix).invert();
39
- cameraPositionEnu.copy(cameraPositionCartesian).transform(cartesianToEnuMatrix);
40
- const projection = Math.sqrt(cameraPositionEnu[0] * cameraPositionEnu[0] + cameraPositionEnu[1] * cameraPositionEnu[1]);
41
- const extraZ = projection * projection / cameraPositionEnu[2];
42
- extraVertexEnu.copy([cameraPositionEnu[0], cameraPositionEnu[1], extraZ]);
43
- const extraVertexCartesian = extraVertexEnu.transform(enuToCartesianMatrix);
44
- const extraVectorCartesian = extraVertexCartesian.subtract(mbsCenterCartesian).normalize();
45
- const radiusVector = toEye.cross(extraVectorCartesian).normalize().scale(mbsR);
46
- const sphereMbsBorderVertexCartesian = radiusVector.add(mbsCenterCartesian);
47
- const sphereMbsBorderVertexCartographic = Ellipsoid.WGS84.cartesianToCartographic(sphereMbsBorderVertexCartesian);
48
- const projectedOrigin = viewport.project([mbsLon, mbsLat, mbsZ]);
49
- const projectedMbsBorderVertex = viewport.project(sphereMbsBorderVertexCartographic);
50
- const projectedRadius = projectedOriginVector.copy(projectedOrigin).subtract(projectedMbsBorderVertex).magnitude();
51
- return projectedRadius;
46
+ const { topDownViewport: viewport } = frameState;
47
+ const mbsLat = tile.header.mbs[1];
48
+ const mbsLon = tile.header.mbs[0];
49
+ const mbsZ = tile.header.mbs[2];
50
+ const mbsR = tile.header.mbs[3];
51
+ const mbsCenterCartesian = [...tile.boundingVolume.center];
52
+ const cameraPositionCartographic = viewport.unprojectPosition(viewport.cameraPosition);
53
+ Ellipsoid.WGS84.cartographicToCartesian(cameraPositionCartographic, cameraPositionCartesian);
54
+ // ---------------------------
55
+ // Calculate mbs border vertex
56
+ // ---------------------------
57
+ toEye.copy(cameraPositionCartesian).subtract(mbsCenterCartesian).normalize();
58
+ // Add extra vector to form plane
59
+ Ellipsoid.WGS84.eastNorthUpToFixedFrame(mbsCenterCartesian, enuToCartesianMatrix);
60
+ cartesianToEnuMatrix.copy(enuToCartesianMatrix).invert();
61
+ cameraPositionEnu.copy(cameraPositionCartesian).transform(cartesianToEnuMatrix);
62
+ // Mean Proportionals in Right Triangles - Altitude rule
63
+ // https://mathbitsnotebook.com/Geometry/RightTriangles/RTmeanRight.html
64
+ const projection = Math.sqrt(cameraPositionEnu[0] * cameraPositionEnu[0] + cameraPositionEnu[1] * cameraPositionEnu[1]);
65
+ const extraZ = (projection * projection) / cameraPositionEnu[2];
66
+ extraVertexEnu.copy([cameraPositionEnu[0], cameraPositionEnu[1], extraZ]);
67
+ const extraVertexCartesian = extraVertexEnu.transform(enuToCartesianMatrix);
68
+ const extraVectorCartesian = extraVertexCartesian.subtract(mbsCenterCartesian).normalize();
69
+ // We need radius vector orthogonal to toEye vector
70
+ const radiusVector = toEye.cross(extraVectorCartesian).normalize().scale(mbsR);
71
+ const sphereMbsBorderVertexCartesian = radiusVector.add(mbsCenterCartesian);
72
+ const sphereMbsBorderVertexCartographic = Ellipsoid.WGS84.cartesianToCartographic(sphereMbsBorderVertexCartesian);
73
+ // ---------------------------
74
+ // Project center vertex and border vertex and calculate projected radius of MBS
75
+ const projectedOrigin = viewport.project([mbsLon, mbsLat, mbsZ]);
76
+ const projectedMbsBorderVertex = viewport.project(sphereMbsBorderVertexCartographic);
77
+ const projectedRadius = projectedOriginVector
78
+ .copy(projectedOrigin)
79
+ .subtract(projectedMbsBorderVertex)
80
+ .magnitude();
81
+ return projectedRadius;
52
82
  }
53
- //# sourceMappingURL=i3s-lod.js.map
@@ -1,3 +1,12 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
4
+ // This file is derived from the Cesium code base under Apache 2 license
5
+ // See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md
6
+ // TODO - Dynamic screen space error provides an optimization when looking at
7
+ // tilesets from above
8
+ /* eslint-disable */
9
+ // @ts-nocheck
1
10
  import { Matrix4, Vector3, clamp } from '@math.gl/core';
2
11
  const scratchPositionNormal = new Vector3();
3
12
  const scratchCartographic = new Vector3();
@@ -5,96 +14,99 @@ const scratchMatrix = new Matrix4();
5
14
  const scratchCenter = new Vector3();
6
15
  const scratchPosition = new Vector3();
7
16
  const scratchDirection = new Vector3();
8
- export function calculateDynamicScreenSpaceError(root, _ref) {
9
- let {
10
- camera,
11
- mapProjection
12
- } = _ref;
13
- let options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
14
- const {
15
- dynamicScreenSpaceErrorHeightFalloff = 0.25,
16
- dynamicScreenSpaceErrorDensity = 0.00278
17
- } = options;
18
- let up;
19
- let direction;
20
- let height;
21
- let minimumHeight;
22
- let maximumHeight;
23
- const tileBoundingVolume = root.contentBoundingVolume;
24
- if (tileBoundingVolume instanceof TileBoundingRegion) {
25
- up = Cartesian3.normalize(camera.positionWC, scratchPositionNormal);
26
- direction = camera.directionWC;
27
- height = camera.positionCartographic.height;
28
- minimumHeight = tileBoundingVolume.minimumHeight;
29
- maximumHeight = tileBoundingVolume.maximumHeight;
30
- } else {
31
- const transformLocal = Matrix4.inverseTransformation(root.computedTransform, scratchMatrix);
32
- const ellipsoid = mapProjection.ellipsoid;
33
- const boundingVolume = tileBoundingVolume.boundingVolume;
34
- const centerLocal = Matrix4.multiplyByPoint(transformLocal, boundingVolume.center, scratchCenter);
35
- if (Cartesian3.magnitude(centerLocal) > ellipsoid.minimumRadius) {
36
- const centerCartographic = Cartographic.fromCartesian(centerLocal, ellipsoid, scratchCartographic);
37
- up = Cartesian3.normalize(camera.positionWC, scratchPositionNormal);
38
- direction = camera.directionWC;
39
- height = camera.positionCartographic.height;
40
- minimumHeight = 0.0;
41
- maximumHeight = centerCartographic.height * 2.0;
42
- } else {
43
- const positionLocal = Matrix4.multiplyByPoint(transformLocal, camera.positionWC, scratchPosition);
44
- up = Cartesian3.UNIT_Z;
45
- direction = Matrix4.multiplyByPointAsVector(transformLocal, camera.directionWC, scratchDirection);
46
- direction = Cartesian3.normalize(direction, direction);
47
- height = positionLocal.z;
48
- if (tileBoundingVolume instanceof TileOrientedBoundingBox) {
49
- const boxHeight = root._header.boundingVolume.box[11];
50
- minimumHeight = centerLocal.z - boxHeight;
51
- maximumHeight = centerLocal.z + boxHeight;
52
- } else if (tileBoundingVolume instanceof TileBoundingSphere) {
53
- const radius = boundingVolume.radius;
54
- minimumHeight = centerLocal.z - radius;
55
- maximumHeight = centerLocal.z + radius;
56
- }
17
+ // eslint-disable-next-line max-statements, complexity
18
+ export function calculateDynamicScreenSpaceError(root, { camera, mapProjection }, options = {}) {
19
+ const { dynamicScreenSpaceErrorHeightFalloff = 0.25, dynamicScreenSpaceErrorDensity = 0.00278 } = options;
20
+ let up;
21
+ let direction;
22
+ let height;
23
+ let minimumHeight;
24
+ let maximumHeight;
25
+ const tileBoundingVolume = root.contentBoundingVolume;
26
+ if (tileBoundingVolume instanceof TileBoundingRegion) {
27
+ up = Cartesian3.normalize(camera.positionWC, scratchPositionNormal);
28
+ direction = camera.directionWC;
29
+ height = camera.positionCartographic.height;
30
+ minimumHeight = tileBoundingVolume.minimumHeight;
31
+ maximumHeight = tileBoundingVolume.maximumHeight;
57
32
  }
58
- }
59
- const heightFalloff = dynamicScreenSpaceErrorHeightFalloff;
60
- const heightClose = minimumHeight + (maximumHeight - minimumHeight) * heightFalloff;
61
- const heightFar = maximumHeight;
62
- const t = clamp((height - heightClose) / (heightFar - heightClose), 0.0, 1.0);
63
- const dot = Math.abs(Cartesian3.dot(direction, up));
64
- let horizonFactor = 1.0 - dot;
65
- horizonFactor = horizonFactor * (1.0 - t);
66
- return dynamicScreenSpaceErrorDensity * horizonFactor;
33
+ else {
34
+ // Transform camera position and direction into the local coordinate system of the tileset
35
+ const transformLocal = Matrix4.inverseTransformation(root.computedTransform, scratchMatrix);
36
+ const ellipsoid = mapProjection.ellipsoid;
37
+ const boundingVolume = tileBoundingVolume.boundingVolume;
38
+ const centerLocal = Matrix4.multiplyByPoint(transformLocal, boundingVolume.center, scratchCenter);
39
+ if (Cartesian3.magnitude(centerLocal) > ellipsoid.minimumRadius) {
40
+ // The tileset is defined in WGS84. Approximate the minimum and maximum height.
41
+ const centerCartographic = Cartographic.fromCartesian(centerLocal, ellipsoid, scratchCartographic);
42
+ up = Cartesian3.normalize(camera.positionWC, scratchPositionNormal);
43
+ direction = camera.directionWC;
44
+ height = camera.positionCartographic.height;
45
+ minimumHeight = 0.0;
46
+ maximumHeight = centerCartographic.height * 2.0;
47
+ }
48
+ else {
49
+ // The tileset is defined in local coordinates (z-up)
50
+ const positionLocal = Matrix4.multiplyByPoint(transformLocal, camera.positionWC, scratchPosition);
51
+ up = Cartesian3.UNIT_Z;
52
+ direction = Matrix4.multiplyByPointAsVector(transformLocal, camera.directionWC, scratchDirection);
53
+ direction = Cartesian3.normalize(direction, direction);
54
+ height = positionLocal.z;
55
+ if (tileBoundingVolume instanceof TileOrientedBoundingBox) {
56
+ // Assuming z-up, the last component stores the half-height of the box
57
+ const boxHeight = root._header.boundingVolume.box[11];
58
+ minimumHeight = centerLocal.z - boxHeight;
59
+ maximumHeight = centerLocal.z + boxHeight;
60
+ }
61
+ else if (tileBoundingVolume instanceof TileBoundingSphere) {
62
+ const radius = boundingVolume.radius;
63
+ minimumHeight = centerLocal.z - radius;
64
+ maximumHeight = centerLocal.z + radius;
65
+ }
66
+ }
67
+ }
68
+ // The range where the density starts to lessen. Start at the quarter height of the tileset.
69
+ const heightFalloff = dynamicScreenSpaceErrorHeightFalloff;
70
+ const heightClose = minimumHeight + (maximumHeight - minimumHeight) * heightFalloff;
71
+ const heightFar = maximumHeight;
72
+ const t = clamp((height - heightClose) / (heightFar - heightClose), 0.0, 1.0);
73
+ // Increase density as the camera tilts towards the horizon
74
+ const dot = Math.abs(Cartesian3.dot(direction, up));
75
+ let horizonFactor = 1.0 - dot;
76
+ // Weaken the horizon factor as the camera height increases, implying the camera is further away from the tileset.
77
+ // The goal is to increase density for the "street view", not when viewing the tileset from a distance.
78
+ horizonFactor = horizonFactor * (1.0 - t);
79
+ return dynamicScreenSpaceErrorDensity * horizonFactor;
67
80
  }
68
81
  export function fog(distanceToCamera, density) {
69
- const scalar = distanceToCamera * density;
70
- return 1.0 - Math.exp(-(scalar * scalar));
82
+ const scalar = distanceToCamera * density;
83
+ return 1.0 - Math.exp(-(scalar * scalar));
71
84
  }
72
85
  export function getDynamicScreenSpaceError(tileset, distanceToCamera) {
73
- if (tileset.dynamicScreenSpaceError && tileset.dynamicScreenSpaceErrorComputedDensity) {
74
- const density = tileset.dynamicScreenSpaceErrorComputedDensity;
75
- const factor = tileset.dynamicScreenSpaceErrorFactor;
76
- const dynamicError = fog(distanceToCamera, density) * factor;
77
- return dynamicError;
78
- }
79
- return 0;
86
+ if (tileset.dynamicScreenSpaceError && tileset.dynamicScreenSpaceErrorComputedDensity) {
87
+ const density = tileset.dynamicScreenSpaceErrorComputedDensity;
88
+ const factor = tileset.dynamicScreenSpaceErrorFactor;
89
+ // TODO: Refined screen space error that minimizes tiles in non-first-person
90
+ const dynamicError = fog(distanceToCamera, density) * factor;
91
+ return dynamicError;
92
+ }
93
+ return 0;
80
94
  }
81
95
  export function getTiles3DScreenSpaceError(tile, frameState, useParentLodMetric) {
82
- const tileset = tile.tileset;
83
- const parentLodMetricValue = tile.parent && tile.parent.lodMetricValue || tile.lodMetricValue;
84
- const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
85
- if (lodMetricValue === 0.0) {
86
- return 0.0;
87
- }
88
- const distance = Math.max(tile._distanceToCamera, 1e-7);
89
- const {
90
- height,
91
- sseDenominator
92
- } = frameState;
93
- const {
94
- viewDistanceScale
95
- } = tileset.options;
96
- let error = lodMetricValue * height * (viewDistanceScale || 1.0) / (distance * sseDenominator);
97
- error -= getDynamicScreenSpaceError(tileset, distance);
98
- return error;
96
+ const tileset = tile.tileset;
97
+ const parentLodMetricValue = (tile.parent && tile.parent.lodMetricValue) || tile.lodMetricValue;
98
+ const lodMetricValue = useParentLodMetric ? parentLodMetricValue : tile.lodMetricValue;
99
+ // Leaf tiles do not have any error so save the computation
100
+ if (lodMetricValue === 0.0) {
101
+ return 0.0;
102
+ }
103
+ // TODO: Orthographic Frustum needs special treatment?
104
+ // this._getOrthograhicScreenSpaceError();
105
+ // Avoid divide by zero when viewer is inside the tile
106
+ const distance = Math.max(tile._distanceToCamera, 1e-7);
107
+ const { height, sseDenominator } = frameState;
108
+ const { viewDistanceScale } = tileset.options;
109
+ let error = (lodMetricValue * height * (viewDistanceScale || 1.0)) / (distance * sseDenominator);
110
+ error -= getDynamicScreenSpaceError(tileset, distance);
111
+ return error;
99
112
  }
100
- //# sourceMappingURL=tiles-3d-lod.js.map