@loaders.gl/tile-converter 3.3.0-alpha.8 → 3.3.0
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/3d-tiles-attributes-worker.js +2 -2
- package/dist/3d-tiles-attributes-worker.js.map +3 -3
- package/dist/converter-cli.js +14 -2
- package/dist/converter.min.js +22 -22
- package/dist/deps-installer/deps-installer.d.ts.map +1 -1
- package/dist/deps-installer/deps-installer.js +8 -0
- package/dist/dist.min.js +1165 -846
- package/dist/es5/3d-tiles-attributes-worker.js +1 -1
- package/dist/es5/3d-tiles-attributes-worker.js.map +1 -1
- package/dist/es5/converter-cli.js +14 -2
- package/dist/es5/converter-cli.js.map +1 -1
- package/dist/es5/deps-installer/deps-installer.js +13 -2
- package/dist/es5/deps-installer/deps-installer.js.map +1 -1
- package/dist/es5/i3s-attributes-worker.js +1 -1
- package/dist/es5/i3s-attributes-worker.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/geometry-attributes.js +16 -7
- package/dist/es5/i3s-converter/helpers/geometry-attributes.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/geometry-converter.js +363 -113
- package/dist/es5/i3s-converter/helpers/geometry-converter.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/gltf-attributes.js +6 -11
- package/dist/es5/i3s-converter/helpers/gltf-attributes.js.map +1 -1
- package/dist/es5/i3s-converter/helpers/node-index-document.js +517 -0
- package/dist/es5/i3s-converter/helpers/node-index-document.js.map +1 -0
- package/dist/es5/i3s-converter/helpers/node-pages.js +455 -173
- package/dist/es5/i3s-converter/helpers/node-pages.js.map +1 -1
- package/dist/es5/i3s-converter/i3s-converter.js +549 -618
- package/dist/es5/i3s-converter/i3s-converter.js.map +1 -1
- package/dist/es5/i3s-converter/json-templates/geometry-definitions.js +107 -0
- package/dist/es5/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
- package/dist/es5/i3s-converter/json-templates/layers.js +2 -93
- package/dist/es5/i3s-converter/json-templates/layers.js.map +1 -1
- package/dist/es5/i3s-converter/json-templates/shared-resources.js +3 -3
- package/dist/es5/i3s-converter/json-templates/shared-resources.js.map +1 -1
- package/dist/es5/i3s-converter/types.js.map +1 -1
- package/dist/es5/lib/utils/file-utils.js +93 -9
- package/dist/es5/lib/utils/file-utils.js.map +1 -1
- package/dist/es5/lib/utils/write-queue.js +38 -25
- package/dist/es5/lib/utils/write-queue.js.map +1 -1
- package/dist/es5/pgm-loader.js +1 -1
- package/dist/es5/pgm-loader.js.map +1 -1
- package/dist/es5/workers/i3s-attributes-worker.js +1 -1
- package/dist/es5/workers/i3s-attributes-worker.js.map +1 -1
- package/dist/esm/3d-tiles-attributes-worker.js +1 -1
- package/dist/esm/3d-tiles-attributes-worker.js.map +1 -1
- package/dist/esm/converter-cli.js +14 -2
- package/dist/esm/converter-cli.js.map +1 -1
- package/dist/esm/deps-installer/deps-installer.js +9 -1
- package/dist/esm/deps-installer/deps-installer.js.map +1 -1
- package/dist/esm/i3s-attributes-worker.js +1 -1
- package/dist/esm/i3s-attributes-worker.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/batch-ids-extensions.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/geometry-attributes.js +16 -7
- package/dist/esm/i3s-converter/helpers/geometry-attributes.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/geometry-converter.js +150 -40
- package/dist/esm/i3s-converter/helpers/geometry-converter.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/gltf-attributes.js +6 -9
- package/dist/esm/i3s-converter/helpers/gltf-attributes.js.map +1 -1
- package/dist/esm/i3s-converter/helpers/node-index-document.js +202 -0
- package/dist/esm/i3s-converter/helpers/node-index-document.js.map +1 -0
- package/dist/esm/i3s-converter/helpers/node-pages.js +162 -76
- package/dist/esm/i3s-converter/helpers/node-pages.js.map +1 -1
- package/dist/esm/i3s-converter/i3s-converter.js +115 -220
- package/dist/esm/i3s-converter/i3s-converter.js.map +1 -1
- package/dist/esm/i3s-converter/json-templates/geometry-definitions.js +89 -0
- package/dist/esm/i3s-converter/json-templates/geometry-definitions.js.map +1 -0
- package/dist/esm/i3s-converter/json-templates/layers.js +2 -85
- package/dist/esm/i3s-converter/json-templates/layers.js.map +1 -1
- package/dist/esm/i3s-converter/json-templates/shared-resources.js +3 -3
- package/dist/esm/i3s-converter/json-templates/shared-resources.js.map +1 -1
- package/dist/esm/i3s-converter/types.js.map +1 -1
- package/dist/esm/lib/utils/file-utils.js +44 -3
- package/dist/esm/lib/utils/file-utils.js.map +1 -1
- package/dist/esm/lib/utils/write-queue.js +19 -10
- package/dist/esm/lib/utils/write-queue.js.map +1 -1
- package/dist/esm/pgm-loader.js +1 -1
- package/dist/esm/pgm-loader.js.map +1 -1
- package/dist/esm/workers/i3s-attributes-worker.js +1 -1
- package/dist/esm/workers/i3s-attributes-worker.js.map +1 -1
- package/dist/i3s-attributes-worker.js +2 -2
- package/dist/i3s-attributes-worker.js.map +2 -2
- package/dist/i3s-converter/helpers/batch-ids-extensions.d.ts +3 -3
- package/dist/i3s-converter/helpers/batch-ids-extensions.js +3 -3
- package/dist/i3s-converter/helpers/geometry-attributes.d.ts.map +1 -1
- package/dist/i3s-converter/helpers/geometry-attributes.js +16 -10
- package/dist/i3s-converter/helpers/geometry-converter.d.ts +8 -4
- package/dist/i3s-converter/helpers/geometry-converter.d.ts.map +1 -1
- package/dist/i3s-converter/helpers/geometry-converter.js +200 -44
- package/dist/i3s-converter/helpers/gltf-attributes.d.ts.map +1 -1
- package/dist/i3s-converter/helpers/gltf-attributes.js +2 -3
- package/dist/i3s-converter/helpers/node-index-document.d.ts +95 -0
- package/dist/i3s-converter/helpers/node-index-document.d.ts.map +1 -0
- package/dist/i3s-converter/helpers/node-index-document.js +250 -0
- package/dist/i3s-converter/helpers/node-pages.d.ts +78 -43
- package/dist/i3s-converter/helpers/node-pages.d.ts.map +1 -1
- package/dist/i3s-converter/helpers/node-pages.js +195 -94
- package/dist/i3s-converter/i3s-converter.d.ts +33 -58
- package/dist/i3s-converter/i3s-converter.d.ts.map +1 -1
- package/dist/i3s-converter/i3s-converter.js +122 -233
- package/dist/i3s-converter/json-templates/geometry-definitions.d.ts +7 -0
- package/dist/i3s-converter/json-templates/geometry-definitions.d.ts.map +1 -0
- package/dist/i3s-converter/json-templates/geometry-definitions.js +87 -0
- package/dist/i3s-converter/json-templates/layers.d.ts +1 -30
- package/dist/i3s-converter/json-templates/layers.d.ts.map +1 -1
- package/dist/i3s-converter/json-templates/layers.js +2 -86
- package/dist/i3s-converter/json-templates/shared-resources.js +3 -3
- package/dist/i3s-converter/types.d.ts +28 -2
- package/dist/i3s-converter/types.d.ts.map +1 -1
- package/dist/lib/utils/file-utils.d.ts +17 -1
- package/dist/lib/utils/file-utils.d.ts.map +1 -1
- package/dist/lib/utils/file-utils.js +64 -7
- package/dist/lib/utils/write-queue.d.ts +18 -2
- package/dist/lib/utils/write-queue.d.ts.map +1 -1
- package/dist/lib/utils/write-queue.js +18 -12
- package/dist/workers/i3s-attributes-worker.js +1 -1
- package/package.json +25 -20
- package/src/converter-cli.ts +22 -2
- package/src/deps-installer/deps-installer.ts +9 -0
- package/src/i3s-converter/helpers/batch-ids-extensions.ts +3 -3
- package/src/i3s-converter/helpers/geometry-attributes.ts +16 -11
- package/src/i3s-converter/helpers/geometry-converter.ts +217 -48
- package/src/i3s-converter/helpers/gltf-attributes.ts +2 -3
- package/src/i3s-converter/helpers/node-index-document.ts +315 -0
- package/src/i3s-converter/helpers/node-pages.ts +215 -110
- package/src/i3s-converter/i3s-converter.ts +170 -312
- package/src/i3s-converter/json-templates/geometry-definitions.ts +83 -0
- package/src/i3s-converter/json-templates/layers.ts +2 -91
- package/src/i3s-converter/json-templates/shared-resources.ts +3 -3
- package/src/i3s-converter/types.ts +29 -2
- package/src/lib/utils/file-utils.ts +62 -7
- package/src/lib/utils/write-queue.ts +36 -15
- package/src/workers/i3s-attributes-worker.ts +2 -1
|
@@ -17,12 +17,14 @@ import {DracoWriterWorker} from '@loaders.gl/draco';
|
|
|
17
17
|
import {assert, encode} from '@loaders.gl/core';
|
|
18
18
|
import {concatenateArrayBuffers, concatenateTypedArrays} from '@loaders.gl/loader-utils';
|
|
19
19
|
import md5 from 'md5';
|
|
20
|
+
import {v4 as uuidv4} from 'uuid';
|
|
20
21
|
import {generateAttributes} from './geometry-attributes';
|
|
21
22
|
import {createBoundingVolumesFromGeometry} from './coordinate-converter';
|
|
22
23
|
import {
|
|
23
24
|
ConvertedAttributes,
|
|
24
25
|
I3SConvertedResources,
|
|
25
26
|
I3SMaterialWithTexture,
|
|
27
|
+
MergedMaterial,
|
|
26
28
|
SharedResourcesArrays
|
|
27
29
|
} from '../types';
|
|
28
30
|
import {
|
|
@@ -68,33 +70,38 @@ let scratchVector = new Vector3();
|
|
|
68
70
|
*
|
|
69
71
|
* @param tileContent - 3d tile content
|
|
70
72
|
* @param addNodeToNodePage - function to add new node to node pages
|
|
73
|
+
* @param propertyTable - batch table (corresponding to feature attributes data)
|
|
71
74
|
* @param featuresHashArray - hash array of features that is needed to not to mix up same features in parent and child nodes
|
|
72
75
|
* @param attributeStorageInfo - attributes metadata from 3DSceneLayer json
|
|
73
76
|
* @param draco - is converter should create draco compressed geometry
|
|
74
77
|
* @param generateBoundingVolumes - is converter should create accurate bounding voulmes from geometry attributes
|
|
78
|
+
* @param shouldMergeMaterials - Try to merge similar materials to be able to merge meshes into one node
|
|
75
79
|
* @param geoidHeightModel - model to convert elevation from elipsoidal to geoid
|
|
80
|
+
* @param workerSource - source code of used workers
|
|
76
81
|
* @returns Array of node resources to create one or more i3s nodes
|
|
77
82
|
*/
|
|
78
83
|
export default async function convertB3dmToI3sGeometry(
|
|
79
84
|
tileContent: B3DMContent,
|
|
80
|
-
addNodeToNodePage: () => number
|
|
85
|
+
addNodeToNodePage: () => Promise<number>,
|
|
81
86
|
propertyTable: FeatureTableJson | null,
|
|
82
87
|
featuresHashArray: string[],
|
|
83
88
|
attributeStorageInfo: AttributeStorageInfo[] | undefined,
|
|
84
89
|
draco: boolean,
|
|
85
90
|
generateBoundingVolumes: boolean,
|
|
91
|
+
shouldMergeMaterials: boolean,
|
|
86
92
|
geoidHeightModel: Geoid,
|
|
87
93
|
workerSource: {[key: string]: string}
|
|
88
94
|
): Promise<I3SConvertedResources[] | null> {
|
|
89
95
|
const useCartesianPositions = generateBoundingVolumes;
|
|
90
|
-
const materialAndTextureList: I3SMaterialWithTexture[] = convertMaterials(
|
|
91
|
-
tileContent.gltf?.materials
|
|
96
|
+
const materialAndTextureList: I3SMaterialWithTexture[] = await convertMaterials(
|
|
97
|
+
tileContent.gltf?.materials,
|
|
98
|
+
shouldMergeMaterials
|
|
92
99
|
);
|
|
93
100
|
|
|
94
101
|
const dataForAttributesConversion = prepareDataForAttributesConversion(tileContent);
|
|
95
|
-
|
|
96
102
|
const convertedAttributesMap: Map<string, ConvertedAttributes> = await convertAttributes(
|
|
97
103
|
dataForAttributesConversion,
|
|
104
|
+
materialAndTextureList,
|
|
98
105
|
useCartesianPositions
|
|
99
106
|
);
|
|
100
107
|
/** Usage of worker here brings more overhead than advantage */
|
|
@@ -110,28 +117,18 @@ export default async function convertB3dmToI3sGeometry(
|
|
|
110
117
|
_generateBoundingVolumesFromGeometry(convertedAttributesMap, geoidHeightModel);
|
|
111
118
|
}
|
|
112
119
|
|
|
113
|
-
if (convertedAttributesMap.has('default')) {
|
|
114
|
-
materialAndTextureList.push({
|
|
115
|
-
material: getDefaultMaterial()
|
|
116
|
-
});
|
|
117
|
-
}
|
|
118
|
-
|
|
119
120
|
const result: I3SConvertedResources[] = [];
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
}
|
|
124
|
-
for (let i = 0; i < materials.length; i++) {
|
|
125
|
-
const sourceMaterial = materials[i];
|
|
126
|
-
if (!convertedAttributesMap.has(sourceMaterial.id)) {
|
|
121
|
+
for (const materialAndTexture of materialAndTextureList) {
|
|
122
|
+
const originarMaterialId = materialAndTexture.mergedMaterials[0].originalMaterialId;
|
|
123
|
+
if (!convertedAttributesMap.has(originarMaterialId)) {
|
|
127
124
|
continue; // eslint-disable-line no-continue
|
|
128
125
|
}
|
|
129
|
-
const convertedAttributes = convertedAttributesMap.get(
|
|
126
|
+
const convertedAttributes = convertedAttributesMap.get(originarMaterialId);
|
|
130
127
|
if (!convertedAttributes) {
|
|
131
128
|
continue;
|
|
132
129
|
}
|
|
133
|
-
const {material, texture} =
|
|
134
|
-
const nodeId = addNodeToNodePage();
|
|
130
|
+
const {material, texture} = materialAndTexture;
|
|
131
|
+
const nodeId = await addNodeToNodePage();
|
|
135
132
|
result.push(
|
|
136
133
|
await _makeNodeResources({
|
|
137
134
|
convertedAttributes,
|
|
@@ -192,8 +189,10 @@ function _generateBoundingVolumesFromGeometry(
|
|
|
192
189
|
* @param params.tileContent - B3DM decoded content
|
|
193
190
|
* @param params.nodeId - new node ID
|
|
194
191
|
* @param params.featuresHashArray - hash array of features that is needed to not to mix up same features in parent and child nodes
|
|
195
|
-
* @param params.
|
|
192
|
+
* @param params.propertyTable - batch table (corresponding to feature attributes data)
|
|
193
|
+
* @param params.attributeStorageInfo - attributes metadata from 3DSceneLayer json
|
|
196
194
|
* @param params.draco - is converter should create draco compressed geometry
|
|
195
|
+
* @param params.workerSource - source code of used workers
|
|
197
196
|
* @returns Array of I3S node resources
|
|
198
197
|
*/
|
|
199
198
|
async function _makeNodeResources({
|
|
@@ -221,7 +220,7 @@ async function _makeNodeResources({
|
|
|
221
220
|
}): Promise<I3SConvertedResources> {
|
|
222
221
|
const boundingVolumes = convertedAttributes.boundingVolumes;
|
|
223
222
|
const vertexCount = convertedAttributes.positions.length / VALUES_PER_VERTEX;
|
|
224
|
-
const {faceRange, featureIds, positions, normals, colors, texCoords, featureCount} =
|
|
223
|
+
const {faceRange, featureIds, positions, normals, colors, uvRegions, texCoords, featureCount} =
|
|
225
224
|
generateAttributes(convertedAttributes);
|
|
226
225
|
|
|
227
226
|
if (tileContent.batchTableJson) {
|
|
@@ -244,6 +243,7 @@ async function _makeNodeResources({
|
|
|
244
243
|
normals.buffer,
|
|
245
244
|
texture ? texCoords.buffer : new ArrayBuffer(0),
|
|
246
245
|
colors.buffer,
|
|
246
|
+
uvRegions,
|
|
247
247
|
typedFeatureIds.buffer,
|
|
248
248
|
faceRange.buffer
|
|
249
249
|
)
|
|
@@ -257,6 +257,7 @@ async function _makeNodeResources({
|
|
|
257
257
|
normals,
|
|
258
258
|
texCoords: texture ? texCoords : new Float32Array(0),
|
|
259
259
|
colors,
|
|
260
|
+
uvRegions,
|
|
260
261
|
featureIds,
|
|
261
262
|
faceRange
|
|
262
263
|
},
|
|
@@ -279,6 +280,7 @@ async function _makeNodeResources({
|
|
|
279
280
|
geometry: fileBuffer,
|
|
280
281
|
compressedGeometry,
|
|
281
282
|
texture,
|
|
283
|
+
hasUvRegions: Boolean(uvRegions.length),
|
|
282
284
|
sharedResources: getSharedResources(tileContent.gltf?.materials || [], nodeId),
|
|
283
285
|
meshMaterial: material,
|
|
284
286
|
vertexCount,
|
|
@@ -290,28 +292,35 @@ async function _makeNodeResources({
|
|
|
290
292
|
|
|
291
293
|
/**
|
|
292
294
|
* Convert attributes from the gltf nodes tree to i3s plain geometry
|
|
293
|
-
* @param
|
|
295
|
+
* @param attributesData - geometry attributes from gltf
|
|
296
|
+
* @param materialAndTextureList - array of data about materials and textures of the content
|
|
294
297
|
* @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
|
|
295
298
|
* Cartesian coordinates will be required for creating bounding voulmest from geometry positions
|
|
296
299
|
* @returns map of converted geometry attributes
|
|
297
300
|
*/
|
|
298
301
|
export async function convertAttributes(
|
|
299
302
|
attributesData: B3DMAttributesData,
|
|
303
|
+
materialAndTextureList: I3SMaterialWithTexture[],
|
|
300
304
|
useCartesianPositions: boolean
|
|
301
305
|
): Promise<Map<string, ConvertedAttributes>> {
|
|
302
|
-
const {
|
|
306
|
+
const {nodes, images, cartographicOrigin, cartesianModelMatrix} = attributesData;
|
|
303
307
|
const attributesMap = new Map<string, ConvertedAttributes>();
|
|
304
308
|
|
|
305
|
-
for (const
|
|
306
|
-
|
|
309
|
+
for (const materialAndTexture of materialAndTextureList) {
|
|
310
|
+
const attributes = {
|
|
307
311
|
positions: new Float32Array(0),
|
|
308
312
|
normals: new Float32Array(0),
|
|
309
313
|
texCoords: new Float32Array(0),
|
|
310
314
|
colors: new Uint8Array(0),
|
|
315
|
+
uvRegions: new Uint16Array(0),
|
|
311
316
|
featureIndicesGroups: [],
|
|
312
317
|
featureIndices: [],
|
|
313
|
-
boundingVolumes: null
|
|
314
|
-
|
|
318
|
+
boundingVolumes: null,
|
|
319
|
+
mergedMaterials: materialAndTexture.mergedMaterials
|
|
320
|
+
};
|
|
321
|
+
for (const mergedMaterial of materialAndTexture.mergedMaterials) {
|
|
322
|
+
attributesMap.set(mergedMaterial.originalMaterialId, attributes);
|
|
323
|
+
}
|
|
315
324
|
}
|
|
316
325
|
|
|
317
326
|
convertNodes(
|
|
@@ -348,7 +357,8 @@ export async function convertAttributes(
|
|
|
348
357
|
* The goal is applying tranformation matrix for all children. Functions "convertNodes" and "convertNode" work together recursively.
|
|
349
358
|
* @param nodes - gltf nodes array
|
|
350
359
|
* @param images - gltf images array
|
|
351
|
-
* @param
|
|
360
|
+
* @param cartographicOrigin - cartographic origin of bounding volume
|
|
361
|
+
* @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
|
|
352
362
|
* @param attributesMap - for recursive concatenation of attributes
|
|
353
363
|
* @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
|
|
354
364
|
* Cartesian coordinates will be required for creating bounding voulmest from geometry positions
|
|
@@ -385,7 +395,7 @@ function convertNodes(
|
|
|
385
395
|
* @param node
|
|
386
396
|
* @param matrix
|
|
387
397
|
*/
|
|
388
|
-
function getCompositeTransformationMatrix(node, matrix) {
|
|
398
|
+
function getCompositeTransformationMatrix(node: GLTFNodePostprocessed, matrix: Matrix4) {
|
|
389
399
|
let transformationMatrix = matrix;
|
|
390
400
|
|
|
391
401
|
const {matrix: nodeMatrix, rotation, scale, translation} = node;
|
|
@@ -413,7 +423,8 @@ function getCompositeTransformationMatrix(node, matrix) {
|
|
|
413
423
|
* Convert all primitives of node and all children nodes
|
|
414
424
|
* @param node - gltf node
|
|
415
425
|
* @param images - gltf images array
|
|
416
|
-
* @param
|
|
426
|
+
* @param cartographicOrigin - cartographic origin of bounding volume
|
|
427
|
+
* @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
|
|
417
428
|
* @param {Map} attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
|
|
418
429
|
* attributes
|
|
419
430
|
* @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
|
|
@@ -457,9 +468,13 @@ function convertNode(
|
|
|
457
468
|
}
|
|
458
469
|
|
|
459
470
|
/**
|
|
460
|
-
* Convert all primitives of
|
|
461
|
-
* @param mesh - gltf
|
|
462
|
-
* @param
|
|
471
|
+
* Convert all primitives of the mesh
|
|
472
|
+
* @param mesh - gltf mesh data
|
|
473
|
+
* @param images - gltf images array
|
|
474
|
+
* @param cartographicOrigin - cartographic origin of bounding volume
|
|
475
|
+
* @param cartesianModelMatrix - cartesian model matrix to convert coordinates to cartographic
|
|
476
|
+
* @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
|
|
477
|
+
* attributes
|
|
463
478
|
* @param useCartesianPositions - convert positions to absolute cartesian coordinates instead of cartographic offsets.
|
|
464
479
|
* Cartesian coordinates will be required for creating bounding voulmest from geometry positions
|
|
465
480
|
* @param attributesMap Map<{positions: Float32Array, normals: Float32Array, texCoords: Float32Array, colors: Uint8Array, featureIndices: Array}> - for recursive concatenation of
|
|
@@ -478,8 +493,12 @@ function convertMesh(
|
|
|
478
493
|
) {
|
|
479
494
|
for (const primitive of mesh.primitives) {
|
|
480
495
|
let outputAttributes: ConvertedAttributes | null | undefined = null;
|
|
496
|
+
let materialUvRegion: Uint16Array | undefined;
|
|
481
497
|
if (primitive.material) {
|
|
482
|
-
outputAttributes = attributesMap.get(primitive.material.
|
|
498
|
+
outputAttributes = attributesMap.get(primitive.material.uniqueId);
|
|
499
|
+
materialUvRegion = outputAttributes?.mergedMaterials.find(
|
|
500
|
+
({originalMaterialId}) => originalMaterialId === primitive.material?.uniqueId
|
|
501
|
+
)?.uvRegion;
|
|
483
502
|
} else if (attributesMap.has('default')) {
|
|
484
503
|
outputAttributes = attributesMap.get('default');
|
|
485
504
|
}
|
|
@@ -525,6 +544,13 @@ function convertMesh(
|
|
|
525
544
|
flattenColors(attributes.COLOR_0, primitive.indices?.value)
|
|
526
545
|
);
|
|
527
546
|
|
|
547
|
+
if (materialUvRegion) {
|
|
548
|
+
outputAttributes.uvRegions = concatenateTypedArrays(
|
|
549
|
+
outputAttributes.uvRegions,
|
|
550
|
+
createUvRegion(materialUvRegion, primitive.indices?.value)
|
|
551
|
+
);
|
|
552
|
+
}
|
|
553
|
+
|
|
528
554
|
outputAttributes.featureIndicesGroups = outputAttributes.featureIndicesGroups || [];
|
|
529
555
|
outputAttributes.featureIndicesGroups.push(
|
|
530
556
|
flattenBatchIds(getBatchIds(attributes, primitive, images), primitive.indices?.value)
|
|
@@ -675,6 +701,20 @@ function flattenColors(
|
|
|
675
701
|
return newColors;
|
|
676
702
|
}
|
|
677
703
|
|
|
704
|
+
/**
|
|
705
|
+
* Create per-vertex uv-region array
|
|
706
|
+
* @param materialUvRegion - uv-region fragment for a single vertex
|
|
707
|
+
* @param indices - geometry indices data
|
|
708
|
+
* @returns - uv-region array
|
|
709
|
+
*/
|
|
710
|
+
function createUvRegion(materialUvRegion: Uint16Array, indices: Uint8Array): Uint16Array {
|
|
711
|
+
const result = new Uint16Array(indices.length * 4);
|
|
712
|
+
for (let i = 0; i < result.length; i += 4) {
|
|
713
|
+
result.set(materialUvRegion, i);
|
|
714
|
+
}
|
|
715
|
+
return result;
|
|
716
|
+
}
|
|
717
|
+
|
|
678
718
|
/**
|
|
679
719
|
* Flatten batchedIds list based on indices to right ordered array, compatible with i3s
|
|
680
720
|
* @param batchedIds - gltf primitive
|
|
@@ -695,9 +735,9 @@ function flattenBatchIds(batchedIds: number[], indices: Uint8Array): number[] {
|
|
|
695
735
|
|
|
696
736
|
/**
|
|
697
737
|
* Get batchIds for featureIds creation
|
|
698
|
-
* @param attributes
|
|
699
|
-
* @param primitive
|
|
700
|
-
* @param
|
|
738
|
+
* @param attributes - gltf accessors
|
|
739
|
+
* @param primitive - gltf primitive data
|
|
740
|
+
* @param images - gltf texture images
|
|
701
741
|
*/
|
|
702
742
|
function getBatchIds(
|
|
703
743
|
attributes: {
|
|
@@ -728,18 +768,134 @@ function getBatchIds(
|
|
|
728
768
|
/**
|
|
729
769
|
* Convert GLTF material to I3S material definitions and textures
|
|
730
770
|
* @param sourceMaterials Source GLTF materials
|
|
771
|
+
* @param shouldMergeMaterials - if true - the converter will try to merge similar materials
|
|
772
|
+
* to be able to merge primitives having those materials
|
|
731
773
|
* @returns Array of Couples I3SMaterialDefinition + texture content
|
|
732
774
|
*/
|
|
733
|
-
function convertMaterials(
|
|
734
|
-
sourceMaterials: GLTFMaterialPostprocessed[] = []
|
|
735
|
-
|
|
736
|
-
|
|
775
|
+
async function convertMaterials(
|
|
776
|
+
sourceMaterials: GLTFMaterialPostprocessed[] = [],
|
|
777
|
+
shouldMergeMaterials: boolean
|
|
778
|
+
): Promise<I3SMaterialWithTexture[]> {
|
|
779
|
+
let materials: I3SMaterialWithTexture[] = [];
|
|
737
780
|
for (const sourceMaterial of sourceMaterials) {
|
|
738
|
-
|
|
781
|
+
materials.push(convertMaterial(sourceMaterial));
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
if (shouldMergeMaterials) {
|
|
785
|
+
materials = await mergeAllMaterials(materials);
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
return materials;
|
|
789
|
+
}
|
|
790
|
+
|
|
791
|
+
/**
|
|
792
|
+
* Merge materials when possible
|
|
793
|
+
* @param materials materials array
|
|
794
|
+
* @returns merged materials array
|
|
795
|
+
*/
|
|
796
|
+
async function mergeAllMaterials(
|
|
797
|
+
materials: I3SMaterialWithTexture[]
|
|
798
|
+
): Promise<I3SMaterialWithTexture[]> {
|
|
799
|
+
const result: I3SMaterialWithTexture[] = [];
|
|
800
|
+
while (materials.length > 0) {
|
|
801
|
+
let newMaterial = materials.splice(0, 1)[0];
|
|
802
|
+
const mergedIndices: number[] = [];
|
|
803
|
+
for (let i = 0; i < materials.length; i++) {
|
|
804
|
+
const material = materials[i];
|
|
805
|
+
if (
|
|
806
|
+
(newMaterial.texture && material.texture) ||
|
|
807
|
+
(!newMaterial.texture && !material.texture)
|
|
808
|
+
) {
|
|
809
|
+
newMaterial = await mergeMaterials(newMaterial, material);
|
|
810
|
+
mergedIndices.push(i);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
if (newMaterial.texture && mergedIndices.length) {
|
|
814
|
+
const newWidth = newMaterial.mergedMaterials?.reduce(
|
|
815
|
+
(accum, {textureSize}) => accum + (textureSize?.width || 0),
|
|
816
|
+
0
|
|
817
|
+
);
|
|
818
|
+
const newHeight = newMaterial.mergedMaterials?.reduce(
|
|
819
|
+
(accum, {textureSize}) => Math.max(accum, textureSize?.height || 0),
|
|
820
|
+
0
|
|
821
|
+
);
|
|
822
|
+
let currentX = -1;
|
|
823
|
+
for (const aTextureMetadata of newMaterial.mergedMaterials) {
|
|
824
|
+
if (aTextureMetadata.textureSize) {
|
|
825
|
+
const newX =
|
|
826
|
+
currentX +
|
|
827
|
+
1 +
|
|
828
|
+
(aTextureMetadata.textureSize.width / newWidth) *
|
|
829
|
+
2 ** (Uint16Array.BYTES_PER_ELEMENT * 8) -
|
|
830
|
+
1;
|
|
831
|
+
aTextureMetadata.uvRegion = new Uint16Array([
|
|
832
|
+
currentX + 1,
|
|
833
|
+
0,
|
|
834
|
+
newX,
|
|
835
|
+
(aTextureMetadata.textureSize.height / newHeight) *
|
|
836
|
+
2 ** (Uint16Array.BYTES_PER_ELEMENT * 8) -
|
|
837
|
+
1
|
|
838
|
+
]);
|
|
839
|
+
currentX = newX;
|
|
840
|
+
}
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
newMaterial.texture.image.width = newWidth;
|
|
844
|
+
newMaterial.texture.image.height = newHeight;
|
|
845
|
+
}
|
|
846
|
+
for (const index of mergedIndices.reverse()) {
|
|
847
|
+
materials.splice(index, 1);
|
|
848
|
+
}
|
|
849
|
+
result.push(newMaterial);
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
if (!result.length) {
|
|
853
|
+
result.push({
|
|
854
|
+
material: getDefaultMaterial(),
|
|
855
|
+
mergedMaterials: [{originalMaterialId: 'default'}]
|
|
856
|
+
});
|
|
739
857
|
}
|
|
740
858
|
return result;
|
|
741
859
|
}
|
|
742
860
|
|
|
861
|
+
/**
|
|
862
|
+
* Merge 2 materials including texture
|
|
863
|
+
* @param material1
|
|
864
|
+
* @param material2
|
|
865
|
+
* @returns
|
|
866
|
+
*/
|
|
867
|
+
async function mergeMaterials(
|
|
868
|
+
material1: I3SMaterialWithTexture,
|
|
869
|
+
material2: I3SMaterialWithTexture
|
|
870
|
+
): Promise<I3SMaterialWithTexture> {
|
|
871
|
+
if (
|
|
872
|
+
material1.texture?.bufferView &&
|
|
873
|
+
material2.texture?.bufferView &&
|
|
874
|
+
material1.mergedMaterials &&
|
|
875
|
+
material2.mergedMaterials
|
|
876
|
+
) {
|
|
877
|
+
const buffer1 = Buffer.from(material1.texture.bufferView.data);
|
|
878
|
+
const buffer2 = Buffer.from(material2.texture.bufferView.data);
|
|
879
|
+
try {
|
|
880
|
+
// @ts-ignore
|
|
881
|
+
const {joinImages} = await import('join-images');
|
|
882
|
+
const sharpData = await joinImages([buffer1, buffer2], {direction: 'horizontal'});
|
|
883
|
+
material1.texture.bufferView.data = await sharpData
|
|
884
|
+
.toFormat(material1.texture.mimeType === 'image/png' ? 'png' : 'jpeg')
|
|
885
|
+
.toBuffer();
|
|
886
|
+
} catch (error) {
|
|
887
|
+
console.log(
|
|
888
|
+
'Join images into a texture atlas has failed. Consider usage `--split-nodes` option. (See documentation https://loaders.gl/modules/tile-converter/docs/cli-reference/tile-converter)'
|
|
889
|
+
);
|
|
890
|
+
throw error;
|
|
891
|
+
}
|
|
892
|
+
// @ts-ignore
|
|
893
|
+
material1.material.pbrMetallicRoughness.baseColorTexture.textureSetDefinitionId = 1;
|
|
894
|
+
}
|
|
895
|
+
material1.mergedMaterials = material1.mergedMaterials.concat(material2.mergedMaterials);
|
|
896
|
+
return material1;
|
|
897
|
+
}
|
|
898
|
+
|
|
743
899
|
/**
|
|
744
900
|
* Convert texture and material from gltf 2.0 material object
|
|
745
901
|
* @param sourceMaterial - material object
|
|
@@ -778,6 +934,9 @@ function convertMaterial(sourceMaterial: GLTFMaterialPostprocessed): I3SMaterial
|
|
|
778
934
|
};
|
|
779
935
|
}
|
|
780
936
|
|
|
937
|
+
const uniqueId = uuidv4();
|
|
938
|
+
sourceMaterial.uniqueId = uniqueId;
|
|
939
|
+
let mergedMaterials: MergedMaterial[] = [{originalMaterialId: uniqueId}];
|
|
781
940
|
if (!texture) {
|
|
782
941
|
// Should use default baseColorFactor if it is not present in source material
|
|
783
942
|
// https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#reference-pbrmetallicroughness
|
|
@@ -789,9 +948,11 @@ function convertMaterial(sourceMaterial: GLTFMaterialPostprocessed): I3SMaterial
|
|
|
789
948
|
number,
|
|
790
949
|
number
|
|
791
950
|
]) || undefined;
|
|
951
|
+
} else {
|
|
952
|
+
mergedMaterials[0].textureSize = {width: texture.image.width, height: texture.image.height};
|
|
792
953
|
}
|
|
793
954
|
|
|
794
|
-
return {material, texture};
|
|
955
|
+
return {material, texture, mergedMaterials};
|
|
795
956
|
}
|
|
796
957
|
|
|
797
958
|
/**
|
|
@@ -968,13 +1129,13 @@ function extractSharedResourcesTextureInfo(
|
|
|
968
1129
|
}
|
|
969
1130
|
|
|
970
1131
|
/**
|
|
971
|
-
* Formula for
|
|
1132
|
+
* Formula for calculating imageId:
|
|
972
1133
|
* https://github.com/Esri/i3s-spec/blob/0a6366a9249b831db8436c322f8d27521e86cf07/format/Indexed%203d%20Scene%20Layer%20Format%20Specification.md#generating-image-ids
|
|
973
1134
|
* @param texture - texture image info
|
|
974
1135
|
* @param nodeId - I3S node ID
|
|
975
1136
|
* @returns calculate image ID according to the spec
|
|
976
1137
|
*/
|
|
977
|
-
function generateImageId(texture: GLTFTexturePostprocessed, nodeId) {
|
|
1138
|
+
function generateImageId(texture: GLTFTexturePostprocessed, nodeId: number) {
|
|
978
1139
|
const {width, height} = texture.source?.image;
|
|
979
1140
|
const levelCountOfTexture = 1;
|
|
980
1141
|
const indexOfLevel = 0;
|
|
@@ -1076,8 +1237,8 @@ function replaceIndicesByUnique(indicesArray, featureMap) {
|
|
|
1076
1237
|
|
|
1077
1238
|
/**
|
|
1078
1239
|
* Convert property table data to attribute buffers.
|
|
1079
|
-
* @param {Object} propertyTable - table with metadata for particular feature.
|
|
1080
1240
|
* @param {Array} featureIds
|
|
1241
|
+
* @param {Object} propertyTable - table with metadata for particular feature.
|
|
1081
1242
|
* @param {Array} attributeStorageInfo
|
|
1082
1243
|
* @returns {Array} - Array of file buffers.
|
|
1083
1244
|
*/
|
|
@@ -1227,7 +1388,7 @@ async function generateCompressedGeometry(
|
|
|
1227
1388
|
attributes,
|
|
1228
1389
|
dracoWorkerSoure
|
|
1229
1390
|
) {
|
|
1230
|
-
const {positions, normals, texCoords, colors, featureIds, faceRange} = attributes;
|
|
1391
|
+
const {positions, normals, texCoords, colors, uvRegions, featureIds, faceRange} = attributes;
|
|
1231
1392
|
const indices = new Uint32Array(vertexCount);
|
|
1232
1393
|
|
|
1233
1394
|
for (let index = 0; index < indices.length; index++) {
|
|
@@ -1246,6 +1407,7 @@ async function generateCompressedGeometry(
|
|
|
1246
1407
|
colors: TypedArray;
|
|
1247
1408
|
'feature-index': TypedArray;
|
|
1248
1409
|
texCoords?: TypedArray;
|
|
1410
|
+
'uv-region'?: TypedArray;
|
|
1249
1411
|
} = {
|
|
1250
1412
|
positions,
|
|
1251
1413
|
normals,
|
|
@@ -1264,6 +1426,13 @@ async function generateCompressedGeometry(
|
|
|
1264
1426
|
}
|
|
1265
1427
|
};
|
|
1266
1428
|
|
|
1429
|
+
if (uvRegions.length) {
|
|
1430
|
+
compressedAttributes['uv-region'] = uvRegions;
|
|
1431
|
+
attributesMetadata['uv-region'] = {
|
|
1432
|
+
'i3s-attribute-type': 'uv-region'
|
|
1433
|
+
};
|
|
1434
|
+
}
|
|
1435
|
+
|
|
1267
1436
|
return encode({attributes: compressedAttributes, indices}, DracoWriterWorker, {
|
|
1268
1437
|
...DracoWriterWorker.options,
|
|
1269
1438
|
source: dracoWorkerSoure,
|
|
@@ -28,7 +28,6 @@ function getB3DMAttributesWithoutBufferView(attributes: AttributesObject): Attri
|
|
|
28
28
|
* @returns
|
|
29
29
|
*/
|
|
30
30
|
export function prepareDataForAttributesConversion(tileContent: B3DMContent): B3DMAttributesData {
|
|
31
|
-
const gltfMaterials = tileContent.gltf?.materials?.map((material) => ({id: material.id}));
|
|
32
31
|
let nodes =
|
|
33
32
|
tileContent.gltf?.scene?.nodes ||
|
|
34
33
|
tileContent.gltf?.scenes?.[0]?.nodes ||
|
|
@@ -64,7 +63,6 @@ export function prepareDataForAttributesConversion(tileContent: B3DMContent): B3
|
|
|
64
63
|
const cartesianModelMatrix = tileContent.cartesianModelMatrix;
|
|
65
64
|
|
|
66
65
|
return {
|
|
67
|
-
gltfMaterials,
|
|
68
66
|
nodes,
|
|
69
67
|
images,
|
|
70
68
|
cartographicOrigin,
|
|
@@ -90,7 +88,8 @@ function prepareNodes(nodes: GLTFNodePostprocessed[]): void {
|
|
|
90
88
|
indices: {value: primitive?.indices?.value},
|
|
91
89
|
attributes: getB3DMAttributesWithoutBufferView(primitive.attributes),
|
|
92
90
|
material: {
|
|
93
|
-
id: primitive?.material?.id
|
|
91
|
+
id: primitive?.material?.id,
|
|
92
|
+
uniqueId: primitive?.material?.uniqueId
|
|
94
93
|
}
|
|
95
94
|
}))
|
|
96
95
|
}
|