@loaders.gl/terrain 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.
@@ -1,89 +1,126 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
1
4
  import { concatenateTypedArrays } from '@loaders.gl/loader-utils';
5
+ /**
6
+ * Add skirt to existing mesh
7
+ * @param {object} attributes - POSITION and TEXCOOD_0 attributes data
8
+ * @param {any} triangles - indices array of the mesh geometry
9
+ * @param skirtHeight - height of the skirt geometry
10
+ * @param outsideIndices - edge indices from quantized mesh data
11
+ * @returns - geometry data with added skirt
12
+ */
2
13
  export function addSkirt(attributes, triangles, skirtHeight, outsideIndices) {
3
- const outsideEdges = outsideIndices ? getOutsideEdgesFromIndices(outsideIndices, attributes.POSITION.value) : getOutsideEdgesFromTriangles(triangles);
4
- const newPosition = new attributes.POSITION.value.constructor(outsideEdges.length * 6);
5
- const newTexcoord0 = new attributes.TEXCOORD_0.value.constructor(outsideEdges.length * 4);
6
- const newTriangles = new triangles.constructor(outsideEdges.length * 6);
7
- for (let i = 0; i < outsideEdges.length; i++) {
8
- const edge = outsideEdges[i];
9
- updateAttributesForNewEdge({
10
- edge,
11
- edgeIndex: i,
12
- attributes,
13
- skirtHeight,
14
- newPosition,
15
- newTexcoord0,
16
- newTriangles
17
- });
18
- }
19
- attributes.POSITION.value = concatenateTypedArrays(attributes.POSITION.value, newPosition);
20
- attributes.TEXCOORD_0.value = concatenateTypedArrays(attributes.TEXCOORD_0.value, newTexcoord0);
21
- const resultTriangles = triangles instanceof Array ? triangles.concat(newTriangles) : concatenateTypedArrays(triangles, newTriangles);
22
- return {
23
- attributes,
24
- triangles: resultTriangles
25
- };
14
+ const outsideEdges = outsideIndices
15
+ ? getOutsideEdgesFromIndices(outsideIndices, attributes.POSITION.value)
16
+ : getOutsideEdgesFromTriangles(triangles);
17
+ // 2 new vertices for each outside edge
18
+ const newPosition = new attributes.POSITION.value.constructor(outsideEdges.length * 6);
19
+ const newTexcoord0 = new attributes.TEXCOORD_0.value.constructor(outsideEdges.length * 4);
20
+ // 2 new triangles for each outside edge
21
+ const newTriangles = new triangles.constructor(outsideEdges.length * 6);
22
+ for (let i = 0; i < outsideEdges.length; i++) {
23
+ const edge = outsideEdges[i];
24
+ updateAttributesForNewEdge({
25
+ edge,
26
+ edgeIndex: i,
27
+ attributes,
28
+ skirtHeight,
29
+ newPosition,
30
+ newTexcoord0,
31
+ newTriangles
32
+ });
33
+ }
34
+ attributes.POSITION.value = concatenateTypedArrays(attributes.POSITION.value, newPosition);
35
+ attributes.TEXCOORD_0.value = concatenateTypedArrays(attributes.TEXCOORD_0.value, newTexcoord0);
36
+ const resultTriangles = triangles instanceof Array
37
+ ? triangles.concat(newTriangles)
38
+ : concatenateTypedArrays(triangles, newTriangles);
39
+ return {
40
+ attributes,
41
+ triangles: resultTriangles
42
+ };
26
43
  }
44
+ /**
45
+ * Get geometry edges that located on a border of the mesh
46
+ * @param {any} triangles - indices array of the mesh geometry
47
+ * @returns {number[][]} - outside edges data
48
+ */
27
49
  function getOutsideEdgesFromTriangles(triangles) {
28
- const edges = [];
29
- for (let i = 0; i < triangles.length; i += 3) {
30
- edges.push([triangles[i], triangles[i + 1]]);
31
- edges.push([triangles[i + 1], triangles[i + 2]]);
32
- edges.push([triangles[i + 2], triangles[i]]);
33
- }
34
- edges.sort((a, b) => Math.min(...a) - Math.min(...b) || Math.max(...a) - Math.max(...b));
35
- const outsideEdges = [];
36
- let index = 0;
37
- while (index < edges.length) {
38
- var _edges, _edges2;
39
- if (edges[index][0] === ((_edges = edges[index + 1]) === null || _edges === void 0 ? void 0 : _edges[1]) && edges[index][1] === ((_edges2 = edges[index + 1]) === null || _edges2 === void 0 ? void 0 : _edges2[0])) {
40
- index += 2;
41
- } else {
42
- outsideEdges.push(edges[index]);
43
- index++;
50
+ const edges = [];
51
+ for (let i = 0; i < triangles.length; i += 3) {
52
+ edges.push([triangles[i], triangles[i + 1]]);
53
+ edges.push([triangles[i + 1], triangles[i + 2]]);
54
+ edges.push([triangles[i + 2], triangles[i]]);
55
+ }
56
+ edges.sort((a, b) => Math.min(...a) - Math.min(...b) || Math.max(...a) - Math.max(...b));
57
+ const outsideEdges = [];
58
+ let index = 0;
59
+ while (index < edges.length) {
60
+ if (edges[index][0] === edges[index + 1]?.[1] && edges[index][1] === edges[index + 1]?.[0]) {
61
+ index += 2;
62
+ }
63
+ else {
64
+ outsideEdges.push(edges[index]);
65
+ index++;
66
+ }
44
67
  }
45
- }
46
- return outsideEdges;
68
+ return outsideEdges;
47
69
  }
70
+ /**
71
+ * Get geometry edges that located on a border of the mesh
72
+ * @param {object} indices - edge indices from quantized mesh data
73
+ * @param {TypedArray} position - position attribute geometry data
74
+ * @returns {number[][]} - outside edges data
75
+ */
48
76
  function getOutsideEdgesFromIndices(indices, position) {
49
- indices.westIndices.sort((a, b) => position[3 * a + 1] - position[3 * b + 1]);
50
- indices.eastIndices.sort((a, b) => position[3 * b + 1] - position[3 * a + 1]);
51
- indices.southIndices.sort((a, b) => position[3 * b] - position[3 * a]);
52
- indices.northIndices.sort((a, b) => position[3 * a] - position[3 * b]);
53
- const edges = [];
54
- for (const index in indices) {
55
- const indexGroup = indices[index];
56
- for (let i = 0; i < indexGroup.length - 1; i++) {
57
- edges.push([indexGroup[i], indexGroup[i + 1]]);
77
+ // Sort skirt indices to create adjacent triangles
78
+ indices.westIndices.sort((a, b) => position[3 * a + 1] - position[3 * b + 1]);
79
+ // Reverse (b - a) to match triangle winding
80
+ indices.eastIndices.sort((a, b) => position[3 * b + 1] - position[3 * a + 1]);
81
+ indices.southIndices.sort((a, b) => position[3 * b] - position[3 * a]);
82
+ // Reverse (b - a) to match triangle winding
83
+ indices.northIndices.sort((a, b) => position[3 * a] - position[3 * b]);
84
+ const edges = [];
85
+ for (const index in indices) {
86
+ const indexGroup = indices[index];
87
+ for (let i = 0; i < indexGroup.length - 1; i++) {
88
+ edges.push([indexGroup[i], indexGroup[i + 1]]);
89
+ }
58
90
  }
59
- }
60
- return edges;
91
+ return edges;
61
92
  }
62
- function updateAttributesForNewEdge(_ref) {
63
- let {
64
- edge,
65
- edgeIndex,
66
- attributes,
67
- skirtHeight,
68
- newPosition,
69
- newTexcoord0,
70
- newTriangles
71
- } = _ref;
72
- const positionsLength = attributes.POSITION.value.length;
73
- const vertex1Offset = edgeIndex * 2;
74
- const vertex2Offset = edgeIndex * 2 + 1;
75
- newPosition.set(attributes.POSITION.value.subarray(edge[0] * 3, edge[0] * 3 + 3), vertex1Offset * 3);
76
- newPosition[vertex1Offset * 3 + 2] = newPosition[vertex1Offset * 3 + 2] - skirtHeight;
77
- newPosition.set(attributes.POSITION.value.subarray(edge[1] * 3, edge[1] * 3 + 3), vertex2Offset * 3);
78
- newPosition[vertex2Offset * 3 + 2] = newPosition[vertex2Offset * 3 + 2] - skirtHeight;
79
- newTexcoord0.set(attributes.TEXCOORD_0.value.subarray(edge[0] * 2, edge[0] * 2 + 2), vertex1Offset * 2);
80
- newTexcoord0.set(attributes.TEXCOORD_0.value.subarray(edge[1] * 2, edge[1] * 2 + 2), vertex2Offset * 2);
81
- const triangle1Offset = edgeIndex * 2 * 3;
82
- newTriangles[triangle1Offset] = edge[0];
83
- newTriangles[triangle1Offset + 1] = positionsLength / 3 + vertex2Offset;
84
- newTriangles[triangle1Offset + 2] = edge[1];
85
- newTriangles[triangle1Offset + 3] = positionsLength / 3 + vertex2Offset;
86
- newTriangles[triangle1Offset + 4] = edge[0];
87
- newTriangles[triangle1Offset + 5] = positionsLength / 3 + vertex1Offset;
93
+ /**
94
+ * Get geometry edges that located on a border of the mesh
95
+ * @param {object} args
96
+ * @param {number[]} args.edge - edge indices in geometry
97
+ * @param {number} args.edgeIndex - edge index in outsideEdges array
98
+ * @param {object} args.attributes - POSITION and TEXCOORD_0 attributes
99
+ * @param {number} args.skirtHeight - height of the skirt geometry
100
+ * @param {TypedArray} args.newPosition - POSITION array for skirt data
101
+ * @param {TypedArray} args.newTexcoord0 - TEXCOORD_0 array for skirt data
102
+ * @param {TypedArray | Array} args.newTriangles - trinagle indices array for skirt data
103
+ * @returns {void}
104
+ */
105
+ function updateAttributesForNewEdge({ edge, edgeIndex, attributes, skirtHeight, newPosition, newTexcoord0, newTriangles }) {
106
+ const positionsLength = attributes.POSITION.value.length;
107
+ const vertex1Offset = edgeIndex * 2;
108
+ const vertex2Offset = edgeIndex * 2 + 1;
109
+ // Define POSITION for new 1st vertex
110
+ newPosition.set(attributes.POSITION.value.subarray(edge[0] * 3, edge[0] * 3 + 3), vertex1Offset * 3);
111
+ newPosition[vertex1Offset * 3 + 2] = newPosition[vertex1Offset * 3 + 2] - skirtHeight; // put down elevation on the skirt height
112
+ // Define POSITION for new 2nd vertex
113
+ newPosition.set(attributes.POSITION.value.subarray(edge[1] * 3, edge[1] * 3 + 3), vertex2Offset * 3);
114
+ newPosition[vertex2Offset * 3 + 2] = newPosition[vertex2Offset * 3 + 2] - skirtHeight; // put down elevation on the skirt height
115
+ // Use same TEXCOORDS for skirt vertices
116
+ newTexcoord0.set(attributes.TEXCOORD_0.value.subarray(edge[0] * 2, edge[0] * 2 + 2), vertex1Offset * 2);
117
+ newTexcoord0.set(attributes.TEXCOORD_0.value.subarray(edge[1] * 2, edge[1] * 2 + 2), vertex2Offset * 2);
118
+ // Define new triangles
119
+ const triangle1Offset = edgeIndex * 2 * 3;
120
+ newTriangles[triangle1Offset] = edge[0];
121
+ newTriangles[triangle1Offset + 1] = positionsLength / 3 + vertex2Offset;
122
+ newTriangles[triangle1Offset + 2] = edge[1];
123
+ newTriangles[triangle1Offset + 3] = positionsLength / 3 + vertex2Offset;
124
+ newTriangles[triangle1Offset + 4] = edge[0];
125
+ newTriangles[triangle1Offset + 5] = positionsLength / 3 + vertex1Offset;
88
126
  }
89
- //# sourceMappingURL=skirt.js.map
@@ -1,85 +1,73 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
1
4
  import { getMeshBoundingBox } from '@loaders.gl/schema';
2
5
  import decode, { DECODING_STEPS } from "./decode-quantized-mesh.js";
3
6
  import { addSkirt } from "./helpers/skirt.js";
4
- export function parseQuantizedMesh(arrayBuffer) {
5
- let options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
6
- const {
7
- bounds
8
- } = options;
9
- const {
10
- header,
11
- vertexData,
12
- triangleIndices: originalTriangleIndices,
13
- westIndices,
14
- northIndices,
15
- eastIndices,
16
- southIndices
17
- } = decode(arrayBuffer, DECODING_STEPS.triangleIndices);
18
- let triangleIndices = originalTriangleIndices;
19
- let attributes = getMeshAttributes(vertexData, header, bounds);
20
- const boundingBox = getMeshBoundingBox(attributes);
21
- if (options !== null && options !== void 0 && options.skirtHeight) {
22
- const {
23
- attributes: newAttributes,
24
- triangles: newTriangles
25
- } = addSkirt(attributes, triangleIndices, options.skirtHeight, {
26
- westIndices,
27
- northIndices,
28
- eastIndices,
29
- southIndices
30
- });
31
- attributes = newAttributes;
32
- triangleIndices = newTriangles;
33
- }
34
- return {
35
- loaderData: {
36
- header: {}
37
- },
38
- header: {
39
- vertexCount: triangleIndices.length,
40
- boundingBox
41
- },
42
- schema: undefined,
43
- topology: 'triangle-list',
44
- mode: 4,
45
- indices: {
46
- value: triangleIndices,
47
- size: 1
48
- },
49
- attributes
50
- };
7
+ export function parseQuantizedMesh(arrayBuffer, options = {}) {
8
+ const { bounds } = options;
9
+ // Don't parse edge indices or format extensions
10
+ const { header, vertexData, triangleIndices: originalTriangleIndices, westIndices, northIndices, eastIndices, southIndices } = decode(arrayBuffer, DECODING_STEPS.triangleIndices);
11
+ let triangleIndices = originalTriangleIndices;
12
+ let attributes = getMeshAttributes(vertexData, header, bounds);
13
+ // Compute bounding box before adding skirt so that z values are not skewed
14
+ // TODO: Find bounding box from header, instead of doing extra pass over
15
+ // vertices.
16
+ const boundingBox = getMeshBoundingBox(attributes);
17
+ if (options?.skirtHeight) {
18
+ const { attributes: newAttributes, triangles: newTriangles } = addSkirt(attributes, triangleIndices, options.skirtHeight, {
19
+ westIndices,
20
+ northIndices,
21
+ eastIndices,
22
+ southIndices
23
+ });
24
+ attributes = newAttributes;
25
+ triangleIndices = newTriangles;
26
+ }
27
+ return {
28
+ // Data return by this loader implementation
29
+ loaderData: {
30
+ header: {}
31
+ },
32
+ header: {
33
+ // @ts-ignore
34
+ vertexCount: triangleIndices.length,
35
+ boundingBox
36
+ },
37
+ // TODO
38
+ schema: undefined,
39
+ topology: 'triangle-list',
40
+ mode: 4, // TRIANGLES
41
+ indices: { value: triangleIndices, size: 1 },
42
+ attributes
43
+ };
51
44
  }
52
45
  function getMeshAttributes(vertexData, header, bounds) {
53
- const {
54
- minHeight,
55
- maxHeight
56
- } = header;
57
- const [minX, minY, maxX, maxY] = bounds || [0, 0, 1, 1];
58
- const xScale = maxX - minX;
59
- const yScale = maxY - minY;
60
- const zScale = maxHeight - minHeight;
61
- const nCoords = vertexData.length / 3;
62
- const positions = new Float32Array(nCoords * 3);
63
- const texCoords = new Float32Array(nCoords * 2);
64
- for (let i = 0; i < nCoords; i++) {
65
- const x = vertexData[i] / 32767;
66
- const y = vertexData[i + nCoords] / 32767;
67
- const z = vertexData[i + nCoords * 2] / 32767;
68
- positions[3 * i + 0] = x * xScale + minX;
69
- positions[3 * i + 1] = y * yScale + minY;
70
- positions[3 * i + 2] = z * zScale + minHeight;
71
- texCoords[2 * i + 0] = x;
72
- texCoords[2 * i + 1] = y;
73
- }
74
- return {
75
- POSITION: {
76
- value: positions,
77
- size: 3
78
- },
79
- TEXCOORD_0: {
80
- value: texCoords,
81
- size: 2
46
+ const { minHeight, maxHeight } = header;
47
+ const [minX, minY, maxX, maxY] = bounds || [0, 0, 1, 1];
48
+ const xScale = maxX - minX;
49
+ const yScale = maxY - minY;
50
+ const zScale = maxHeight - minHeight;
51
+ const nCoords = vertexData.length / 3;
52
+ // vec3. x, y defined by bounds, z in meters
53
+ const positions = new Float32Array(nCoords * 3);
54
+ // vec2. 1 to 1 relationship with position. represents the uv on the texture image. 0,0 to 1,1.
55
+ const texCoords = new Float32Array(nCoords * 2);
56
+ // Data is not interleaved; all u, then all v, then all heights
57
+ for (let i = 0; i < nCoords; i++) {
58
+ const x = vertexData[i] / 32767;
59
+ const y = vertexData[i + nCoords] / 32767;
60
+ const z = vertexData[i + nCoords * 2] / 32767;
61
+ positions[3 * i + 0] = x * xScale + minX;
62
+ positions[3 * i + 1] = y * yScale + minY;
63
+ positions[3 * i + 2] = z * zScale + minHeight;
64
+ texCoords[2 * i + 0] = x;
65
+ texCoords[2 * i + 1] = y;
82
66
  }
83
- };
67
+ return {
68
+ POSITION: { value: positions, size: 3 },
69
+ TEXCOORD_0: { value: texCoords, size: 2 }
70
+ // TODO: Parse normals if they exist in the file
71
+ // NORMAL: {}, - optional, but creates the high poly look with lighting
72
+ };
84
73
  }
85
- //# sourceMappingURL=parse-quantized-mesh.js.map
@@ -1,151 +1,149 @@
1
+ // loaders.gl
2
+ // SPDX-License-Identifier: MIT
3
+ // Copyright (c) vis.gl contributors
1
4
  import { getMeshBoundingBox } from '@loaders.gl/schema';
2
5
  import Martini from '@mapbox/martini';
3
6
  import Delatin from "./delatin/index.js";
4
7
  import { addSkirt } from "./helpers/skirt.js";
8
+ /**
9
+ * Returns generated mesh object from image data
10
+ *
11
+ * @param terrainImage terrain image data
12
+ * @param terrainOptions terrain options
13
+ * @returns mesh object
14
+ */
5
15
  export function makeTerrainMeshFromImage(terrainImage, terrainOptions) {
6
- const {
7
- meshMaxError,
8
- bounds,
9
- elevationDecoder
10
- } = terrainOptions;
11
- const {
12
- data,
13
- width,
14
- height
15
- } = terrainImage;
16
- let terrain;
17
- let mesh;
18
- switch (terrainOptions.tesselator) {
19
- case 'martini':
20
- terrain = getTerrain(data, width, height, elevationDecoder, terrainOptions.tesselator);
21
- mesh = getMartiniTileMesh(meshMaxError, width, terrain);
22
- break;
23
- case 'delatin':
24
- terrain = getTerrain(data, width, height, elevationDecoder, terrainOptions.tesselator);
25
- mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
26
- break;
27
- default:
28
- if (width === height && !(height & width - 1)) {
29
- terrain = getTerrain(data, width, height, elevationDecoder, 'martini');
30
- mesh = getMartiniTileMesh(meshMaxError, width, terrain);
31
- } else {
32
- terrain = getTerrain(data, width, height, elevationDecoder, 'delatin');
33
- mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
34
- }
35
- break;
36
- }
37
- const {
38
- vertices
39
- } = mesh;
40
- let {
41
- triangles
42
- } = mesh;
43
- let attributes = getMeshAttributes(vertices, terrain, width, height, bounds);
44
- const boundingBox = getMeshBoundingBox(attributes);
45
- if (terrainOptions.skirtHeight) {
46
- const {
47
- attributes: newAttributes,
48
- triangles: newTriangles
49
- } = addSkirt(attributes, triangles, terrainOptions.skirtHeight);
50
- attributes = newAttributes;
51
- triangles = newTriangles;
52
- }
53
- return {
54
- loaderData: {
55
- header: {}
56
- },
57
- header: {
58
- vertexCount: triangles.length,
59
- boundingBox
60
- },
61
- mode: 4,
62
- indices: {
63
- value: Uint32Array.from(triangles),
64
- size: 1
65
- },
66
- attributes
67
- };
16
+ const { meshMaxError, bounds, elevationDecoder } = terrainOptions;
17
+ const { data, width, height } = terrainImage;
18
+ let terrain;
19
+ let mesh;
20
+ switch (terrainOptions.tesselator) {
21
+ case 'martini':
22
+ terrain = getTerrain(data, width, height, elevationDecoder, terrainOptions.tesselator);
23
+ mesh = getMartiniTileMesh(meshMaxError, width, terrain);
24
+ break;
25
+ case 'delatin':
26
+ terrain = getTerrain(data, width, height, elevationDecoder, terrainOptions.tesselator);
27
+ mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
28
+ break;
29
+ // auto
30
+ default:
31
+ if (width === height && !(height & (width - 1))) {
32
+ terrain = getTerrain(data, width, height, elevationDecoder, 'martini');
33
+ mesh = getMartiniTileMesh(meshMaxError, width, terrain);
34
+ }
35
+ else {
36
+ terrain = getTerrain(data, width, height, elevationDecoder, 'delatin');
37
+ mesh = getDelatinTileMesh(meshMaxError, width, height, terrain);
38
+ }
39
+ break;
40
+ }
41
+ const { vertices } = mesh;
42
+ let { triangles } = mesh;
43
+ let attributes = getMeshAttributes(vertices, terrain, width, height, bounds);
44
+ // Compute bounding box before adding skirt so that z values are not skewed
45
+ const boundingBox = getMeshBoundingBox(attributes);
46
+ if (terrainOptions.skirtHeight) {
47
+ const { attributes: newAttributes, triangles: newTriangles } = addSkirt(attributes, triangles, terrainOptions.skirtHeight);
48
+ attributes = newAttributes;
49
+ triangles = newTriangles;
50
+ }
51
+ return {
52
+ // Data return by this loader implementation
53
+ loaderData: {
54
+ header: {}
55
+ },
56
+ header: {
57
+ vertexCount: triangles.length,
58
+ boundingBox
59
+ },
60
+ mode: 4, // TRIANGLES
61
+ indices: { value: Uint32Array.from(triangles), size: 1 },
62
+ attributes
63
+ };
68
64
  }
65
+ /**
66
+ * Get Martini generated vertices and triangles
67
+ *
68
+ * @param {number} meshMaxError threshold for simplifying mesh
69
+ * @param {number} width width of the input data
70
+ * @param {number[] | Float32Array} terrain elevation data
71
+ * @returns {{vertices: Uint16Array, triangles: Uint32Array}} vertices and triangles data
72
+ */
69
73
  function getMartiniTileMesh(meshMaxError, width, terrain) {
70
- const gridSize = width + 1;
71
- const martini = new Martini(gridSize);
72
- const tile = martini.createTile(terrain);
73
- const {
74
- vertices,
75
- triangles
76
- } = tile.getMesh(meshMaxError);
77
- return {
78
- vertices,
79
- triangles
80
- };
74
+ const gridSize = width + 1;
75
+ const martini = new Martini(gridSize);
76
+ const tile = martini.createTile(terrain);
77
+ const { vertices, triangles } = tile.getMesh(meshMaxError);
78
+ return { vertices, triangles };
81
79
  }
80
+ /**
81
+ * Get Delatin generated vertices and triangles
82
+ *
83
+ * @param {number} meshMaxError threshold for simplifying mesh
84
+ * @param {number} width width of the input data array
85
+ * @param {number} height height of the input data array
86
+ * @param {number[] | Float32Array} terrain elevation data
87
+ * @returns {{vertices: number[], triangles: number[]}} vertices and triangles data
88
+ */
82
89
  function getDelatinTileMesh(meshMaxError, width, height, terrain) {
83
- const tin = new Delatin(terrain, width + 1, height + 1);
84
- tin.run(meshMaxError);
85
- const {
86
- coords,
87
- triangles
88
- } = tin;
89
- const vertices = coords;
90
- return {
91
- vertices,
92
- triangles
93
- };
90
+ const tin = new Delatin(terrain, width + 1, height + 1);
91
+ tin.run(meshMaxError);
92
+ // @ts-expect-error
93
+ const { coords, triangles } = tin;
94
+ const vertices = coords;
95
+ return { vertices, triangles };
94
96
  }
95
97
  function getTerrain(imageData, width, height, elevationDecoder, tesselator) {
96
- const {
97
- rScaler,
98
- bScaler,
99
- gScaler,
100
- offset
101
- } = elevationDecoder;
102
- const terrain = new Float32Array((width + 1) * (height + 1));
103
- for (let i = 0, y = 0; y < height; y++) {
104
- for (let x = 0; x < width; x++, i++) {
105
- const k = i * 4;
106
- const r = imageData[k + 0];
107
- const g = imageData[k + 1];
108
- const b = imageData[k + 2];
109
- terrain[i + y] = r * rScaler + g * gScaler + b * bScaler + offset;
110
- }
111
- }
112
- if (tesselator === 'martini') {
113
- for (let i = (width + 1) * width, x = 0; x < width; x++, i++) {
114
- terrain[i] = terrain[i - width - 1];
98
+ const { rScaler, bScaler, gScaler, offset } = elevationDecoder;
99
+ // From Martini demo
100
+ // https://observablehq.com/@mourner/martin-real-time-rtin-terrain-mesh
101
+ const terrain = new Float32Array((width + 1) * (height + 1));
102
+ // decode terrain values
103
+ for (let i = 0, y = 0; y < height; y++) {
104
+ for (let x = 0; x < width; x++, i++) {
105
+ const k = i * 4;
106
+ const r = imageData[k + 0];
107
+ const g = imageData[k + 1];
108
+ const b = imageData[k + 2];
109
+ terrain[i + y] = r * rScaler + g * gScaler + b * bScaler + offset;
110
+ }
115
111
  }
116
- for (let i = height, y = 0; y < height + 1; y++, i += height + 1) {
117
- terrain[i] = terrain[i - 1];
112
+ if (tesselator === 'martini') {
113
+ // backfill bottom border
114
+ for (let i = (width + 1) * width, x = 0; x < width; x++, i++) {
115
+ terrain[i] = terrain[i - width - 1];
116
+ }
117
+ // backfill right border
118
+ for (let i = height, y = 0; y < height + 1; y++, i += height + 1) {
119
+ terrain[i] = terrain[i - 1];
120
+ }
118
121
  }
119
- }
120
- return terrain;
122
+ return terrain;
121
123
  }
122
124
  function getMeshAttributes(vertices, terrain, width, height, bounds) {
123
- const gridSize = width + 1;
124
- const numOfVerticies = vertices.length / 2;
125
- const positions = new Float32Array(numOfVerticies * 3);
126
- const texCoords = new Float32Array(numOfVerticies * 2);
127
- const [minX, minY, maxX, maxY] = bounds || [0, 0, width, height];
128
- const xScale = (maxX - minX) / width;
129
- const yScale = (maxY - minY) / height;
130
- for (let i = 0; i < numOfVerticies; i++) {
131
- const x = vertices[i * 2];
132
- const y = vertices[i * 2 + 1];
133
- const pixelIdx = y * gridSize + x;
134
- positions[3 * i + 0] = x * xScale + minX;
135
- positions[3 * i + 1] = -y * yScale + maxY;
136
- positions[3 * i + 2] = terrain[pixelIdx];
137
- texCoords[2 * i + 0] = x / width;
138
- texCoords[2 * i + 1] = y / height;
139
- }
140
- return {
141
- POSITION: {
142
- value: positions,
143
- size: 3
144
- },
145
- TEXCOORD_0: {
146
- value: texCoords,
147
- size: 2
125
+ const gridSize = width + 1;
126
+ const numOfVerticies = vertices.length / 2;
127
+ // vec3. x, y in pixels, z in meters
128
+ const positions = new Float32Array(numOfVerticies * 3);
129
+ // vec2. 1 to 1 relationship with position. represents the uv on the texture image. 0,0 to 1,1.
130
+ const texCoords = new Float32Array(numOfVerticies * 2);
131
+ const [minX, minY, maxX, maxY] = bounds || [0, 0, width, height];
132
+ const xScale = (maxX - minX) / width;
133
+ const yScale = (maxY - minY) / height;
134
+ for (let i = 0; i < numOfVerticies; i++) {
135
+ const x = vertices[i * 2];
136
+ const y = vertices[i * 2 + 1];
137
+ const pixelIdx = y * gridSize + x;
138
+ positions[3 * i + 0] = x * xScale + minX;
139
+ positions[3 * i + 1] = -y * yScale + maxY;
140
+ positions[3 * i + 2] = terrain[pixelIdx];
141
+ texCoords[2 * i + 0] = x / width;
142
+ texCoords[2 * i + 1] = y / height;
148
143
  }
149
- };
144
+ return {
145
+ POSITION: { value: positions, size: 3 },
146
+ TEXCOORD_0: { value: texCoords, size: 2 }
147
+ // NORMAL: {}, - optional, but creates the high poly look with lighting
148
+ };
150
149
  }
151
- //# sourceMappingURL=parse-terrain.js.map