/
i3s-loader.ts
125 lines (106 loc) · 3.74 KB
/
i3s-loader.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// loaders.gl
// SPDX-License-Identifier: MIT
// Copyright vis.gl contributors
import type {LoaderWithParser, LoaderOptions} from '@loaders.gl/loader-utils';
import {parse} from '@loaders.gl/core';
import type {I3STilesetHeader} from './types';
import {I3SContentLoader} from './i3s-content-loader';
import {normalizeTileData, normalizeTilesetData} from './lib/parsers/parse-i3s';
import {COORDINATE_SYSTEM} from './lib/parsers/constants';
import {I3SParseOptions} from './types';
import {getUrlWithoutParams} from './lib/utils/url-utils';
// __VERSION__ is injected by babel-plugin-version-inline
// @ts-ignore TS2304: Cannot find name '__VERSION__'.
const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';
const TILESET_REGEX = /layers\/[0-9]+$/;
const LOCAL_SLPK_REGEX = /\.slpk$/;
const TILE_HEADER_REGEX = /nodes\/([0-9-]+|root)$/;
const SLPK_HEX = '504b0304';
const POINT_CLOUD = 'PointCloud';
export type I3SLoaderOptions = LoaderOptions & {
i3s?: I3SParseOptions;
};
/**
* Loader for I3S - Indexed 3D Scene Layer
*/
export const I3SLoader = {
dataType: null as unknown as I3STilesetHeader,
batchType: null as never,
name: 'I3S (Indexed Scene Layers)',
id: 'i3s',
module: 'i3s',
version: VERSION,
mimeTypes: ['application/octet-stream'],
parse: parseI3S,
extensions: ['bin'],
options: {
i3s: {
token: null,
isTileset: 'auto',
isTileHeader: 'auto',
tile: null,
tileset: null,
_tileOptions: null,
_tilesetOptions: null,
useDracoGeometry: true,
useCompressedTextures: true,
decodeTextures: true,
coordinateSystem: COORDINATE_SYSTEM.METER_OFFSETS
}
}
} as const satisfies LoaderWithParser<I3STilesetHeader, never, LoaderOptions>;
async function parseI3S(data, options: I3SLoaderOptions = {}, context): Promise<I3STilesetHeader> {
const url = context.url;
options.i3s = options.i3s || {};
const magicNumber = getMagicNumber(data);
// check if file is slpk
if (magicNumber === SLPK_HEX) {
throw new Error('Files with .slpk extention currently are not supported by I3SLoader');
}
const urlWithoutParams = getUrlWithoutParams(url);
// auto detect file type based on url
let isTileset;
if (options.i3s.isTileset === 'auto') {
isTileset = TILESET_REGEX.test(urlWithoutParams) || LOCAL_SLPK_REGEX.test(urlWithoutParams);
} else {
isTileset = options.i3s.isTileset;
}
let isTileHeader;
if (options.isTileHeader === 'auto') {
isTileHeader = TILE_HEADER_REGEX.test(urlWithoutParams);
} else {
isTileHeader = options.i3s.isTileHeader;
}
if (isTileset) {
data = await parseTileset(data, options, context);
} else if (isTileHeader) {
data = await parseTile(data, context);
} else {
data = await parseTileContent(data, options);
}
return data;
}
async function parseTileContent(arrayBuffer, options: I3SLoaderOptions) {
return await parse(arrayBuffer, I3SContentLoader, options);
}
async function parseTileset(data, options: I3SLoaderOptions, context) {
const tilesetJson = JSON.parse(new TextDecoder().decode(data));
if (tilesetJson?.layerType === POINT_CLOUD) {
throw new Error('Point Cloud layers currently are not supported by I3SLoader');
}
const tilesetPostprocessed = await normalizeTilesetData(tilesetJson, options, context);
return tilesetPostprocessed;
}
async function parseTile(data, context) {
data = JSON.parse(new TextDecoder().decode(data));
return normalizeTileData(data, context);
}
function getMagicNumber(data) {
if (data instanceof ArrayBuffer) {
// slice binary data (4 bytes from the beginning) and transform it to hexadecimal numeral system
return [...new Uint8Array(data, 0, 4)]
.map((value) => value.toString(16).padStart(2, '0'))
.join('');
}
return null;
}