Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore(tile-converter): i3s-converter - mitigate "tiles" module dependency #2517

Merged
merged 4 commits into from
Jun 27, 2023
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions modules/3d-tiles/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,11 @@ export {getIonTilesetMetadata as _getIonTilesetMetadata} from './lib/ion/ion';
export type {
FeatureTableJson,
B3DMContent,
Tile3DBoundingVolume,
Tiles3DTileJSON,
Tiles3DTileJSONPostprocessed,
Tiles3DTilesetJSON,
Tiles3DTilesetJSONPostprocessed,
Tiles3DTileContent,
ImplicitTilingExensionData
} from './types';
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import {Tile3DBatchTable} from '@loaders.gl/3d-tiles';
import {decodeRGB565, GL} from '@loaders.gl/math';
import {Tiles3DTileContent} from 'modules/3d-tiles/src/types';

/* eslint-disable complexity*/
export function normalize3DTileColorAttribute(tile, colors, batchTable?) {
export function normalize3DTileColorAttribute(
tile: Tiles3DTileContent,
colors: Uint8ClampedArray | null,
batchTable?: Tile3DBatchTable
): {type: number; value: Uint8ClampedArray; size: number; normalized: boolean} | null {
// no colors defined
if (!colors && (!tile || !tile.batchIds || !batchTable)) {
return null;
}

const {batchIds, isRGB565, pointCount} = tile;
const {batchIds, isRGB565, pointCount = 0} = tile;
// Batch table, look up colors in table
if (batchIds && batchTable) {
const colorArray = new Uint8ClampedArray(pointCount * 3);
Expand All @@ -29,7 +35,7 @@ export function normalize3DTileColorAttribute(tile, colors, batchTable?) {
}

// RGB565 case, convert to RGB
if (isRGB565) {
if (colors && isRGB565) {
const colorArray = new Uint8ClampedArray(pointCount * 3);
for (let i = 0; i < pointCount; i++) {
const color = decodeRGB565(colors[i]);
Expand Down Expand Up @@ -58,7 +64,7 @@ export function normalize3DTileColorAttribute(tile, colors, batchTable?) {
// DEFAULT: RGBA case
return {
type: GL.UNSIGNED_BYTE,
value: colors,
value: colors || new Uint8ClampedArray(),
size: 4,
normalized: true
};
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
import {Vector3} from '@math.gl/core';
import {GL, octDecode} from '@loaders.gl/math';
import {Tiles3DTileContent} from 'modules/3d-tiles/src/types';

const scratchNormal = new Vector3();

export function normalize3DTileNormalAttribute(tile, normals) {
export function normalize3DTileNormalAttribute(
tile: Tiles3DTileContent,
normals: Float32Array | null
): {type: number; size: number; value: Float32Array} | null {
if (!normals) {
return null;
}

if (tile.isOctEncoded16P) {
const decodedArray = new Float32Array(tile.pointsLength * 3);
for (let i = 0; i < tile.pointsLength; i++) {
const decodedArray = new Float32Array((tile.pointsLength || 0) * 3);
for (let i = 0; i < (tile.pointsLength || 0); i++) {
octDecode(normals[i * 2], normals[i * 2 + 1], scratchNormal);
// @ts-ignore
scratchNormal.toArray(decodedArray, i * 3);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type {Availability, BoundingVolume, Subtree} from '../../../types';
import type {Availability, Tile3DBoundingVolume, Subtree} from '../../../types';
import {Tile3DSubtreeLoader} from '../../../tile-3d-subtree-loader';
import {load} from '@loaders.gl/core';

Expand Down Expand Up @@ -263,7 +263,7 @@ function formatTileData(
const uri = tile.contentUrl && tile.contentUrl.replace(`${basePath}/`, '');
const lodMetricValue = rootLodMetricValue / 2 ** level;

const boundingVolume: BoundingVolume = s2VolumeBox?.box
const boundingVolume: Tile3DBoundingVolume = s2VolumeBox?.box
? {box: s2VolumeBox.box}
: rootBoundingVolume;

Expand Down Expand Up @@ -297,9 +297,9 @@ function formatTileData(
*/
function calculateBoundingVolumeForChildTile(
level: number,
rootBoundingVolume: BoundingVolume,
rootBoundingVolume: Tile3DBoundingVolume,
childCoordinates: {childTileX: number; childTileY: number; childTileZ: number}
): BoundingVolume {
): Tile3DBoundingVolume {
if (rootBoundingVolume.region) {
const {childTileX, childTileY, childTileZ} = childCoordinates;
const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
Expand Down
48 changes: 35 additions & 13 deletions modules/3d-tiles/src/lib/parsers/helpers/parse-3d-tile-gltf-view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,28 +8,35 @@
// - Also, should we have hard dependency on gltf module or use injection or auto-discovery for gltf parser?

import {GLTFLoader, postProcessGLTF, _getMemoryUsageGLTF} from '@loaders.gl/gltf';
import {sliceArrayBuffer} from '@loaders.gl/loader-utils';
import {LoaderContext, sliceArrayBuffer} from '@loaders.gl/loader-utils';
import {Tiles3DLoaderOptions} from 'modules/3d-tiles/src/tiles-3d-loader';
import {Tiles3DTileContent} from 'modules/3d-tiles/src/types';

export const GLTF_FORMAT = {
URI: 0,
EMBEDDED: 1
};

export function parse3DTileGLTFViewSync(tile, arrayBuffer, byteOffset, options) {
export function parse3DTileGLTFViewSync(
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number,
options: Tiles3DLoaderOptions | undefined
) {
// Set flags
// glTF models need to be rotated from Y to Z up
// https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification#y-up-to-z-up
tile.rotateYtoZ = true;

// Assume glTF consumes rest of tile
const gltfByteLength = tile.byteOffset + tile.byteLength - byteOffset;
const gltfByteLength = (tile.byteOffset || 0) + (tile.byteLength || 0) - byteOffset;
if (gltfByteLength === 0) {
throw new Error('glTF byte length must be greater than 0.');
}

// Save gltf up axis
tile.gltfUpAxis =
options['3d-tiles'] && options['3d-tiles'].assetGltfUpAxis
options?.['3d-tiles'] && options['3d-tiles'].assetGltfUpAxis
? options['3d-tiles'].assetGltfUpAxis
: 'Y';

Expand All @@ -50,18 +57,27 @@ export function parse3DTileGLTFViewSync(tile, arrayBuffer, byteOffset, options)
}

// Entire tile is consumed
return tile.byteOffset + tile.byteLength;
return (tile.byteOffset || 0) + (tile.byteLength || 0);
}

export async function extractGLTF(tile, gltfFormat, options, context) {
const tile3DOptions = options['3d-tiles'] || {};
export async function extractGLTF(
tile: Tiles3DTileContent,
gltfFormat: number,
options?: Tiles3DLoaderOptions,
context?: LoaderContext
): Promise<void> {
const tile3DOptions = options?.['3d-tiles'] || {};

extractGLTFBufferOrURL(tile, gltfFormat, options);

if (tile3DOptions.loadGLTF) {
if (!context) {
return;
}
const {parse, fetch} = context;
if (tile.gltfUrl) {
tile.gltfArrayBuffer = await fetch(tile.gltfUrl, options);
const response = await fetch(tile.gltfUrl, options);
tile.gltfArrayBuffer = await response.arrayBuffer();
tile.gltfByteOffset = 0;
}
if (tile.gltfArrayBuffer) {
Expand All @@ -76,15 +92,21 @@ export async function extractGLTF(tile, gltfFormat, options, context) {
}
}

function extractGLTFBufferOrURL(tile, gltfFormat, options) {
function extractGLTFBufferOrURL(
tile: Tiles3DTileContent,
gltfFormat: number,
options: Tiles3DLoaderOptions | undefined
) {
switch (gltfFormat) {
case GLTF_FORMAT.URI:
// We need to remove padding from the end of the model URL in case this tile was part of a composite tile.
// This removes all white space and null characters from the end of the string.
const gltfUrlBytes = new Uint8Array(tile.gltfArrayBuffer, tile.gltfByteOffset);
const textDecoder = new TextDecoder();
const gltfUrl = textDecoder.decode(gltfUrlBytes);
tile.gltfUrl = gltfUrl.replace(/[\s\0]+$/, '');
if (tile.gltfArrayBuffer) {
const gltfUrlBytes = new Uint8Array(tile.gltfArrayBuffer, tile.gltfByteOffset);
const textDecoder = new TextDecoder();
const gltfUrl = textDecoder.decode(gltfUrlBytes);
tile.gltfUrl = gltfUrl.replace(/[\s\0]+$/, '');
}
delete tile.gltfArrayBuffer;
delete tile.gltfByteOffset;
delete tile.gltfByteLength;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// This file is derived from the Cesium code base under Apache 2 license
// See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md

import {Tiles3DTileContent} from 'modules/3d-tiles/src/types';

const SIZEOF_UINT32 = 4;

/* PARSE FIXED HEADER:
Expand All @@ -10,7 +12,11 @@ Populates
version,
byteLength
*/
export function parse3DTileHeaderSync(tile, arrayBuffer, byteOffset = 0) {
export function parse3DTileHeaderSync(
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number = 0
) {
const view = new DataView(arrayBuffer);

tile.magic = view.getUint32(byteOffset, true);
Expand Down
43 changes: 32 additions & 11 deletions modules/3d-tiles/src/lib/parsers/helpers/parse-3d-tile-tables.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
// This file is derived from the Cesium code base under Apache 2 license
// See LICENSE.md and https://github.com/AnalyticalGraphicsInc/cesium/blob/master/LICENSE.md

import {Tiles3DTileContent} from 'modules/3d-tiles/src/types';
import {getStringFromArrayBuffer} from './parse-utils';
import {Tiles3DLoaderOptions} from 'modules/3d-tiles/src/tiles-3d-loader';

const SIZEOF_UINT32 = 4;
const DEPRECATION_WARNING = 'b3dm tile in legacy format.';

// eslint-disable-next-line max-statements
export function parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset) {
export function parse3DTileTablesHeaderSync(
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number
) {
const view = new DataView(arrayBuffer);
let batchLength;

Expand Down Expand Up @@ -58,31 +64,41 @@ export function parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset) {
return byteOffset;
}

export function parse3DTileTablesSync(tile, arrayBuffer, byteOffset, options) {
export function parse3DTileTablesSync(
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number,
options?: Tiles3DLoaderOptions
) {
byteOffset = parse3DTileFeatureTable(tile, arrayBuffer, byteOffset, options);
byteOffset = parse3DTileBatchTable(tile, arrayBuffer, byteOffset, options);
return byteOffset;
}

function parse3DTileFeatureTable(tile, arrayBuffer, byteOffset, options) {
const {featureTableJsonByteLength, featureTableBinaryByteLength, batchLength} = tile.header;
function parse3DTileFeatureTable(
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number,
options?: Tiles3DLoaderOptions
) {
const {featureTableJsonByteLength, featureTableBinaryByteLength, batchLength} = tile.header || {};

tile.featureTableJson = {
BATCH_LENGTH: batchLength || 0
};

if (featureTableJsonByteLength > 0) {
if (featureTableJsonByteLength && featureTableJsonByteLength > 0) {
const featureTableString = getStringFromArrayBuffer(
arrayBuffer,
byteOffset,
featureTableJsonByteLength
);
tile.featureTableJson = JSON.parse(featureTableString);
}
byteOffset += featureTableJsonByteLength;
byteOffset += featureTableJsonByteLength || 0;

tile.featureTableBinary = new Uint8Array(arrayBuffer, byteOffset, featureTableBinaryByteLength);
byteOffset += featureTableBinaryByteLength;
byteOffset += featureTableBinaryByteLength || 0;

/*
const featureTable = parseFeatureTable(featureTableJson, featureTableBinary);
Expand All @@ -94,10 +110,15 @@ function parse3DTileFeatureTable(tile, arrayBuffer, byteOffset, options) {
return byteOffset;
}

function parse3DTileBatchTable(tile, arrayBuffer, byteOffset, options) {
const {batchTableJsonByteLength, batchTableBinaryByteLength} = tile.header;
function parse3DTileBatchTable(
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number,
options?: Tiles3DLoaderOptions
) {
const {batchTableJsonByteLength, batchTableBinaryByteLength} = tile.header || {};

if (batchTableJsonByteLength > 0) {
if (batchTableJsonByteLength && batchTableJsonByteLength > 0) {
const batchTableString = getStringFromArrayBuffer(
arrayBuffer,
byteOffset,
Expand All @@ -106,7 +127,7 @@ function parse3DTileBatchTable(tile, arrayBuffer, byteOffset, options) {
tile.batchTableJson = JSON.parse(batchTableString);
byteOffset += batchTableJsonByteLength;

if (batchTableBinaryByteLength > 0) {
if (batchTableBinaryByteLength && batchTableBinaryByteLength > 0) {
// Has a batch table binary
tile.batchTableBinary = new Uint8Array(arrayBuffer, byteOffset, batchTableBinaryByteLength);
// Copy the batchTableBinary section and let the underlying ArrayBuffer be freed
Expand Down
21 changes: 18 additions & 3 deletions modules/3d-tiles/src/lib/parsers/parse-3d-tile-batched-model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,17 @@ import Tile3DFeatureTable from '../classes/tile-3d-feature-table';
import {parse3DTileHeaderSync} from './helpers/parse-3d-tile-header';
import {parse3DTileTablesHeaderSync, parse3DTileTablesSync} from './helpers/parse-3d-tile-tables';
import {parse3DTileGLTFViewSync, extractGLTF, GLTF_FORMAT} from './helpers/parse-3d-tile-gltf-view';

export async function parseBatchedModel3DTile(tile, arrayBuffer, byteOffset, options, context) {
import {Tiles3DTileContent} from '../../types';
import {Tiles3DLoaderOptions} from '../../tiles-3d-loader';
import {LoaderContext} from '@loaders.gl/loader-utils';

export async function parseBatchedModel3DTile(
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number,
options?: Tiles3DLoaderOptions,
context?: LoaderContext
) {
byteOffset = parseBatchedModel(tile, arrayBuffer, byteOffset, options, context);
await extractGLTF(tile, GLTF_FORMAT.EMBEDDED, options, context);

Expand All @@ -21,7 +30,13 @@ export async function parseBatchedModel3DTile(tile, arrayBuffer, byteOffset, opt
return byteOffset;
}

function parseBatchedModel(tile, arrayBuffer, byteOffset, options, context) {
function parseBatchedModel(
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number,
options?: Tiles3DLoaderOptions,
context?: LoaderContext
) {
byteOffset = parse3DTileHeaderSync(tile, arrayBuffer, byteOffset);

byteOffset = parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset);
Expand Down
13 changes: 7 additions & 6 deletions modules/3d-tiles/src/lib/parsers/parse-3d-tile-composite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,24 @@
import type {LoaderContext} from '@loaders.gl/loader-utils';
import type {Tiles3DLoaderOptions} from '../../tiles-3d-loader';
import {parse3DTileHeaderSync} from './helpers/parse-3d-tile-header';
import {Tiles3DTileContent} from '../../types';

/** Resolve circulate dependency by passing in parsing function as argument */
type Parse3DTile = (
arrayBuffer: ArrayBuffer,
byteOffset: number,
options: Tiles3DLoaderOptions,
context: LoaderContext,
options: Tiles3DLoaderOptions | undefined,
context: LoaderContext | undefined,
subtile
) => Promise<number>;

// eslint-disable-next-line max-params
export async function parseComposite3DTile(
tile,
tile: Tiles3DTileContent,
arrayBuffer: ArrayBuffer,
byteOffset: number,
options: Tiles3DLoaderOptions,
context: LoaderContext,
options: Tiles3DLoaderOptions | undefined,
context: LoaderContext | undefined,
parse3DTile: Parse3DTile
): Promise<number> {
byteOffset = parse3DTileHeaderSync(tile, arrayBuffer, byteOffset);
Expand All @@ -36,7 +37,7 @@ export async function parseComposite3DTile(

// extract each tile from the byte stream
tile.tiles = [];
while (tile.tiles.length < tile.tilesLength && tile.byteLength - byteOffset > 12) {
while (tile.tiles.length < tile.tilesLength && (tile.byteLength || 0) - byteOffset > 12) {
const subtile = {};
tile.tiles.push(subtile);
byteOffset = await parse3DTile(arrayBuffer, byteOffset, options, context, subtile);
Expand Down
Loading