@loaders.gl/terrain
Version:
Framework-independent loader for terrain raster formats
107 lines (93 loc) • 3.07 kB
text/typescript
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright (c) vis.gl contributors
import {Mesh, getMeshBoundingBox} from '@loaders.gl/schema';
import decode, {DECODING_STEPS} from './decode-quantized-mesh';
import {addSkirt} from './helpers/skirt';
export type ParseQuantizedMeshOptions = {
bounds?: [number, number, number, number];
skirtHeight?: number | null;
};
export function parseQuantizedMesh(
arrayBuffer: ArrayBuffer,
options: ParseQuantizedMeshOptions = {}
): Mesh {
const {bounds} = options;
// Don't parse edge indices or format extensions
const {
header,
vertexData,
triangleIndices: originalTriangleIndices,
westIndices,
northIndices,
eastIndices,
southIndices
} = decode(arrayBuffer, DECODING_STEPS.triangleIndices);
let triangleIndices = originalTriangleIndices;
let attributes = getMeshAttributes(vertexData, header, bounds);
// Compute bounding box before adding skirt so that z values are not skewed
// TODO: Find bounding box from header, instead of doing extra pass over
// vertices.
const boundingBox = getMeshBoundingBox(attributes);
if (options?.skirtHeight) {
const {attributes: newAttributes, triangles: newTriangles} = addSkirt(
attributes,
triangleIndices,
options.skirtHeight,
{
westIndices,
northIndices,
eastIndices,
southIndices
}
);
attributes = newAttributes;
triangleIndices = newTriangles;
}
return {
// Data return by this loader implementation
loaderData: {
header: {}
},
header: {
// @ts-ignore
vertexCount: triangleIndices.length,
boundingBox
},
// TODO
schema: undefined!,
topology: 'triangle-list',
mode: 4, // TRIANGLES
indices: {value: triangleIndices, size: 1},
attributes
};
}
function getMeshAttributes(vertexData, header, bounds) {
const {minHeight, maxHeight} = header;
const [minX, minY, maxX, maxY] = bounds || [0, 0, 1, 1];
const xScale = maxX - minX;
const yScale = maxY - minY;
const zScale = maxHeight - minHeight;
const nCoords = vertexData.length / 3;
// vec3. x, y defined by bounds, z in meters
const positions = new Float32Array(nCoords * 3);
// vec2. 1 to 1 relationship with position. represents the uv on the texture image. 0,0 to 1,1.
const texCoords = new Float32Array(nCoords * 2);
// Data is not interleaved; all u, then all v, then all heights
for (let i = 0; i < nCoords; i++) {
const x = vertexData[i] / 32767;
const y = vertexData[i + nCoords] / 32767;
const z = vertexData[i + nCoords * 2] / 32767;
positions[3 * i + 0] = x * xScale + minX;
positions[3 * i + 1] = y * yScale + minY;
positions[3 * i + 2] = z * zScale + minHeight;
texCoords[2 * i + 0] = x;
texCoords[2 * i + 1] = y;
}
return {
POSITION: {value: positions, size: 3},
TEXCOORD_0: {value: texCoords, size: 2}
// TODO: Parse normals if they exist in the file
// NORMAL: {}, - optional, but creates the high poly look with lighting
};
}