You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1066 lines
32 KiB
1066 lines
32 KiB
9 months ago
|
/**
|
||
|
* @license
|
||
|
* Cesium - https://github.com/CesiumGS/cesium
|
||
|
* Version 1.99
|
||
|
*
|
||
|
* Copyright 2011-2022 Cesium Contributors
|
||
|
*
|
||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||
|
* you may not use this file except in compliance with the License.
|
||
|
* You may obtain a copy of the License at
|
||
|
*
|
||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||
|
*
|
||
|
* Unless required by applicable law or agreed to in writing, software
|
||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||
|
* See the License for the specific language governing permissions and
|
||
|
* limitations under the License.
|
||
|
*
|
||
|
* Columbus View (Pat. Pend.)
|
||
|
*
|
||
|
* Portions licensed separately.
|
||
|
* See https://github.com/CesiumGS/cesium/blob/main/LICENSE.md for full licensing details.
|
||
|
*/
|
||
|
|
||
|
define(['./createTaskProcessorWorker', './defaultValue-135942ca', './WebMercatorProjection-7dd32693', './Matrix3-ea964448', './Math-efde0c7b', './Check-40d84a28'], (function (createTaskProcessorWorker, defaultValue, WebMercatorProjection, Matrix3, Math$1, Check) { 'use strict';
|
||
|
|
||
|
/* global require */
|
||
|
|
||
|
let draco;
|
||
|
|
||
|
function bilinearInterpolate(tx, ty, h00, h10, h01, h11) {
|
||
|
const a = h00 * (1 - tx) + h10 * tx;
|
||
|
const b = h01 * (1 - tx) + h11 * tx;
|
||
|
return a * (1 - ty) + b * ty;
|
||
|
}
|
||
|
|
||
|
function sampleMap(u, v, width, data) {
|
||
|
const address = u + v * width;
|
||
|
return data[address];
|
||
|
}
|
||
|
|
||
|
function sampleGeoid(sampleX, sampleY, geoidData) {
|
||
|
const extent = geoidData.nativeExtent;
|
||
|
let x =
|
||
|
((sampleX - extent.west) / (extent.east - extent.west)) *
|
||
|
(geoidData.width - 1);
|
||
|
let y =
|
||
|
((sampleY - extent.south) / (extent.north - extent.south)) *
|
||
|
(geoidData.height - 1);
|
||
|
const xi = Math.floor(x);
|
||
|
let yi = Math.floor(y);
|
||
|
|
||
|
x -= xi;
|
||
|
y -= yi;
|
||
|
|
||
|
const xNext = xi < geoidData.width ? xi + 1 : xi;
|
||
|
let yNext = yi < geoidData.height ? yi + 1 : yi;
|
||
|
|
||
|
yi = geoidData.height - 1 - yi;
|
||
|
yNext = geoidData.height - 1 - yNext;
|
||
|
|
||
|
const h00 = sampleMap(xi, yi, geoidData.width, geoidData.buffer);
|
||
|
const h10 = sampleMap(xNext, yi, geoidData.width, geoidData.buffer);
|
||
|
const h01 = sampleMap(xi, yNext, geoidData.width, geoidData.buffer);
|
||
|
const h11 = sampleMap(xNext, yNext, geoidData.width, geoidData.buffer);
|
||
|
|
||
|
let finalHeight = bilinearInterpolate(x, y, h00, h10, h01, h11);
|
||
|
finalHeight = finalHeight * geoidData.scale + geoidData.offset;
|
||
|
return finalHeight;
|
||
|
}
|
||
|
|
||
|
function sampleGeoidFromList(lon, lat, geoidDataList) {
|
||
|
for (let i = 0; i < geoidDataList.length; i++) {
|
||
|
const localExtent = geoidDataList[i].nativeExtent;
|
||
|
|
||
|
let localPt = new Matrix3.Cartesian3();
|
||
|
if (geoidDataList[i].projectionType === "WebMercator") {
|
||
|
const radii = geoidDataList[i].projection._ellipsoid._radii;
|
||
|
const webMercatorProj = new WebMercatorProjection.WebMercatorProjection(
|
||
|
new Matrix3.Ellipsoid(radii.x, radii.y, radii.z)
|
||
|
);
|
||
|
localPt = webMercatorProj.project(new Matrix3.Cartographic(lon, lat, 0));
|
||
|
} else {
|
||
|
localPt.x = lon;
|
||
|
localPt.y = lat;
|
||
|
}
|
||
|
|
||
|
if (
|
||
|
localPt.x > localExtent.west &&
|
||
|
localPt.x < localExtent.east &&
|
||
|
localPt.y > localExtent.south &&
|
||
|
localPt.y < localExtent.north
|
||
|
) {
|
||
|
return sampleGeoid(localPt.x, localPt.y, geoidDataList[i]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
function orthometricToEllipsoidal(
|
||
|
vertexCount,
|
||
|
position,
|
||
|
scale_x,
|
||
|
scale_y,
|
||
|
center,
|
||
|
geoidDataList,
|
||
|
fast
|
||
|
) {
|
||
|
if (fast) {
|
||
|
// Geometry is already relative to the tile origin which has already been shifted to account for geoid height
|
||
|
// Nothing to do here
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
// For more precision, sample the geoid height at each vertex and shift by the difference between that value and the height at the center of the tile
|
||
|
const centerHeight = sampleGeoidFromList(
|
||
|
center.longitude,
|
||
|
center.latitude,
|
||
|
geoidDataList
|
||
|
);
|
||
|
|
||
|
for (let i = 0; i < vertexCount; ++i) {
|
||
|
const height = sampleGeoidFromList(
|
||
|
center.longitude + Math$1.CesiumMath.toRadians(scale_x * position[i * 3]),
|
||
|
center.latitude + Math$1.CesiumMath.toRadians(scale_y * position[i * 3 + 1]),
|
||
|
geoidDataList
|
||
|
);
|
||
|
position[i * 3 + 2] += height - centerHeight;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function transformToLocal(
|
||
|
vertexCount,
|
||
|
positions,
|
||
|
normals,
|
||
|
cartographicCenter,
|
||
|
cartesianCenter,
|
||
|
parentRotation,
|
||
|
ellipsoidRadiiSquare,
|
||
|
scale_x,
|
||
|
scale_y
|
||
|
) {
|
||
|
if (vertexCount === 0 || !defaultValue.defined(positions) || positions.length === 0) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const ellipsoid = new Matrix3.Ellipsoid(
|
||
|
Math.sqrt(ellipsoidRadiiSquare.x),
|
||
|
Math.sqrt(ellipsoidRadiiSquare.y),
|
||
|
Math.sqrt(ellipsoidRadiiSquare.z)
|
||
|
);
|
||
|
for (let i = 0; i < vertexCount; ++i) {
|
||
|
const indexOffset = i * 3;
|
||
|
const indexOffset1 = indexOffset + 1;
|
||
|
const indexOffset2 = indexOffset + 2;
|
||
|
|
||
|
const cartographic = new Matrix3.Cartographic();
|
||
|
cartographic.longitude =
|
||
|
cartographicCenter.longitude +
|
||
|
Math$1.CesiumMath.toRadians(scale_x * positions[indexOffset]);
|
||
|
|
||
|
cartographic.latitude =
|
||
|
cartographicCenter.latitude +
|
||
|
Math$1.CesiumMath.toRadians(scale_y * positions[indexOffset1]);
|
||
|
cartographic.height = cartographicCenter.height + positions[indexOffset2];
|
||
|
|
||
|
const position = {};
|
||
|
ellipsoid.cartographicToCartesian(cartographic, position);
|
||
|
|
||
|
position.x -= cartesianCenter.x;
|
||
|
position.y -= cartesianCenter.y;
|
||
|
position.z -= cartesianCenter.z;
|
||
|
|
||
|
const rotatedPosition = {};
|
||
|
Matrix3.Matrix3.multiplyByVector(parentRotation, position, rotatedPosition);
|
||
|
|
||
|
positions[indexOffset] = rotatedPosition.x;
|
||
|
positions[indexOffset1] = rotatedPosition.y;
|
||
|
positions[indexOffset2] = rotatedPosition.z;
|
||
|
|
||
|
if (defaultValue.defined(normals)) {
|
||
|
const normal = new Matrix3.Cartesian3(
|
||
|
normals[indexOffset],
|
||
|
normals[indexOffset1],
|
||
|
normals[indexOffset2]
|
||
|
);
|
||
|
|
||
|
const rotatedNormal = {};
|
||
|
Matrix3.Matrix3.multiplyByVector(parentRotation, normal, rotatedNormal);
|
||
|
|
||
|
// TODO: check if normals are Z-UP or Y-UP and flip y and z
|
||
|
normals[indexOffset] = rotatedNormal.x;
|
||
|
normals[indexOffset1] = rotatedNormal.y;
|
||
|
normals[indexOffset2] = rotatedNormal.z;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function cropUVs(vertexCount, uv0s, uvRegions) {
|
||
|
for (let vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) {
|
||
|
const minU = uvRegions[vertexIndex * 4] / 65535.0;
|
||
|
const minV = uvRegions[vertexIndex * 4 + 1] / 65535.0;
|
||
|
const scaleU =
|
||
|
(uvRegions[vertexIndex * 4 + 2] - uvRegions[vertexIndex * 4]) / 65535.0;
|
||
|
const scaleV =
|
||
|
(uvRegions[vertexIndex * 4 + 3] - uvRegions[vertexIndex * 4 + 1]) /
|
||
|
65535.0;
|
||
|
|
||
|
uv0s[vertexIndex * 2] *= scaleU;
|
||
|
uv0s[vertexIndex * 2] += minU;
|
||
|
|
||
|
uv0s[vertexIndex * 2 + 1] *= scaleV;
|
||
|
uv0s[vertexIndex * 2 + 1] += minV;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function generateGltfBuffer(
|
||
|
vertexCount,
|
||
|
indices,
|
||
|
positions,
|
||
|
normals,
|
||
|
uv0s,
|
||
|
colors
|
||
|
) {
|
||
|
if (vertexCount === 0 || !defaultValue.defined(positions) || positions.length === 0) {
|
||
|
return {
|
||
|
buffers: [],
|
||
|
bufferViews: [],
|
||
|
accessors: [],
|
||
|
meshes: [],
|
||
|
nodes: [],
|
||
|
nodesInScene: [],
|
||
|
};
|
||
|
}
|
||
|
|
||
|
const buffers = [];
|
||
|
const bufferViews = [];
|
||
|
const accessors = [];
|
||
|
const meshes = [];
|
||
|
const nodes = [];
|
||
|
const nodesInScene = [];
|
||
|
|
||
|
// If we provide indices, then the vertex count is the length
|
||
|
// of that array, otherwise we assume non-indexed triangle
|
||
|
if (defaultValue.defined(indices)) {
|
||
|
vertexCount = indices.length;
|
||
|
}
|
||
|
|
||
|
// Allocate array
|
||
|
const indexArray = new Uint32Array(vertexCount);
|
||
|
|
||
|
if (defaultValue.defined(indices)) {
|
||
|
// Set the indices
|
||
|
for (let vertexIndex = 0; vertexIndex < vertexCount; ++vertexIndex) {
|
||
|
indexArray[vertexIndex] = indices[vertexIndex];
|
||
|
}
|
||
|
} else {
|
||
|
// Generate indices
|
||
|
for (
|
||
|
let newVertexIndex = 0;
|
||
|
newVertexIndex < vertexCount;
|
||
|
++newVertexIndex
|
||
|
) {
|
||
|
indexArray[newVertexIndex] = newVertexIndex;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Push to the buffers, bufferViews and accessors
|
||
|
const indicesBlob = new Blob([indexArray], { type: "application/binary" });
|
||
|
const indicesURL = URL.createObjectURL(indicesBlob);
|
||
|
|
||
|
const endIndex = vertexCount;
|
||
|
|
||
|
// POSITIONS
|
||
|
const meshPositions = positions.subarray(0, endIndex * 3);
|
||
|
const positionsBlob = new Blob([meshPositions], {
|
||
|
type: "application/binary",
|
||
|
});
|
||
|
const positionsURL = URL.createObjectURL(positionsBlob);
|
||
|
|
||
|
let minX = Number.POSITIVE_INFINITY;
|
||
|
let maxX = Number.NEGATIVE_INFINITY;
|
||
|
let minY = Number.POSITIVE_INFINITY;
|
||
|
let maxY = Number.NEGATIVE_INFINITY;
|
||
|
let minZ = Number.POSITIVE_INFINITY;
|
||
|
let maxZ = Number.NEGATIVE_INFINITY;
|
||
|
|
||
|
for (let i = 0; i < meshPositions.length / 3; i++) {
|
||
|
minX = Math.min(minX, meshPositions[i * 3 + 0]);
|
||
|
maxX = Math.max(maxX, meshPositions[i * 3 + 0]);
|
||
|
minY = Math.min(minY, meshPositions[i * 3 + 1]);
|
||
|
maxY = Math.max(maxY, meshPositions[i * 3 + 1]);
|
||
|
minZ = Math.min(minZ, meshPositions[i * 3 + 2]);
|
||
|
maxZ = Math.max(maxZ, meshPositions[i * 3 + 2]);
|
||
|
}
|
||
|
|
||
|
// NORMALS
|
||
|
const meshNormals = normals ? normals.subarray(0, endIndex * 3) : undefined;
|
||
|
let normalsURL;
|
||
|
if (defaultValue.defined(meshNormals)) {
|
||
|
const normalsBlob = new Blob([meshNormals], {
|
||
|
type: "application/binary",
|
||
|
});
|
||
|
normalsURL = URL.createObjectURL(normalsBlob);
|
||
|
}
|
||
|
|
||
|
// UV0s
|
||
|
const meshUv0s = uv0s ? uv0s.subarray(0, endIndex * 2) : undefined;
|
||
|
let uv0URL;
|
||
|
if (defaultValue.defined(meshUv0s)) {
|
||
|
const uv0Blob = new Blob([meshUv0s], { type: "application/binary" });
|
||
|
uv0URL = URL.createObjectURL(uv0Blob);
|
||
|
}
|
||
|
|
||
|
// COLORS
|
||
|
const meshColorsInBytes = defaultValue.defined(colors)
|
||
|
? colors.subarray(0, endIndex * 4)
|
||
|
: undefined;
|
||
|
let colorsURL;
|
||
|
if (defaultValue.defined(meshColorsInBytes)) {
|
||
|
const colorsBlob = new Blob([meshColorsInBytes], {
|
||
|
type: "application/binary",
|
||
|
});
|
||
|
colorsURL = URL.createObjectURL(colorsBlob);
|
||
|
}
|
||
|
|
||
|
const posIndex = 0;
|
||
|
let normalIndex = 0;
|
||
|
let uv0Index = 0;
|
||
|
let colorIndex = 0;
|
||
|
let indicesIndex = 0;
|
||
|
|
||
|
let currentIndex = posIndex;
|
||
|
|
||
|
const attributes = {};
|
||
|
|
||
|
// POSITIONS
|
||
|
attributes.POSITION = posIndex;
|
||
|
buffers.push({
|
||
|
uri: positionsURL,
|
||
|
byteLength: meshPositions.byteLength,
|
||
|
});
|
||
|
bufferViews.push({
|
||
|
buffer: posIndex,
|
||
|
byteOffset: 0,
|
||
|
byteLength: meshPositions.byteLength,
|
||
|
target: 34962,
|
||
|
});
|
||
|
accessors.push({
|
||
|
bufferView: posIndex,
|
||
|
byteOffset: 0,
|
||
|
componentType: 5126,
|
||
|
count: vertexCount,
|
||
|
type: "VEC3",
|
||
|
max: [minX, minY, minZ],
|
||
|
min: [maxX, maxY, maxZ],
|
||
|
});
|
||
|
|
||
|
// NORMALS
|
||
|
if (defaultValue.defined(normalsURL)) {
|
||
|
++currentIndex;
|
||
|
normalIndex = currentIndex;
|
||
|
attributes.NORMAL = normalIndex;
|
||
|
buffers.push({
|
||
|
uri: normalsURL,
|
||
|
byteLength: meshNormals.byteLength,
|
||
|
});
|
||
|
bufferViews.push({
|
||
|
buffer: normalIndex,
|
||
|
byteOffset: 0,
|
||
|
byteLength: meshNormals.byteLength,
|
||
|
target: 34962,
|
||
|
});
|
||
|
accessors.push({
|
||
|
bufferView: normalIndex,
|
||
|
byteOffset: 0,
|
||
|
componentType: 5126,
|
||
|
count: vertexCount,
|
||
|
type: "VEC3",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// UV0
|
||
|
if (defaultValue.defined(uv0URL)) {
|
||
|
++currentIndex;
|
||
|
uv0Index = currentIndex;
|
||
|
attributes.TEXCOORD_0 = uv0Index;
|
||
|
buffers.push({
|
||
|
uri: uv0URL,
|
||
|
byteLength: meshUv0s.byteLength,
|
||
|
});
|
||
|
bufferViews.push({
|
||
|
buffer: uv0Index,
|
||
|
byteOffset: 0,
|
||
|
byteLength: meshUv0s.byteLength,
|
||
|
target: 34962,
|
||
|
});
|
||
|
accessors.push({
|
||
|
bufferView: uv0Index,
|
||
|
byteOffset: 0,
|
||
|
componentType: 5126,
|
||
|
count: vertexCount,
|
||
|
type: "VEC2",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// COLORS
|
||
|
if (defaultValue.defined(colorsURL)) {
|
||
|
++currentIndex;
|
||
|
colorIndex = currentIndex;
|
||
|
attributes.COLOR_0 = colorIndex;
|
||
|
buffers.push({
|
||
|
uri: colorsURL,
|
||
|
byteLength: meshColorsInBytes.byteLength,
|
||
|
});
|
||
|
bufferViews.push({
|
||
|
buffer: colorIndex,
|
||
|
byteOffset: 0,
|
||
|
byteLength: meshColorsInBytes.byteLength,
|
||
|
target: 34962,
|
||
|
});
|
||
|
accessors.push({
|
||
|
bufferView: colorIndex,
|
||
|
byteOffset: 0,
|
||
|
componentType: 5121,
|
||
|
normalized: true,
|
||
|
count: vertexCount,
|
||
|
type: "VEC4",
|
||
|
});
|
||
|
}
|
||
|
|
||
|
// INDICES
|
||
|
++currentIndex;
|
||
|
indicesIndex = currentIndex;
|
||
|
buffers.push({
|
||
|
uri: indicesURL,
|
||
|
byteLength: indexArray.byteLength,
|
||
|
});
|
||
|
bufferViews.push({
|
||
|
buffer: indicesIndex,
|
||
|
byteOffset: 0,
|
||
|
byteLength: indexArray.byteLength,
|
||
|
target: 34963,
|
||
|
});
|
||
|
accessors.push({
|
||
|
bufferView: indicesIndex,
|
||
|
byteOffset: 0,
|
||
|
componentType: 5125,
|
||
|
count: vertexCount,
|
||
|
type: "SCALAR",
|
||
|
});
|
||
|
|
||
|
// Create a new mesh for this page
|
||
|
meshes.push({
|
||
|
primitives: [
|
||
|
{
|
||
|
attributes: attributes,
|
||
|
indices: indicesIndex,
|
||
|
material: 0,
|
||
|
},
|
||
|
],
|
||
|
});
|
||
|
nodesInScene.push(0);
|
||
|
nodes.push({ mesh: 0 });
|
||
|
|
||
|
return {
|
||
|
buffers: buffers,
|
||
|
bufferViews: bufferViews,
|
||
|
accessors: accessors,
|
||
|
meshes: meshes,
|
||
|
nodes: nodes,
|
||
|
nodesInScene: nodesInScene,
|
||
|
};
|
||
|
}
|
||
|
|
||
|
function decode(data, schema, bufferInfo, featureData) {
|
||
|
const magicNumber = new Uint8Array(data, 0, 5);
|
||
|
if (
|
||
|
magicNumber[0] === "D".charCodeAt() &&
|
||
|
magicNumber[1] === "R".charCodeAt() &&
|
||
|
magicNumber[2] === "A".charCodeAt() &&
|
||
|
magicNumber[3] === "C".charCodeAt() &&
|
||
|
magicNumber[4] === "O".charCodeAt()
|
||
|
) {
|
||
|
return decodeDracoEncodedGeometry(data);
|
||
|
}
|
||
|
return decodeBinaryGeometry(data, schema, bufferInfo, featureData);
|
||
|
}
|
||
|
|
||
|
function decodeDracoEncodedGeometry(data) {
|
||
|
// Create the Draco decoder.
|
||
|
const dracoDecoderModule = draco;
|
||
|
const buffer = new dracoDecoderModule.DecoderBuffer();
|
||
|
|
||
|
const byteArray = new Uint8Array(data);
|
||
|
buffer.Init(byteArray, byteArray.length);
|
||
|
|
||
|
// Create a buffer to hold the encoded data.
|
||
|
const dracoDecoder = new dracoDecoderModule.Decoder();
|
||
|
const geometryType = dracoDecoder.GetEncodedGeometryType(buffer);
|
||
|
const metadataQuerier = new dracoDecoderModule.MetadataQuerier();
|
||
|
|
||
|
// Decode the encoded geometry.
|
||
|
// See: https://github.com/google/draco/blob/master/src/draco/javascript/emscripten/draco_web_decoder.idl
|
||
|
let dracoGeometry;
|
||
|
let status;
|
||
|
if (geometryType === dracoDecoderModule.TRIANGULAR_MESH) {
|
||
|
dracoGeometry = new dracoDecoderModule.Mesh();
|
||
|
status = dracoDecoder.DecodeBufferToMesh(buffer, dracoGeometry);
|
||
|
}
|
||
|
|
||
|
const decodedGeometry = {
|
||
|
vertexCount: [0],
|
||
|
featureCount: 0,
|
||
|
};
|
||
|
|
||
|
// if all is OK
|
||
|
if (defaultValue.defined(status) && status.ok() && dracoGeometry.ptr !== 0) {
|
||
|
const faceCount = dracoGeometry.num_faces();
|
||
|
const attributesCount = dracoGeometry.num_attributes();
|
||
|
const vertexCount = dracoGeometry.num_points();
|
||
|
decodedGeometry.indices = new Uint32Array(faceCount * 3);
|
||
|
const faces = decodedGeometry.indices;
|
||
|
|
||
|
decodedGeometry.vertexCount[0] = vertexCount;
|
||
|
decodedGeometry.scale_x = 1;
|
||
|
decodedGeometry.scale_y = 1;
|
||
|
|
||
|
// Decode faces
|
||
|
// @TODO: Replace that code with GetTrianglesUInt32Array for better efficiency
|
||
|
const face = new dracoDecoderModule.DracoInt32Array(3);
|
||
|
for (let faceIndex = 0; faceIndex < faceCount; ++faceIndex) {
|
||
|
dracoDecoder.GetFaceFromMesh(dracoGeometry, faceIndex, face);
|
||
|
faces[faceIndex * 3] = face.GetValue(0);
|
||
|
faces[faceIndex * 3 + 1] = face.GetValue(1);
|
||
|
faces[faceIndex * 3 + 2] = face.GetValue(2);
|
||
|
}
|
||
|
|
||
|
dracoDecoderModule.destroy(face);
|
||
|
|
||
|
for (let attrIndex = 0; attrIndex < attributesCount; ++attrIndex) {
|
||
|
const dracoAttribute = dracoDecoder.GetAttribute(
|
||
|
dracoGeometry,
|
||
|
attrIndex
|
||
|
);
|
||
|
|
||
|
const attributeData = decodeDracoAttribute(
|
||
|
dracoDecoderModule,
|
||
|
dracoDecoder,
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
vertexCount
|
||
|
);
|
||
|
|
||
|
// initial mapping
|
||
|
const dracoAttributeType = dracoAttribute.attribute_type();
|
||
|
let attributei3sName = "unknown";
|
||
|
|
||
|
if (dracoAttributeType === dracoDecoderModule.POSITION) {
|
||
|
attributei3sName = "positions";
|
||
|
} else if (dracoAttributeType === dracoDecoderModule.NORMAL) {
|
||
|
attributei3sName = "normals";
|
||
|
} else if (dracoAttributeType === dracoDecoderModule.COLOR) {
|
||
|
attributei3sName = "colors";
|
||
|
} else if (dracoAttributeType === dracoDecoderModule.TEX_COORD) {
|
||
|
attributei3sName = "uv0s";
|
||
|
}
|
||
|
|
||
|
// get the metadata
|
||
|
const metadata = dracoDecoder.GetAttributeMetadata(
|
||
|
dracoGeometry,
|
||
|
attrIndex
|
||
|
);
|
||
|
|
||
|
if (metadata.ptr !== 0) {
|
||
|
const numEntries = metadataQuerier.NumEntries(metadata);
|
||
|
for (let entry = 0; entry < numEntries; ++entry) {
|
||
|
const entryName = metadataQuerier.GetEntryName(metadata, entry);
|
||
|
if (entryName === "i3s-scale_x") {
|
||
|
decodedGeometry.scale_x = metadataQuerier.GetDoubleEntry(
|
||
|
metadata,
|
||
|
"i3s-scale_x"
|
||
|
);
|
||
|
} else if (entryName === "i3s-scale_y") {
|
||
|
decodedGeometry.scale_y = metadataQuerier.GetDoubleEntry(
|
||
|
metadata,
|
||
|
"i3s-scale_y"
|
||
|
);
|
||
|
} else if (entryName === "i3s-attribute-type") {
|
||
|
attributei3sName = metadataQuerier.GetStringEntry(
|
||
|
metadata,
|
||
|
"i3s-attribute-type"
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (defaultValue.defined(decodedGeometry[attributei3sName])) {
|
||
|
console.log("Attribute already exists", attributei3sName);
|
||
|
}
|
||
|
|
||
|
decodedGeometry[attributei3sName] = attributeData;
|
||
|
|
||
|
if (attributei3sName === "feature-index") {
|
||
|
decodedGeometry.featureCount++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
dracoDecoderModule.destroy(dracoGeometry);
|
||
|
}
|
||
|
|
||
|
dracoDecoderModule.destroy(metadataQuerier);
|
||
|
dracoDecoderModule.destroy(dracoDecoder);
|
||
|
|
||
|
return decodedGeometry;
|
||
|
}
|
||
|
|
||
|
function decodeDracoAttribute(
|
||
|
dracoDecoderModule,
|
||
|
dracoDecoder,
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
vertexCount
|
||
|
) {
|
||
|
const bufferSize = dracoAttribute.num_components() * vertexCount;
|
||
|
let dracoAttributeData;
|
||
|
|
||
|
const handlers = [
|
||
|
function () {}, // DT_INVALID - 0
|
||
|
function () {
|
||
|
// DT_INT8 - 1
|
||
|
dracoAttributeData = new dracoDecoderModule.DracoInt8Array(bufferSize);
|
||
|
const success = dracoDecoder.GetAttributeInt8ForAllPoints(
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
dracoAttributeData
|
||
|
);
|
||
|
|
||
|
if (!success) {
|
||
|
console.error("Bad stream");
|
||
|
}
|
||
|
const attributeData = new Int8Array(bufferSize);
|
||
|
for (let i = 0; i < bufferSize; ++i) {
|
||
|
attributeData[i] = dracoAttributeData.GetValue(i);
|
||
|
}
|
||
|
return attributeData;
|
||
|
},
|
||
|
function () {
|
||
|
// DT_UINT8 - 2
|
||
|
dracoAttributeData = new dracoDecoderModule.DracoInt8Array(bufferSize);
|
||
|
const success = dracoDecoder.GetAttributeUInt8ForAllPoints(
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
dracoAttributeData
|
||
|
);
|
||
|
|
||
|
if (!success) {
|
||
|
console.error("Bad stream");
|
||
|
}
|
||
|
const attributeData = new Uint8Array(bufferSize);
|
||
|
for (let i = 0; i < bufferSize; ++i) {
|
||
|
attributeData[i] = dracoAttributeData.GetValue(i);
|
||
|
}
|
||
|
return attributeData;
|
||
|
},
|
||
|
function () {
|
||
|
// DT_INT16 - 3
|
||
|
dracoAttributeData = new dracoDecoderModule.DracoInt16Array(bufferSize);
|
||
|
const success = dracoDecoder.GetAttributeInt16ForAllPoints(
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
dracoAttributeData
|
||
|
);
|
||
|
|
||
|
if (!success) {
|
||
|
console.error("Bad stream");
|
||
|
}
|
||
|
const attributeData = new Int16Array(bufferSize);
|
||
|
for (let i = 0; i < bufferSize; ++i) {
|
||
|
attributeData[i] = dracoAttributeData.GetValue(i);
|
||
|
}
|
||
|
return attributeData;
|
||
|
},
|
||
|
function () {
|
||
|
// DT_UINT16 - 4
|
||
|
dracoAttributeData = new dracoDecoderModule.DracoInt16Array(bufferSize);
|
||
|
const success = dracoDecoder.GetAttributeUInt16ForAllPoints(
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
dracoAttributeData
|
||
|
);
|
||
|
|
||
|
if (!success) {
|
||
|
console.error("Bad stream");
|
||
|
}
|
||
|
const attributeData = new Uint16Array(bufferSize);
|
||
|
for (let i = 0; i < bufferSize; ++i) {
|
||
|
attributeData[i] = dracoAttributeData.GetValue(i);
|
||
|
}
|
||
|
return attributeData;
|
||
|
},
|
||
|
function () {
|
||
|
// DT_INT32 - 5
|
||
|
dracoAttributeData = new dracoDecoderModule.DracoInt32Array(bufferSize);
|
||
|
const success = dracoDecoder.GetAttributeInt32ForAllPoints(
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
dracoAttributeData
|
||
|
);
|
||
|
|
||
|
if (!success) {
|
||
|
console.error("Bad stream");
|
||
|
}
|
||
|
const attributeData = new Int32Array(bufferSize);
|
||
|
for (let i = 0; i < bufferSize; ++i) {
|
||
|
attributeData[i] = dracoAttributeData.GetValue(i);
|
||
|
}
|
||
|
return attributeData;
|
||
|
},
|
||
|
function () {
|
||
|
// DT_UINT32 - 6
|
||
|
dracoAttributeData = new dracoDecoderModule.DracoInt32Array(bufferSize);
|
||
|
const success = dracoDecoder.GetAttributeUInt32ForAllPoints(
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
dracoAttributeData
|
||
|
);
|
||
|
|
||
|
if (!success) {
|
||
|
console.error("Bad stream");
|
||
|
}
|
||
|
const attributeData = new Uint32Array(bufferSize);
|
||
|
for (let i = 0; i < bufferSize; ++i) {
|
||
|
attributeData[i] = dracoAttributeData.GetValue(i);
|
||
|
}
|
||
|
return attributeData;
|
||
|
},
|
||
|
function () {
|
||
|
// DT_INT64 - 7
|
||
|
},
|
||
|
function () {
|
||
|
// DT_UINT64 - 8
|
||
|
},
|
||
|
function () {
|
||
|
// DT_FLOAT32 - 9
|
||
|
dracoAttributeData = new dracoDecoderModule.DracoFloat32Array(bufferSize);
|
||
|
const success = dracoDecoder.GetAttributeFloatForAllPoints(
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
dracoAttributeData
|
||
|
);
|
||
|
|
||
|
if (!success) {
|
||
|
console.error("Bad stream");
|
||
|
}
|
||
|
const attributeData = new Float32Array(bufferSize);
|
||
|
for (let i = 0; i < bufferSize; ++i) {
|
||
|
attributeData[i] = dracoAttributeData.GetValue(i);
|
||
|
}
|
||
|
return attributeData;
|
||
|
},
|
||
|
function () {
|
||
|
// DT_FLOAT64 - 10
|
||
|
},
|
||
|
function () {
|
||
|
// DT_FLOAT32 - 11
|
||
|
dracoAttributeData = new dracoDecoderModule.DracoUInt8Array(bufferSize);
|
||
|
const success = dracoDecoder.GetAttributeUInt8ForAllPoints(
|
||
|
dracoGeometry,
|
||
|
dracoAttribute,
|
||
|
dracoAttributeData
|
||
|
);
|
||
|
|
||
|
if (!success) {
|
||
|
console.error("Bad stream");
|
||
|
}
|
||
|
const attributeData = new Uint8Array(bufferSize);
|
||
|
for (let i = 0; i < bufferSize; ++i) {
|
||
|
attributeData[i] = dracoAttributeData.GetValue(i);
|
||
|
}
|
||
|
return attributeData;
|
||
|
},
|
||
|
];
|
||
|
|
||
|
const attributeData = handlers[dracoAttribute.data_type()]();
|
||
|
|
||
|
if (defaultValue.defined(dracoAttributeData)) {
|
||
|
dracoDecoderModule.destroy(dracoAttributeData);
|
||
|
}
|
||
|
|
||
|
return attributeData;
|
||
|
}
|
||
|
|
||
|
const binaryAttributeDecoders = {
|
||
|
position: function (decodedGeometry, data, offset) {
|
||
|
const count = decodedGeometry.vertexCount * 3;
|
||
|
decodedGeometry.positions = new Float32Array(data, offset, count);
|
||
|
offset += count * 4;
|
||
|
return offset;
|
||
|
},
|
||
|
normal: function (decodedGeometry, data, offset) {
|
||
|
const count = decodedGeometry.vertexCount * 3;
|
||
|
decodedGeometry.normals = new Float32Array(data, offset, count);
|
||
|
offset += count * 4;
|
||
|
return offset;
|
||
|
},
|
||
|
uv0: function (decodedGeometry, data, offset) {
|
||
|
const count = decodedGeometry.vertexCount * 2;
|
||
|
decodedGeometry.uv0s = new Float32Array(data, offset, count);
|
||
|
offset += count * 4;
|
||
|
return offset;
|
||
|
},
|
||
|
color: function (decodedGeometry, data, offset) {
|
||
|
const count = decodedGeometry.vertexCount * 4;
|
||
|
decodedGeometry.colors = new Uint8Array(data, offset, count);
|
||
|
offset += count;
|
||
|
return offset;
|
||
|
},
|
||
|
featureId: function (decodedGeometry, data, offset) {
|
||
|
// We don't need to use this for anything so just increment the offset
|
||
|
const count = decodedGeometry.featureCount;
|
||
|
offset += count * 8;
|
||
|
return offset;
|
||
|
},
|
||
|
id: function (decodedGeometry, data, offset) {
|
||
|
// We don't need to use this for anything so just increment the offset
|
||
|
const count = decodedGeometry.featureCount;
|
||
|
offset += count * 8;
|
||
|
return offset;
|
||
|
},
|
||
|
faceRange: function (decodedGeometry, data, offset) {
|
||
|
const count = decodedGeometry.featureCount * 2;
|
||
|
decodedGeometry.faceRange = new Uint32Array(data, offset, count);
|
||
|
offset += count * 4;
|
||
|
return offset;
|
||
|
},
|
||
|
uvRegion: function (decodedGeometry, data, offset) {
|
||
|
const count = decodedGeometry.vertexCount * 4;
|
||
|
decodedGeometry["uv-region"] = new Uint16Array(data, offset, count);
|
||
|
offset += count * 2;
|
||
|
return offset;
|
||
|
},
|
||
|
region: function (decodedGeometry, data, offset) {
|
||
|
const count = decodedGeometry.vertexCount * 4;
|
||
|
decodedGeometry["uv-region"] = new Uint16Array(data, offset, count);
|
||
|
offset += count * 2;
|
||
|
return offset;
|
||
|
},
|
||
|
};
|
||
|
|
||
|
function decodeBinaryGeometry(data, schema, bufferInfo, featureData) {
|
||
|
// From this spec:
|
||
|
// https://github.com/Esri/i3s-spec/blob/master/docs/1.7/defaultGeometrySchema.cmn.md
|
||
|
const decodedGeometry = {
|
||
|
vertexCount: 0,
|
||
|
};
|
||
|
|
||
|
const dataView = new DataView(data);
|
||
|
|
||
|
try {
|
||
|
let offset = 0;
|
||
|
decodedGeometry.vertexCount = dataView.getUint32(offset, 1);
|
||
|
offset += 4;
|
||
|
|
||
|
decodedGeometry.featureCount = dataView.getUint32(offset, 1);
|
||
|
offset += 4;
|
||
|
|
||
|
if (defaultValue.defined(bufferInfo)) {
|
||
|
for (
|
||
|
let attrIndex = 0;
|
||
|
attrIndex < bufferInfo.attributes.length;
|
||
|
attrIndex++
|
||
|
) {
|
||
|
if (
|
||
|
defaultValue.defined(binaryAttributeDecoders[bufferInfo.attributes[attrIndex]])
|
||
|
) {
|
||
|
offset = binaryAttributeDecoders[bufferInfo.attributes[attrIndex]](
|
||
|
decodedGeometry,
|
||
|
data,
|
||
|
offset
|
||
|
);
|
||
|
} else {
|
||
|
console.error(
|
||
|
"Unknown decoder for",
|
||
|
bufferInfo.attributes[attrIndex]
|
||
|
);
|
||
|
}
|
||
|
}
|
||
|
} else {
|
||
|
let ordering = schema.ordering;
|
||
|
let featureAttributeOrder = schema.featureAttributeOrder;
|
||
|
|
||
|
if (
|
||
|
defaultValue.defined(featureData) &&
|
||
|
defaultValue.defined(featureData.geometryData) &&
|
||
|
defaultValue.defined(featureData.geometryData[0]) &&
|
||
|
defaultValue.defined(featureData.geometryData[0].params)
|
||
|
) {
|
||
|
ordering = Object.keys(
|
||
|
featureData.geometryData[0].params.vertexAttributes
|
||
|
);
|
||
|
featureAttributeOrder = Object.keys(
|
||
|
featureData.geometryData[0].params.featureAttributes
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Use default geometry schema
|
||
|
for (let i = 0; i < ordering.length; i++) {
|
||
|
const decoder = binaryAttributeDecoders[ordering[i]];
|
||
|
if (!defaultValue.defined(decoder)) {
|
||
|
console.log(ordering[i]);
|
||
|
}
|
||
|
offset = decoder(decodedGeometry, data, offset);
|
||
|
}
|
||
|
|
||
|
for (let j = 0; j < featureAttributeOrder.length; j++) {
|
||
|
const curDecoder = binaryAttributeDecoders[featureAttributeOrder[j]];
|
||
|
if (!defaultValue.defined(curDecoder)) {
|
||
|
console.log(featureAttributeOrder[j]);
|
||
|
}
|
||
|
offset = curDecoder(decodedGeometry, data, offset);
|
||
|
}
|
||
|
}
|
||
|
} catch (e) {
|
||
|
console.error(e);
|
||
|
}
|
||
|
|
||
|
decodedGeometry.scale_x = 1;
|
||
|
decodedGeometry.scale_y = 1;
|
||
|
|
||
|
return decodedGeometry;
|
||
|
}
|
||
|
|
||
|
function decodeI3S(parameters) {
|
||
|
// Decode the data into geometry
|
||
|
const geometryData = decode(
|
||
|
parameters.binaryData,
|
||
|
parameters.schema,
|
||
|
parameters.bufferInfo,
|
||
|
parameters.featureData
|
||
|
);
|
||
|
|
||
|
// Adjust height from orthometric to ellipsoidal
|
||
|
if (
|
||
|
defaultValue.defined(parameters.geoidDataList) &&
|
||
|
parameters.geoidDataList.length > 0
|
||
|
) {
|
||
|
orthometricToEllipsoidal(
|
||
|
geometryData.vertexCount,
|
||
|
geometryData.positions,
|
||
|
geometryData.scale_x,
|
||
|
geometryData.scale_y,
|
||
|
parameters.cartographicCenter,
|
||
|
parameters.geoidDataList,
|
||
|
false
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Transform vertices to local
|
||
|
transformToLocal(
|
||
|
geometryData.vertexCount,
|
||
|
geometryData.positions,
|
||
|
geometryData.normals,
|
||
|
parameters.cartographicCenter,
|
||
|
parameters.cartesianCenter,
|
||
|
parameters.parentRotation,
|
||
|
parameters.ellipsoidRadiiSquare,
|
||
|
geometryData.scale_x,
|
||
|
geometryData.scale_y
|
||
|
);
|
||
|
|
||
|
// Adjust UVs if there is a UV region
|
||
|
if (defaultValue.defined(geometryData.uv0s) && defaultValue.defined(geometryData["uv-region"])) {
|
||
|
cropUVs(
|
||
|
geometryData.vertexCount,
|
||
|
geometryData.uv0s,
|
||
|
geometryData["uv-region"]
|
||
|
);
|
||
|
}
|
||
|
|
||
|
// Create the final buffer
|
||
|
const meshData = generateGltfBuffer(
|
||
|
geometryData.vertexCount,
|
||
|
geometryData.indices,
|
||
|
geometryData.positions,
|
||
|
geometryData.normals,
|
||
|
geometryData.uv0s,
|
||
|
geometryData.colors
|
||
|
);
|
||
|
|
||
|
const customAttributes = {};
|
||
|
if (defaultValue.defined(geometryData["feature-index"])) {
|
||
|
customAttributes.positions = geometryData.positions;
|
||
|
customAttributes.indices = geometryData.indices;
|
||
|
customAttributes.featureIndex = geometryData["feature-index"];
|
||
|
customAttributes.cartesianCenter = parameters.cartesianCenter;
|
||
|
customAttributes.parentRotation = parameters.parentRotation;
|
||
|
} else if (defaultValue.defined(geometryData["faceRange"])) {
|
||
|
customAttributes.positions = geometryData.positions;
|
||
|
customAttributes.indices = geometryData.indices;
|
||
|
customAttributes.sourceURL = parameters.url;
|
||
|
customAttributes.cartesianCenter = parameters.cartesianCenter;
|
||
|
customAttributes.parentRotation = parameters.parentRotation;
|
||
|
|
||
|
// Build the feature index array from the faceRange.
|
||
|
customAttributes.featureIndex = new Array(geometryData.positions.length);
|
||
|
for (
|
||
|
let range = 0;
|
||
|
range < geometryData["faceRange"].length - 1;
|
||
|
range += 2
|
||
|
) {
|
||
|
const curIndex = range / 2;
|
||
|
const rangeStart = geometryData["faceRange"][range];
|
||
|
const rangeEnd = geometryData["faceRange"][range + 1];
|
||
|
for (let i = rangeStart; i <= rangeEnd; i++) {
|
||
|
customAttributes.featureIndex[i * 3] = curIndex;
|
||
|
customAttributes.featureIndex[i * 3 + 1] = curIndex;
|
||
|
customAttributes.featureIndex[i * 3 + 2] = curIndex;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
meshData._customAttributes = customAttributes;
|
||
|
|
||
|
const results = {
|
||
|
meshData: meshData,
|
||
|
};
|
||
|
|
||
|
return results;
|
||
|
}
|
||
|
|
||
|
function initWorker(dracoModule) {
|
||
|
draco = dracoModule;
|
||
|
self.onmessage = createTaskProcessorWorker(decodeI3S);
|
||
|
self.postMessage(true);
|
||
|
}
|
||
|
|
||
|
function decodeI3SStart(event) {
|
||
|
const data = event.data;
|
||
|
|
||
|
// Expect the first message to be to load a web assembly module
|
||
|
const wasmConfig = data.webAssemblyConfig;
|
||
|
if (defaultValue.defined(wasmConfig)) {
|
||
|
// Require and compile WebAssembly module, or use fallback if not supported
|
||
|
return require([wasmConfig.modulePath], function (dracoModule) {
|
||
|
if (defaultValue.defined(wasmConfig.wasmBinaryFile)) {
|
||
|
if (!defaultValue.defined(dracoModule)) {
|
||
|
dracoModule = self.DracoDecoderModule;
|
||
|
}
|
||
|
|
||
|
dracoModule(wasmConfig).then(function (compiledModule) {
|
||
|
initWorker(compiledModule);
|
||
|
});
|
||
|
} else {
|
||
|
initWorker(dracoModule());
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return decodeI3SStart;
|
||
|
|
||
|
}));
|