@loaders.gl/3d-tiles 4.0.0-alpha.4 → 4.0.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.
- package/dist/bundle.d.ts +2 -0
- package/dist/bundle.d.ts.map +1 -0
- package/dist/cesium-ion-loader.d.ts +6 -0
- package/dist/cesium-ion-loader.d.ts.map +1 -0
- package/dist/dist.min.js +12227 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/classes/helpers/tile-3d-accessor-utils.d.ts +8 -0
- package/dist/lib/classes/helpers/tile-3d-accessor-utils.d.ts.map +1 -0
- package/dist/lib/classes/helpers/tile-3d-accessor-utils.js.map +1 -1
- package/dist/lib/classes/tile-3d-batch-table-hierarchy.d.ts +10 -0
- package/dist/lib/classes/tile-3d-batch-table-hierarchy.d.ts.map +1 -0
- package/dist/lib/classes/tile-3d-batch-table-hierarchy.js.map +1 -1
- package/dist/lib/classes/tile-3d-batch-table.d.ts +34 -0
- package/dist/lib/classes/tile-3d-batch-table.d.ts.map +1 -0
- package/dist/lib/classes/tile-3d-batch-table.js +16 -1
- package/dist/lib/classes/tile-3d-batch-table.js.map +1 -1
- package/dist/lib/classes/tile-3d-feature-table.d.ts +15 -0
- package/dist/lib/classes/tile-3d-feature-table.d.ts.map +1 -0
- package/dist/lib/classes/tile-3d-feature-table.js +9 -2
- package/dist/lib/classes/tile-3d-feature-table.js.map +1 -1
- package/dist/lib/constants.d.ts +22 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/constants.js +2 -1
- package/dist/lib/constants.js.map +1 -1
- package/dist/lib/encoders/encode-3d-tile-batched-model.d.ts +2 -0
- package/dist/lib/encoders/encode-3d-tile-batched-model.d.ts.map +1 -0
- package/dist/lib/encoders/encode-3d-tile-batched-model.js.map +1 -1
- package/dist/lib/encoders/encode-3d-tile-composite.d.ts +2 -0
- package/dist/lib/encoders/encode-3d-tile-composite.d.ts.map +1 -0
- package/dist/lib/encoders/encode-3d-tile-composite.js.map +1 -1
- package/dist/lib/encoders/encode-3d-tile-instanced-model.d.ts +2 -0
- package/dist/lib/encoders/encode-3d-tile-instanced-model.d.ts.map +1 -0
- package/dist/lib/encoders/encode-3d-tile-instanced-model.js.map +1 -1
- package/dist/lib/encoders/encode-3d-tile-point-cloud.d.ts +2 -0
- package/dist/lib/encoders/encode-3d-tile-point-cloud.d.ts.map +1 -0
- package/dist/lib/encoders/encode-3d-tile-point-cloud.js.map +1 -1
- package/dist/lib/encoders/encode-3d-tile.d.ts +2 -0
- package/dist/lib/encoders/encode-3d-tile.d.ts.map +1 -0
- package/dist/lib/encoders/encode-3d-tile.js.map +1 -1
- package/dist/lib/encoders/helpers/encode-3d-tile-header.d.ts +3 -0
- package/dist/lib/encoders/helpers/encode-3d-tile-header.d.ts.map +1 -0
- package/dist/lib/encoders/helpers/encode-3d-tile-header.js.map +1 -1
- package/dist/lib/ion/ion.d.ts +4 -0
- package/dist/lib/ion/ion.d.ts.map +1 -0
- package/dist/lib/ion/ion.js.map +1 -1
- package/dist/lib/parsers/helpers/normalize-3d-tile-colors.d.ts +7 -0
- package/dist/lib/parsers/helpers/normalize-3d-tile-colors.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/normalize-3d-tile-colors.js.map +1 -1
- package/dist/lib/parsers/helpers/normalize-3d-tile-normals.d.ts +6 -0
- package/dist/lib/parsers/helpers/normalize-3d-tile-normals.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/normalize-3d-tile-normals.js.map +1 -1
- package/dist/lib/parsers/helpers/normalize-3d-tile-positions.d.ts +2 -0
- package/dist/lib/parsers/helpers/normalize-3d-tile-positions.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/normalize-3d-tile-positions.js.map +1 -1
- package/dist/lib/parsers/helpers/parse-3d-implicit-tiles.d.ts +46 -0
- package/dist/lib/parsers/helpers/parse-3d-implicit-tiles.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/parse-3d-implicit-tiles.js +222 -0
- package/dist/lib/parsers/helpers/parse-3d-implicit-tiles.js.map +1 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-gltf-view.d.ts +7 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-gltf-view.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-gltf-view.js +1 -1
- package/dist/lib/parsers/helpers/parse-3d-tile-gltf-view.js.map +1 -1
- package/dist/lib/parsers/helpers/parse-3d-tile-header.d.ts +2 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-header.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-header.js.map +1 -1
- package/dist/lib/parsers/helpers/parse-3d-tile-subtree.d.ts +9 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-subtree.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-subtree.js +64 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-subtree.js.map +1 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-tables.d.ts +3 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-tables.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/parse-3d-tile-tables.js +1 -1
- package/dist/lib/parsers/helpers/parse-3d-tile-tables.js.map +1 -1
- package/dist/lib/parsers/helpers/parse-utils.d.ts +4 -0
- package/dist/lib/parsers/helpers/parse-utils.d.ts.map +1 -0
- package/dist/lib/parsers/helpers/parse-utils.js.map +1 -1
- package/dist/lib/parsers/parse-3d-tile-batched-model.d.ts +2 -0
- package/dist/lib/parsers/parse-3d-tile-batched-model.d.ts.map +1 -0
- package/dist/lib/parsers/parse-3d-tile-batched-model.js.map +1 -1
- package/dist/lib/parsers/parse-3d-tile-composite.d.ts +2 -0
- package/dist/lib/parsers/parse-3d-tile-composite.d.ts.map +1 -0
- package/dist/lib/parsers/parse-3d-tile-composite.js.map +1 -1
- package/dist/lib/parsers/parse-3d-tile-gltf.d.ts +2 -0
- package/dist/lib/parsers/parse-3d-tile-gltf.d.ts.map +1 -0
- package/dist/lib/parsers/parse-3d-tile-gltf.js +10 -0
- package/dist/lib/parsers/parse-3d-tile-gltf.js.map +1 -0
- package/dist/lib/parsers/parse-3d-tile-header.d.ts +19 -0
- package/dist/lib/parsers/parse-3d-tile-header.d.ts.map +1 -0
- package/dist/lib/parsers/parse-3d-tile-header.js +72 -0
- package/dist/lib/parsers/parse-3d-tile-header.js.map +1 -1
- package/dist/lib/parsers/parse-3d-tile-instanced-model.d.ts +2 -0
- package/dist/lib/parsers/parse-3d-tile-instanced-model.d.ts.map +1 -0
- package/dist/lib/parsers/parse-3d-tile-instanced-model.js.map +1 -1
- package/dist/lib/parsers/parse-3d-tile-point-cloud.d.ts +3 -0
- package/dist/lib/parsers/parse-3d-tile-point-cloud.d.ts.map +1 -0
- package/dist/lib/parsers/parse-3d-tile-point-cloud.js +1 -1
- package/dist/lib/parsers/parse-3d-tile-point-cloud.js.map +1 -1
- package/dist/lib/parsers/parse-3d-tile.d.ts +2 -0
- package/dist/lib/parsers/parse-3d-tile.d.ts.map +1 -0
- package/dist/lib/parsers/parse-3d-tile.js +4 -0
- package/dist/lib/parsers/parse-3d-tile.js.map +1 -1
- package/dist/lib/utils/version.d.ts +2 -0
- package/dist/lib/utils/version.d.ts.map +1 -0
- package/dist/lib/utils/version.js +1 -1
- package/dist/lib/utils/version.js.map +1 -1
- package/dist/tile-3d-subtree-loader.d.ts +7 -0
- package/dist/tile-3d-subtree-loader.d.ts.map +1 -0
- package/dist/tile-3d-subtree-loader.js +14 -0
- package/dist/tile-3d-subtree-loader.js.map +1 -0
- package/dist/tile-3d-writer.d.ts +6 -0
- package/dist/tile-3d-writer.d.ts.map +1 -0
- package/dist/tiles-3d-loader.d.ts +6 -0
- package/dist/tiles-3d-loader.d.ts.map +1 -0
- package/dist/tiles-3d-loader.js +9 -2
- package/dist/tiles-3d-loader.js.map +1 -1
- package/dist/types.d.ts +80 -0
- package/dist/types.d.ts.map +1 -0
- package/package.json +10 -10
- package/src/index.ts +1 -0
- package/src/lib/classes/helpers/{tile-3d-accessor-utils.js → tile-3d-accessor-utils.ts} +0 -0
- package/src/lib/classes/{tile-3d-batch-table-hierarchy.js → tile-3d-batch-table-hierarchy.ts} +0 -0
- package/src/lib/classes/{tile-3d-batch-table.js → tile-3d-batch-table.ts} +14 -4
- package/src/lib/classes/{tile-3d-feature-table.js → tile-3d-feature-table.ts} +5 -2
- package/src/lib/{constants.js → constants.ts} +2 -1
- package/src/lib/encoders/{encode-3d-tile-batched-model.js → encode-3d-tile-batched-model.ts} +0 -0
- package/src/lib/encoders/{encode-3d-tile-composite.js → encode-3d-tile-composite.ts} +0 -0
- package/src/lib/encoders/{encode-3d-tile-instanced-model.js → encode-3d-tile-instanced-model.ts} +0 -0
- package/src/lib/encoders/{encode-3d-tile-point-cloud.js → encode-3d-tile-point-cloud.ts} +0 -0
- package/src/lib/encoders/{encode-3d-tile.js → encode-3d-tile.ts} +0 -0
- package/src/lib/encoders/helpers/{encode-3d-tile-header.js → encode-3d-tile-header.ts} +0 -0
- package/src/lib/ion/{ion.js → ion.ts} +0 -0
- package/src/lib/parsers/helpers/{normalize-3d-tile-colors.js → normalize-3d-tile-colors.ts} +1 -1
- package/src/lib/parsers/helpers/{normalize-3d-tile-normals.js → normalize-3d-tile-normals.ts} +0 -0
- package/src/lib/parsers/helpers/{normalize-3d-tile-positions.js → normalize-3d-tile-positions.ts} +0 -0
- package/src/lib/parsers/helpers/parse-3d-implicit-tiles.ts +319 -0
- package/src/lib/parsers/helpers/{parse-3d-tile-gltf-view.js → parse-3d-tile-gltf-view.ts} +1 -1
- package/src/lib/parsers/helpers/{parse-3d-tile-header.js → parse-3d-tile-header.ts} +0 -0
- package/src/lib/parsers/helpers/parse-3d-tile-subtree.ts +105 -0
- package/src/lib/parsers/helpers/{parse-3d-tile-tables.js → parse-3d-tile-tables.ts} +1 -1
- package/src/lib/parsers/helpers/{parse-utils.js → parse-utils.ts} +0 -0
- package/src/lib/parsers/{parse-3d-tile-batched-model.js → parse-3d-tile-batched-model.ts} +0 -0
- package/src/lib/parsers/{parse-3d-tile-composite.js → parse-3d-tile-composite.ts} +0 -0
- package/src/lib/parsers/parse-3d-tile-gltf.js +16 -0
- package/src/lib/parsers/parse-3d-tile-header.ts +152 -0
- package/src/lib/parsers/{parse-3d-tile-instanced-model.js → parse-3d-tile-instanced-model.ts} +6 -0
- package/src/lib/parsers/{parse-3d-tile-point-cloud.js → parse-3d-tile-point-cloud.ts} +2 -2
- package/src/lib/parsers/{parse-3d-tile.js → parse-3d-tile.ts} +8 -0
- package/src/lib/utils/{version.js → version.ts} +0 -0
- package/src/tile-3d-subtree-loader.ts +19 -0
- package/src/tiles-3d-loader.ts +17 -2
- package/src/types.ts +40 -1
- package/src/lib/parsers/parse-3d-tile-header.js +0 -69
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
import type {Availability, BoundingVolume, Subtree} from '../../../types';
|
|
2
|
+
import {Tile3DSubtreeLoader} from '../../../tile-3d-subtree-loader';
|
|
3
|
+
import {load} from '@loaders.gl/core';
|
|
4
|
+
|
|
5
|
+
const QUADTREE_DEVISION_COUNT = 4;
|
|
6
|
+
const OCTREE_DEVISION_COUNT = 8;
|
|
7
|
+
|
|
8
|
+
const SUBDIVISION_COUNT_MAP = {
|
|
9
|
+
QUADTREE: QUADTREE_DEVISION_COUNT,
|
|
10
|
+
OCTREE: OCTREE_DEVISION_COUNT
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Recursively parse implicit tiles tree
|
|
15
|
+
* Spec - https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_implicit_tiling
|
|
16
|
+
* TODO Check out do we able to use Tile3D class as return type here.
|
|
17
|
+
* @param subtree
|
|
18
|
+
* @param lodMetricValue
|
|
19
|
+
* @param options
|
|
20
|
+
* @param parentData
|
|
21
|
+
* @param childIndex
|
|
22
|
+
* @param level
|
|
23
|
+
* @param globalData
|
|
24
|
+
*/
|
|
25
|
+
// eslint-disable-next-line max-statements
|
|
26
|
+
export async function parseImplicitTiles(params: {
|
|
27
|
+
subtree: Subtree;
|
|
28
|
+
options: any;
|
|
29
|
+
parentData?: {mortonIndex: number; x: number; y: number; z: number};
|
|
30
|
+
childIndex?: number;
|
|
31
|
+
level?: number;
|
|
32
|
+
globalData?: {level: number; mortonIndex: number; x: number; y: number; z: number};
|
|
33
|
+
}) {
|
|
34
|
+
const {
|
|
35
|
+
options,
|
|
36
|
+
parentData = {
|
|
37
|
+
mortonIndex: 0,
|
|
38
|
+
x: 0,
|
|
39
|
+
y: 0,
|
|
40
|
+
z: 0
|
|
41
|
+
},
|
|
42
|
+
childIndex = 0,
|
|
43
|
+
globalData = {
|
|
44
|
+
level: 0,
|
|
45
|
+
mortonIndex: 0,
|
|
46
|
+
x: 0,
|
|
47
|
+
y: 0,
|
|
48
|
+
z: 0
|
|
49
|
+
}
|
|
50
|
+
} = params;
|
|
51
|
+
let {subtree, level = 0} = params;
|
|
52
|
+
const {
|
|
53
|
+
subdivisionScheme,
|
|
54
|
+
subtreeLevels,
|
|
55
|
+
maximumLevel,
|
|
56
|
+
contentUrlTemplate,
|
|
57
|
+
subtreesUriTemplate,
|
|
58
|
+
basePath
|
|
59
|
+
} = options;
|
|
60
|
+
|
|
61
|
+
const tile = {children: [], lodMetricValue: 0, contentUrl: ''};
|
|
62
|
+
|
|
63
|
+
const childrenPerTile = SUBDIVISION_COUNT_MAP[subdivisionScheme];
|
|
64
|
+
|
|
65
|
+
const childX = childIndex & 0b01;
|
|
66
|
+
const childY = (childIndex >> 1) & 0b01;
|
|
67
|
+
const childZ = (childIndex >> 2) & 0b01;
|
|
68
|
+
|
|
69
|
+
const levelOffset = (childrenPerTile ** level - 1) / (childrenPerTile - 1);
|
|
70
|
+
let childTileMortonIndex = concatBits(parentData.mortonIndex, childIndex);
|
|
71
|
+
let tileAvailabilityIndex = levelOffset + childTileMortonIndex;
|
|
72
|
+
|
|
73
|
+
// Local tile coordinates
|
|
74
|
+
let childTileX = concatBits(parentData.x, childX);
|
|
75
|
+
let childTileY = concatBits(parentData.y, childY);
|
|
76
|
+
let childTileZ = concatBits(parentData.z, childZ);
|
|
77
|
+
|
|
78
|
+
// TODO Remove after real implicit tileset will be tested.
|
|
79
|
+
// Degug data
|
|
80
|
+
// tile.level = level + globalData.level;
|
|
81
|
+
// tile.x = concatBits(globalData.x, childTileX);
|
|
82
|
+
// tile.y = concatBits(globalData.y, childTileY);
|
|
83
|
+
// tile.z = concatBits(globalData.z, childTileZ);
|
|
84
|
+
// tile.mortonIndex = childTileMortonIndex;
|
|
85
|
+
// End of debug data
|
|
86
|
+
|
|
87
|
+
let isChildSubtreeAvailable = false;
|
|
88
|
+
|
|
89
|
+
if (level + 1 > subtreeLevels) {
|
|
90
|
+
isChildSubtreeAvailable = getAvailabilityResult(
|
|
91
|
+
subtree.childSubtreeAvailability,
|
|
92
|
+
childTileMortonIndex
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const x = concatBits(globalData.x, childTileX);
|
|
97
|
+
const y = concatBits(globalData.y, childTileY);
|
|
98
|
+
const z = concatBits(globalData.z, childTileZ);
|
|
99
|
+
const lev = level + globalData.level;
|
|
100
|
+
|
|
101
|
+
if (isChildSubtreeAvailable) {
|
|
102
|
+
const subtreePath = `${basePath}/${subtreesUriTemplate}`;
|
|
103
|
+
const childSubtreeUrl = replaceContentUrlTemplate(subtreePath, lev, x, y, z);
|
|
104
|
+
const childSubtree = await load(childSubtreeUrl, Tile3DSubtreeLoader);
|
|
105
|
+
|
|
106
|
+
subtree = childSubtree;
|
|
107
|
+
|
|
108
|
+
globalData.mortonIndex = childTileMortonIndex;
|
|
109
|
+
globalData.x = childTileX;
|
|
110
|
+
globalData.y = childTileY;
|
|
111
|
+
globalData.z = childTileZ;
|
|
112
|
+
globalData.level = level;
|
|
113
|
+
|
|
114
|
+
childTileMortonIndex = 0;
|
|
115
|
+
tileAvailabilityIndex = 0;
|
|
116
|
+
childTileX = 0;
|
|
117
|
+
childTileY = 0;
|
|
118
|
+
childTileZ = 0;
|
|
119
|
+
level = 0;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
const isTileAvailable = getAvailabilityResult(subtree.tileAvailability, tileAvailabilityIndex);
|
|
123
|
+
|
|
124
|
+
if (!isTileAvailable || level > maximumLevel) {
|
|
125
|
+
return tile;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const isContentAvailable = getAvailabilityResult(
|
|
129
|
+
subtree.contentAvailability,
|
|
130
|
+
tileAvailabilityIndex
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (isContentAvailable) {
|
|
134
|
+
tile.contentUrl = replaceContentUrlTemplate(contentUrlTemplate, lev, x, y, z);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const childTileLevel = level + 1;
|
|
138
|
+
const pData = {mortonIndex: childTileMortonIndex, x: childTileX, y: childTileY, z: childTileZ};
|
|
139
|
+
|
|
140
|
+
for (let index = 0; index < childrenPerTile; index++) {
|
|
141
|
+
const currentTile = await parseImplicitTiles({
|
|
142
|
+
subtree,
|
|
143
|
+
options,
|
|
144
|
+
parentData: pData,
|
|
145
|
+
childIndex: index,
|
|
146
|
+
level: childTileLevel,
|
|
147
|
+
globalData
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
if (currentTile.contentUrl || currentTile.children.length) {
|
|
151
|
+
const globalLevel = lev + 1;
|
|
152
|
+
const childCoordinates = {childTileX, childTileY, childTileZ};
|
|
153
|
+
const formattedTile = formatTileData(currentTile, globalLevel, childCoordinates, options);
|
|
154
|
+
// @ts-ignore
|
|
155
|
+
tile.children.push(formattedTile);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return tile;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function getAvailabilityResult(availabilityData: Availability, index: number): boolean {
|
|
163
|
+
if ('constant' in availabilityData) {
|
|
164
|
+
return Boolean(availabilityData.constant);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (availabilityData.explicitBitstream) {
|
|
168
|
+
return getBooleanValueFromBitstream(index, availabilityData.explicitBitstream);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Do formatting of implicit tile data.
|
|
176
|
+
* TODO Check out do we able to use Tile3D class as type here.
|
|
177
|
+
* @param tile
|
|
178
|
+
* @param lodMetricValue
|
|
179
|
+
* @param options
|
|
180
|
+
* @returns
|
|
181
|
+
*/
|
|
182
|
+
function formatTileData(
|
|
183
|
+
tile,
|
|
184
|
+
level: number,
|
|
185
|
+
childCoordinates: {childTileX: number; childTileY: number; childTileZ: number},
|
|
186
|
+
options: any
|
|
187
|
+
) {
|
|
188
|
+
const {
|
|
189
|
+
basePath,
|
|
190
|
+
refine,
|
|
191
|
+
getRefine,
|
|
192
|
+
lodMetricType,
|
|
193
|
+
getTileType,
|
|
194
|
+
rootLodMetricValue,
|
|
195
|
+
rootBoundingVolume
|
|
196
|
+
} = options;
|
|
197
|
+
const uri = tile.contentUrl && tile.contentUrl.replace(`${basePath}/`, '');
|
|
198
|
+
const lodMetricValue = rootLodMetricValue / 2 ** level;
|
|
199
|
+
const boundingVolume = calculateBoundingVolumeForChildTile(
|
|
200
|
+
level,
|
|
201
|
+
rootBoundingVolume,
|
|
202
|
+
childCoordinates
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
return {
|
|
206
|
+
children: tile.children,
|
|
207
|
+
contentUrl: tile.contentUrl,
|
|
208
|
+
content: {uri},
|
|
209
|
+
id: tile.contentUrl,
|
|
210
|
+
refine: getRefine(refine),
|
|
211
|
+
type: getTileType(tile),
|
|
212
|
+
lodMetricType,
|
|
213
|
+
lodMetricValue,
|
|
214
|
+
boundingVolume
|
|
215
|
+
// Temp debug values. Remove when real implicit tileset will be tested.
|
|
216
|
+
// x: tile.x,
|
|
217
|
+
// y: tile.y,
|
|
218
|
+
// z: tile.z,
|
|
219
|
+
// level: tile.level
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Calculate child bounding volume.
|
|
225
|
+
* Spec - https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_implicit_tiling#subdivision-rules
|
|
226
|
+
* @param level
|
|
227
|
+
* @param rootBoundingVolume
|
|
228
|
+
* @param childCoordinates
|
|
229
|
+
*/
|
|
230
|
+
function calculateBoundingVolumeForChildTile(
|
|
231
|
+
level: number,
|
|
232
|
+
rootBoundingVolume: BoundingVolume,
|
|
233
|
+
childCoordinates: {childTileX: number; childTileY: number; childTileZ: number}
|
|
234
|
+
): BoundingVolume | null {
|
|
235
|
+
if (rootBoundingVolume.region) {
|
|
236
|
+
const {childTileX, childTileY, childTileZ} = childCoordinates;
|
|
237
|
+
const [west, south, east, north, minimumHeight, maximumHeight] = rootBoundingVolume.region;
|
|
238
|
+
const boundingVolumesCount = 2 ** level;
|
|
239
|
+
|
|
240
|
+
const sizeX = (east - west) / boundingVolumesCount;
|
|
241
|
+
const sizeY = (north - south) / boundingVolumesCount;
|
|
242
|
+
const sizeZ = (maximumHeight - minimumHeight) / boundingVolumesCount;
|
|
243
|
+
|
|
244
|
+
const [childWest, childEast] = [west + sizeX * childTileX, west + sizeX * (childTileX + 1)];
|
|
245
|
+
const [childSouth, childNorth] = [south + sizeY * childTileY, south + sizeY * (childTileY + 1)];
|
|
246
|
+
const [childMinimumHeight, childMaximumHeight] = [
|
|
247
|
+
minimumHeight + sizeZ * childTileZ,
|
|
248
|
+
minimumHeight + sizeZ * (childTileZ + 1)
|
|
249
|
+
];
|
|
250
|
+
|
|
251
|
+
return {
|
|
252
|
+
region: [childWest, childSouth, childEast, childNorth, childMinimumHeight, childMaximumHeight]
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// eslint-disable-next-line no-console
|
|
257
|
+
console.warn('Unsupported bounding volume type: ', rootBoundingVolume);
|
|
258
|
+
return null;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/**
|
|
262
|
+
* Do binary concatenation
|
|
263
|
+
* @param first
|
|
264
|
+
* @param second
|
|
265
|
+
*/
|
|
266
|
+
function concatBits(first: number, second: number): number {
|
|
267
|
+
return parseInt(first.toString(2) + second.toString(2), 2);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
/**
|
|
271
|
+
* Replace implicit tile content url with real coordinates.
|
|
272
|
+
* @param templateUrl
|
|
273
|
+
* @param level
|
|
274
|
+
* @param x
|
|
275
|
+
* @param y
|
|
276
|
+
* @param z
|
|
277
|
+
*/
|
|
278
|
+
export function replaceContentUrlTemplate(
|
|
279
|
+
templateUrl: string,
|
|
280
|
+
level: number,
|
|
281
|
+
x: number,
|
|
282
|
+
y: number,
|
|
283
|
+
z: number
|
|
284
|
+
): string {
|
|
285
|
+
const mapUrl = generateMapUrl({level, x, y, z});
|
|
286
|
+
return templateUrl.replace(/{level}|{x}|{y}|{z}/gi, (matched) => mapUrl[matched]);
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
/**
|
|
290
|
+
* Get Map object for content url generation
|
|
291
|
+
* @param items
|
|
292
|
+
*/
|
|
293
|
+
function generateMapUrl(items: {[key: string]: number}): {[key: string]: string} {
|
|
294
|
+
const mapUrl = {};
|
|
295
|
+
|
|
296
|
+
for (const key in items) {
|
|
297
|
+
mapUrl[`{${key}}`] = items[key];
|
|
298
|
+
}
|
|
299
|
+
return mapUrl;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Get boolean value from bistream by index
|
|
304
|
+
* A boolean value is encoded as a single bit, either 0 (false) or 1 (true).
|
|
305
|
+
* Multiple boolean values are packed tightly in the same buffer.
|
|
306
|
+
* These buffers of tightly-packed bits are sometimes referred to as bitstreams.
|
|
307
|
+
* Spec - https://github.com/CesiumGS/3d-tiles/tree/implicit-revisions/specification/Metadata#booleans
|
|
308
|
+
* @param availabilitiIndex
|
|
309
|
+
*/
|
|
310
|
+
function getBooleanValueFromBitstream(
|
|
311
|
+
availabilityIndex: number,
|
|
312
|
+
availabilityBuffer: Uint8Array
|
|
313
|
+
): boolean {
|
|
314
|
+
const byteIndex = Math.floor(availabilityIndex / 8);
|
|
315
|
+
const bitIndex = availabilityIndex % 8;
|
|
316
|
+
const bitValue = (availabilityBuffer[byteIndex] >> bitIndex) & 1;
|
|
317
|
+
|
|
318
|
+
return bitValue === 1;
|
|
319
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import type {Subtree, ExplicitBitstream} from '../../../types';
|
|
2
|
+
import {fetchFile} from '@loaders.gl/core';
|
|
3
|
+
|
|
4
|
+
const SUBTREE_FILE_MAGIC = 0x74627573;
|
|
5
|
+
const SUBTREE_FILE_VERSION = 1;
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Parse subtree file
|
|
9
|
+
* Spec - https://github.com/CesiumGS/3d-tiles/tree/main/extensions/3DTILES_implicit_tiling#subtree-file-format
|
|
10
|
+
* @param data
|
|
11
|
+
* @returns
|
|
12
|
+
*/
|
|
13
|
+
// eslint-disable-next-line max-statements
|
|
14
|
+
export default async function parse3DTilesSubtree(data: ArrayBuffer): Promise<Subtree> {
|
|
15
|
+
const magic = new Uint32Array(data.slice(0, 4));
|
|
16
|
+
|
|
17
|
+
if (magic[0] !== SUBTREE_FILE_MAGIC) {
|
|
18
|
+
throw new Error('Wrong subtree file magic number');
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const version = new Uint32Array(data.slice(4, 8));
|
|
22
|
+
|
|
23
|
+
if (version[0] !== SUBTREE_FILE_VERSION) {
|
|
24
|
+
throw new Error('Wrong subtree file verson, must be 1');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const jsonByteLength = parseUint64Value(data.slice(8, 16));
|
|
28
|
+
const stringAttribute = new Uint8Array(data, 24, jsonByteLength);
|
|
29
|
+
|
|
30
|
+
const textDecoder = new TextDecoder('utf8');
|
|
31
|
+
const string = textDecoder.decode(stringAttribute);
|
|
32
|
+
const subtree = JSON.parse(string);
|
|
33
|
+
|
|
34
|
+
const binaryByteLength = parseUint64Value(data.slice(16, 24));
|
|
35
|
+
let internalBinaryBuffer = new ArrayBuffer(0);
|
|
36
|
+
|
|
37
|
+
if (binaryByteLength) {
|
|
38
|
+
internalBinaryBuffer = data.slice(24 + jsonByteLength);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
if ('bufferView' in subtree.tileAvailability) {
|
|
42
|
+
subtree.tileAvailability.explicitBitstream = await getExplicitBitstream(
|
|
43
|
+
subtree,
|
|
44
|
+
'tileAvailability',
|
|
45
|
+
internalBinaryBuffer
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if ('bufferView' in subtree.contentAvailability) {
|
|
50
|
+
subtree.contentAvailability.explicitBitstream = await getExplicitBitstream(
|
|
51
|
+
subtree,
|
|
52
|
+
'contentAvailability',
|
|
53
|
+
internalBinaryBuffer
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
if ('bufferView' in subtree.childSubtreeAvailability) {
|
|
58
|
+
subtree.childSubtreeAvailability.explicitBitstream = await getExplicitBitstream(
|
|
59
|
+
subtree,
|
|
60
|
+
'childSubtreeAvailability',
|
|
61
|
+
internalBinaryBuffer
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return subtree;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Get explicit bitstream for subtree availability data.
|
|
70
|
+
* @param subtree
|
|
71
|
+
* @param name
|
|
72
|
+
* @param internalBinaryBuffer
|
|
73
|
+
*/
|
|
74
|
+
async function getExplicitBitstream(
|
|
75
|
+
subtree: Subtree,
|
|
76
|
+
name: string,
|
|
77
|
+
internalBinaryBuffer: ArrayBuffer
|
|
78
|
+
): Promise<ExplicitBitstream> {
|
|
79
|
+
const bufferViewIndex = subtree[name].bufferView;
|
|
80
|
+
const bufferView = subtree.bufferViews[bufferViewIndex];
|
|
81
|
+
const buffer = subtree.buffers[bufferView.buffer];
|
|
82
|
+
|
|
83
|
+
// External bitstream loading
|
|
84
|
+
if (buffer.uri) {
|
|
85
|
+
const response = await fetchFile(buffer.uri);
|
|
86
|
+
const data = await response.arrayBuffer();
|
|
87
|
+
// Return view of bitstream.
|
|
88
|
+
return new Uint8Array(data, bufferView.byteOffset, bufferView.byteLength);
|
|
89
|
+
}
|
|
90
|
+
// Return view of bitstream.
|
|
91
|
+
return new Uint8Array(internalBinaryBuffer, bufferView.byteOffset, bufferView.byteLength);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Parse buffer to return uint64 value
|
|
96
|
+
* @param buffer
|
|
97
|
+
* @returns 64-bit value until precision is lost after Number.MAX_SAFE_INTEGER
|
|
98
|
+
*/
|
|
99
|
+
function parseUint64Value(buffer: ArrayBuffer): number {
|
|
100
|
+
const dataView = new DataView(buffer);
|
|
101
|
+
const left = dataView.getUint32(0, true);
|
|
102
|
+
const right = dataView.getUint32(4, true);
|
|
103
|
+
// combine the two 32-bit values
|
|
104
|
+
return left + 2 ** 32 * right;
|
|
105
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
import {getStringFromArrayBuffer} from './parse-utils';
|
|
5
5
|
|
|
6
6
|
const SIZEOF_UINT32 = 4;
|
|
7
|
-
const DEPRECATION_WARNING =
|
|
7
|
+
const DEPRECATION_WARNING = 'b3dm tile in legacy format.';
|
|
8
8
|
|
|
9
9
|
// eslint-disable-next-line max-statements
|
|
10
10
|
export function parse3DTileTablesHeaderSync(tile, arrayBuffer, byteOffset) {
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import {GLTFLoader} from '@loaders.gl/gltf';
|
|
2
|
+
|
|
3
|
+
export async function parseGltf3DTile(tile, arrayBuffer, options, context) {
|
|
4
|
+
// Set flags
|
|
5
|
+
// glTF models need to be rotated from Y to Z up
|
|
6
|
+
// https://github.com/AnalyticalGraphicsInc/3d-tiles/tree/master/specification#y-up-to-z-up
|
|
7
|
+
tile.rotateYtoZ = true;
|
|
8
|
+
// Save gltf up axis
|
|
9
|
+
tile.gltfUpAxis =
|
|
10
|
+
options['3d-tiles'] && options['3d-tiles'].assetGltfUpAxis
|
|
11
|
+
? options['3d-tiles'].assetGltfUpAxis
|
|
12
|
+
: 'Y';
|
|
13
|
+
|
|
14
|
+
const {parse} = context;
|
|
15
|
+
tile.gltf = await parse(arrayBuffer, GLTFLoader, options, context);
|
|
16
|
+
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import {Tile3DSubtreeLoader} from '../../tile-3d-subtree-loader';
|
|
2
|
+
import {load} from '@loaders.gl/core';
|
|
3
|
+
import {Tileset3D, LOD_METRIC_TYPE, TILE_REFINEMENT, TILE_TYPE} from '@loaders.gl/tiles';
|
|
4
|
+
import {Subtree} from '../../types';
|
|
5
|
+
import {parseImplicitTiles, replaceContentUrlTemplate} from './helpers/parse-3d-implicit-tiles';
|
|
6
|
+
|
|
7
|
+
function getTileType(tile) {
|
|
8
|
+
if (!tile.contentUrl) {
|
|
9
|
+
return TILE_TYPE.EMPTY;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const contentUrl = tile.contentUrl;
|
|
13
|
+
const fileExtension = contentUrl.split('.').pop();
|
|
14
|
+
switch (fileExtension) {
|
|
15
|
+
case 'pnts':
|
|
16
|
+
return TILE_TYPE.POINTCLOUD;
|
|
17
|
+
case 'i3dm':
|
|
18
|
+
case 'b3dm':
|
|
19
|
+
case 'glb':
|
|
20
|
+
case 'gltf':
|
|
21
|
+
return TILE_TYPE.SCENEGRAPH;
|
|
22
|
+
default:
|
|
23
|
+
return fileExtension;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function getRefine(refine) {
|
|
28
|
+
switch (refine) {
|
|
29
|
+
case 'REPLACE':
|
|
30
|
+
case 'replace':
|
|
31
|
+
return TILE_REFINEMENT.REPLACE;
|
|
32
|
+
case 'ADD':
|
|
33
|
+
case 'add':
|
|
34
|
+
return TILE_REFINEMENT.ADD;
|
|
35
|
+
default:
|
|
36
|
+
return refine;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function normalizeTileData(tile, options) {
|
|
41
|
+
if (!tile) {
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (tile.content) {
|
|
45
|
+
const contentUri = tile.content.uri || tile.content.url;
|
|
46
|
+
tile.contentUrl = `${options.basePath}/${contentUri}`;
|
|
47
|
+
}
|
|
48
|
+
tile.id = tile.contentUrl;
|
|
49
|
+
tile.lodMetricType = LOD_METRIC_TYPE.GEOMETRIC_ERROR;
|
|
50
|
+
tile.lodMetricValue = tile.geometricError;
|
|
51
|
+
tile.transformMatrix = tile.transform;
|
|
52
|
+
tile.type = getTileType(tile);
|
|
53
|
+
tile.refine = getRefine(tile.refine);
|
|
54
|
+
|
|
55
|
+
return tile;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// normalize tile headers
|
|
59
|
+
export function normalizeTileHeaders(tileset) {
|
|
60
|
+
const basePath = tileset.basePath;
|
|
61
|
+
const root = normalizeTileData(tileset.root, tileset);
|
|
62
|
+
|
|
63
|
+
const stack: any[] = [];
|
|
64
|
+
stack.push(root);
|
|
65
|
+
|
|
66
|
+
while (stack.length > 0) {
|
|
67
|
+
const tile = stack.pop() || {};
|
|
68
|
+
const children = tile.children || [];
|
|
69
|
+
for (const childHeader of children) {
|
|
70
|
+
normalizeTileData(childHeader, {basePath});
|
|
71
|
+
stack.push(childHeader);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return root;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Do normalisation of implicit tile headers
|
|
80
|
+
* TODO Check if Tile3D class can be a return type here.
|
|
81
|
+
* @param tileset
|
|
82
|
+
*/
|
|
83
|
+
export async function normalizeImplicitTileHeaders(tileset: Tileset3D) {
|
|
84
|
+
if (!tileset.root) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const basePath = tileset.basePath;
|
|
89
|
+
const implicitTilingExtension = tileset.root.extensions['3DTILES_implicit_tiling'];
|
|
90
|
+
const {
|
|
91
|
+
subdivisionScheme,
|
|
92
|
+
maximumLevel,
|
|
93
|
+
subtreeLevels,
|
|
94
|
+
subtrees: {uri: subtreesUriTemplate}
|
|
95
|
+
} = implicitTilingExtension;
|
|
96
|
+
const subtreeUrl = replaceContentUrlTemplate(subtreesUriTemplate, 0, 0, 0, 0);
|
|
97
|
+
const rootSubtreeUrl = `${basePath}/${subtreeUrl}`;
|
|
98
|
+
const rootSubtree = await load(rootSubtreeUrl, Tile3DSubtreeLoader);
|
|
99
|
+
const contentUrlTemplate = `${basePath}/${tileset.root.content.uri}`;
|
|
100
|
+
const refine = tileset.root.refine;
|
|
101
|
+
// @ts-ignore
|
|
102
|
+
const rootLodMetricValue = tileset.root.geometricError;
|
|
103
|
+
const rootBoundingVolume = tileset.root.boundingVolume;
|
|
104
|
+
|
|
105
|
+
const options = {
|
|
106
|
+
contentUrlTemplate,
|
|
107
|
+
subtreesUriTemplate,
|
|
108
|
+
subdivisionScheme,
|
|
109
|
+
subtreeLevels,
|
|
110
|
+
maximumLevel,
|
|
111
|
+
refine,
|
|
112
|
+
basePath,
|
|
113
|
+
lodMetricType: LOD_METRIC_TYPE.GEOMETRIC_ERROR,
|
|
114
|
+
rootLodMetricValue,
|
|
115
|
+
rootBoundingVolume,
|
|
116
|
+
getTileType,
|
|
117
|
+
getRefine
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
return await normalizeImplicitTileData(tileset.root, rootSubtree, options);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Do implicit data normalisation to create hierarchical tile structure
|
|
125
|
+
* @param tile
|
|
126
|
+
* @param rootSubtree
|
|
127
|
+
* @param options
|
|
128
|
+
* @returns
|
|
129
|
+
*/
|
|
130
|
+
export async function normalizeImplicitTileData(tile, rootSubtree: Subtree, options: any) {
|
|
131
|
+
if (!tile) {
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
tile.lodMetricType = LOD_METRIC_TYPE.GEOMETRIC_ERROR;
|
|
136
|
+
tile.lodMetricValue = tile.geometricError;
|
|
137
|
+
tile.transformMatrix = tile.transform;
|
|
138
|
+
|
|
139
|
+
const {children, contentUrl} = await parseImplicitTiles({subtree: rootSubtree, options});
|
|
140
|
+
|
|
141
|
+
if (contentUrl) {
|
|
142
|
+
tile.contentUrl = contentUrl;
|
|
143
|
+
tile.content = {uri: contentUrl.replace(`${options.basePath}/`, '')};
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
tile.refine = getRefine(tile.refine);
|
|
147
|
+
tile.type = getTileType(tile);
|
|
148
|
+
tile.children = children;
|
|
149
|
+
tile.id = tile.contentUrl;
|
|
150
|
+
|
|
151
|
+
return tile;
|
|
152
|
+
}
|
package/src/lib/parsers/{parse-3d-tile-instanced-model.js → parse-3d-tile-instanced-model.ts}
RENAMED
|
@@ -146,6 +146,7 @@ function extractInstancedAttributes(tile, featureTable, batchTable, instancesLen
|
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
instancePosition.copy(position);
|
|
149
|
+
// @ts-expect-error
|
|
149
150
|
instanceTranslationRotationScale.translation = instancePosition;
|
|
150
151
|
|
|
151
152
|
// Get the instance rotation
|
|
@@ -203,6 +204,7 @@ function extractInstancedAttributes(tile, featureTable, batchTable, instancesLen
|
|
|
203
204
|
}
|
|
204
205
|
|
|
205
206
|
instanceQuaternion.fromMatrix3(instanceRotation);
|
|
207
|
+
// @ts-expect-error
|
|
206
208
|
instanceTranslationRotationScale.rotation = instanceQuaternion;
|
|
207
209
|
|
|
208
210
|
// Get the instance scale
|
|
@@ -216,6 +218,7 @@ function extractInstancedAttributes(tile, featureTable, batchTable, instancesLen
|
|
|
216
218
|
instanceScale.scale(nonUniformScale);
|
|
217
219
|
}
|
|
218
220
|
|
|
221
|
+
// @ts-expect-error
|
|
219
222
|
instanceTranslationRotationScale.scale = instanceScale;
|
|
220
223
|
|
|
221
224
|
// Get the batchId
|
|
@@ -225,12 +228,15 @@ function extractInstancedAttributes(tile, featureTable, batchTable, instancesLen
|
|
|
225
228
|
batchId = i;
|
|
226
229
|
}
|
|
227
230
|
|
|
231
|
+
// @ts-expect-error
|
|
228
232
|
const rotationMatrix = new Matrix4().fromQuaternion(instanceTranslationRotationScale.rotation);
|
|
229
233
|
|
|
230
234
|
// Create the model matrix and the instance
|
|
231
235
|
instanceTransform.identity();
|
|
236
|
+
// @ts-expect-error
|
|
232
237
|
instanceTransform.translate(instanceTranslationRotationScale.translation);
|
|
233
238
|
instanceTransform.multiplyRight(rotationMatrix);
|
|
239
|
+
// @ts-expect-error
|
|
234
240
|
instanceTransform.scale(instanceTranslationRotationScale.scale);
|
|
235
241
|
|
|
236
242
|
const modelMatrix = instanceTransform.clone();
|
|
@@ -137,7 +137,7 @@ function parseNormals(tile, featureTable) {
|
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
function parseBatchIds(tile, featureTable) {
|
|
140
|
-
let batchTable = null;
|
|
140
|
+
let batchTable: Tile3DBatchTable | null = null;
|
|
141
141
|
if (!tile.batchIds && featureTable.hasProperty('BATCH_ID')) {
|
|
142
142
|
tile.batchIds = featureTable.getPropertyArray('BATCH_ID', GL.UNSIGNED_SHORT, 1);
|
|
143
143
|
|
|
@@ -250,7 +250,7 @@ export async function loadDraco(tile, dracoData, options, context) {
|
|
|
250
250
|
|
|
251
251
|
tile.attributes = {
|
|
252
252
|
positions: decodedPositions,
|
|
253
|
-
colors: normalize3DTileColorAttribute(tile, decodedColors),
|
|
253
|
+
colors: normalize3DTileColorAttribute(tile, decodedColors, undefined),
|
|
254
254
|
normals: decodedNormals,
|
|
255
255
|
batchIds: decodedBatchIds,
|
|
256
256
|
...batchTableAttributes
|