@loaders.gl/tile-converter 4.2.0-alpha.1 → 4.2.0-alpha.2
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-converter/3d-tiles-converter.d.ts +11 -0
- package/dist/3d-tiles-converter/3d-tiles-converter.d.ts.map +1 -1
- package/dist/3d-tiles-converter/3d-tiles-converter.js +77 -27
- package/dist/3d-tiles-converter/3d-tiles-converter.js.map +1 -1
- package/dist/3d-tiles-converter/helpers/load-i3s.d.ts +22 -1
- package/dist/3d-tiles-converter/helpers/load-i3s.d.ts.map +1 -1
- package/dist/3d-tiles-converter/helpers/load-i3s.js +49 -4
- package/dist/3d-tiles-converter/helpers/load-i3s.js.map +1 -1
- package/dist/converter-cli.js +2 -1
- package/dist/converter-cli.js.map +1 -1
- package/dist/converter.min.cjs +137 -130
- package/dist/deps-installer/deps-installer.js +1 -1
- package/dist/i3s-converter/helpers/load-3d-tiles.d.ts.map +1 -1
- package/dist/i3s-converter/helpers/load-3d-tiles.js +22 -2
- package/dist/i3s-converter/helpers/load-3d-tiles.js.map +1 -1
- package/dist/i3s-server/bin/i3s-server.min.cjs +86 -86
- package/dist/index.cjs +530 -66
- package/dist/lib/json-schemas/conversion-dump-json-schema.d.ts +463 -0
- package/dist/lib/json-schemas/conversion-dump-json-schema.d.ts.map +1 -0
- package/dist/lib/json-schemas/conversion-dump-json-schema.js +463 -0
- package/dist/lib/json-schemas/conversion-dump-json-schema.js.map +1 -0
- package/dist/lib/utils/conversion-dump.d.ts +12 -5
- package/dist/lib/utils/conversion-dump.d.ts.map +1 -1
- package/dist/lib/utils/conversion-dump.js +44 -24
- package/dist/lib/utils/conversion-dump.js.map +1 -1
- package/dist/lib/utils/file-utils.d.ts +6 -0
- package/dist/lib/utils/file-utils.d.ts.map +1 -1
- package/dist/lib/utils/file-utils.js +7 -0
- package/dist/lib/utils/file-utils.js.map +1 -1
- package/dist/pgm-loader.js +1 -1
- package/package.json +15 -14
- package/src/3d-tiles-converter/3d-tiles-converter.ts +104 -31
- package/src/3d-tiles-converter/helpers/load-i3s.ts +86 -7
- package/src/converter-cli.ts +2 -1
- package/src/i3s-converter/helpers/load-3d-tiles.ts +52 -2
- package/src/lib/json-schemas/conversion-dump-json-schema.ts +285 -0
- package/src/lib/utils/conversion-dump.ts +79 -27
- package/src/lib/utils/file-utils.ts +13 -0
|
@@ -1,36 +1,45 @@
|
|
|
1
|
-
import {load} from '@loaders.gl/core';
|
|
1
|
+
import {LoaderWithParser, load} from '@loaders.gl/core';
|
|
2
2
|
import {
|
|
3
3
|
I3STileContent,
|
|
4
4
|
I3STileHeader,
|
|
5
5
|
I3STilesetHeader,
|
|
6
6
|
I3SLoader,
|
|
7
|
-
I3SLoaderOptions
|
|
7
|
+
I3SLoaderOptions,
|
|
8
|
+
parseSLPKArchive
|
|
8
9
|
} from '@loaders.gl/i3s';
|
|
10
|
+
import {FileHandleFile} from '@loaders.gl/loader-utils';
|
|
11
|
+
import {ZipFileSystem} from '@loaders.gl/zip';
|
|
12
|
+
|
|
13
|
+
export type SLPKUrlParts = {slpkFileName: string; internalFileName: string};
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
16
|
* Load I3S node content
|
|
12
17
|
* @param sourceTileset - source layer JSON
|
|
13
18
|
* @param sourceTile - source I3S node metadata
|
|
14
19
|
* @param tilesetLoadOptions - load options for Tiles3DLoader
|
|
20
|
+
* @param slpkFilesystem - loaded instance of ZipFileSystem for local convertion from SLPK file
|
|
15
21
|
* @returns - 3DTiles tile content or null
|
|
16
22
|
*/
|
|
17
23
|
export const loadI3SContent = async (
|
|
18
24
|
sourceTileset: I3STilesetHeader | null,
|
|
19
25
|
sourceTile: I3STileHeader,
|
|
20
|
-
tilesetLoadOptions: I3SLoaderOptions
|
|
26
|
+
tilesetLoadOptions: I3SLoaderOptions,
|
|
27
|
+
slpkFilesystem: ZipFileSystem | null
|
|
21
28
|
): Promise<I3STileContent | null> => {
|
|
22
29
|
if (!sourceTileset || !sourceTile.contentUrl) {
|
|
23
30
|
return null;
|
|
24
31
|
}
|
|
25
32
|
|
|
26
|
-
const loadOptions = {
|
|
33
|
+
const loadOptions: I3SLoaderOptions = {
|
|
27
34
|
...tilesetLoadOptions,
|
|
28
35
|
i3s: {
|
|
29
36
|
...tilesetLoadOptions.i3s,
|
|
37
|
+
// @ts-expect-error
|
|
30
38
|
isTileset: false,
|
|
39
|
+
// @ts-expect-error
|
|
31
40
|
isTileHeader: false,
|
|
32
41
|
_tileOptions: {
|
|
33
|
-
attributeUrls: sourceTile.attributeUrls,
|
|
42
|
+
attributeUrls: sourceTile.attributeUrls || [],
|
|
34
43
|
textureUrl: sourceTile.textureUrl,
|
|
35
44
|
textureFormat: sourceTile.textureFormat,
|
|
36
45
|
textureLoaderOptions: sourceTile.textureLoaderOptions,
|
|
@@ -40,13 +49,83 @@ export const loadI3SContent = async (
|
|
|
40
49
|
},
|
|
41
50
|
_tilesetOptions: {
|
|
42
51
|
store: sourceTileset.store,
|
|
52
|
+
// @ts-expect-error
|
|
43
53
|
attributeStorageInfo: sourceTileset.attributeStorageInfo,
|
|
54
|
+
// @ts-expect-error
|
|
44
55
|
fields: sourceTileset.fields
|
|
45
56
|
}
|
|
46
57
|
}
|
|
47
58
|
};
|
|
48
|
-
const tileContent = await
|
|
59
|
+
const tileContent = await loadFromArchive(
|
|
60
|
+
sourceTile.contentUrl,
|
|
61
|
+
I3SLoader,
|
|
62
|
+
loadOptions,
|
|
63
|
+
slpkFilesystem
|
|
64
|
+
);
|
|
49
65
|
|
|
50
|
-
// @ts-expect-error
|
|
51
66
|
return tileContent;
|
|
52
67
|
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Load local SLPK file to ZipFileSystem instance
|
|
71
|
+
* @param url - path to SLPK file
|
|
72
|
+
* @returns instance of ZipFileSystem or null if url is not an SLPK file
|
|
73
|
+
*/
|
|
74
|
+
export async function openSLPK(url: string): Promise<ZipFileSystem | null> {
|
|
75
|
+
const slpkUrlParts = url.split('.slpk');
|
|
76
|
+
const {slpkFileName} = getSlpkUrlParts(url) || {};
|
|
77
|
+
if (slpkFileName) {
|
|
78
|
+
const slpkFileName = `${slpkUrlParts[0]}.slpk`;
|
|
79
|
+
const fileProvider = new FileHandleFile(slpkFileName);
|
|
80
|
+
const archive = await parseSLPKArchive(fileProvider, undefined, slpkFileName);
|
|
81
|
+
const fileSystem = new ZipFileSystem(archive);
|
|
82
|
+
return fileSystem;
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Load a resource with load options and .3tz format support
|
|
89
|
+
* @param url - resource URL
|
|
90
|
+
* @param loader - loader to parse data (Tiles3DLoader / CesiumIonLoader)
|
|
91
|
+
* @param loadOptions - i3s loader options
|
|
92
|
+
* @returns i3s resource
|
|
93
|
+
*/
|
|
94
|
+
export async function loadFromArchive(
|
|
95
|
+
url: string,
|
|
96
|
+
loader: LoaderWithParser,
|
|
97
|
+
loadOptions: I3SLoaderOptions,
|
|
98
|
+
fileSystem: ZipFileSystem | null
|
|
99
|
+
) {
|
|
100
|
+
const slpkUrlParts = getSlpkUrlParts(url);
|
|
101
|
+
if (fileSystem !== null && slpkUrlParts !== null) {
|
|
102
|
+
const {internalFileName} = slpkUrlParts;
|
|
103
|
+
const content = await load(internalFileName, loader, {
|
|
104
|
+
...loadOptions,
|
|
105
|
+
fetch: fileSystem.fetch.bind(fileSystem)
|
|
106
|
+
});
|
|
107
|
+
return content;
|
|
108
|
+
}
|
|
109
|
+
return await load(url, loader, loadOptions);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Extract slpk file path and internal from the url
|
|
114
|
+
* For example, for `./path/to/file.slpk/nodes/0` it returns
|
|
115
|
+
* {"slpkFileName": "./path/to/file.slpk", "internalFileName": "/nodes/0" }
|
|
116
|
+
* @param url full internal file path
|
|
117
|
+
* @returns object with internal slpk file parts
|
|
118
|
+
*/
|
|
119
|
+
function getSlpkUrlParts(url: string): SLPKUrlParts | null {
|
|
120
|
+
const slpkUrlParts = url.split('.slpk');
|
|
121
|
+
let result: SLPKUrlParts | null;
|
|
122
|
+
// Not '.slpk'. The file will be loaded with global fetch function
|
|
123
|
+
if (slpkUrlParts.length === 1) {
|
|
124
|
+
result = null;
|
|
125
|
+
} else if (slpkUrlParts.length === 2) {
|
|
126
|
+
result = {slpkFileName: `${slpkUrlParts[0]}.slpk`, internalFileName: slpkUrlParts[1].slice(1)};
|
|
127
|
+
} else {
|
|
128
|
+
throw new Error('Unexpected URL format');
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
package/src/converter-cli.ts
CHANGED
|
@@ -210,7 +210,8 @@ async function convert(options: ValidatedTileConversionOptions) {
|
|
|
210
210
|
outputPath: options.output,
|
|
211
211
|
tilesetName: options.name,
|
|
212
212
|
maxDepth: options.maxDepth,
|
|
213
|
-
egmFilePath: options.egm
|
|
213
|
+
egmFilePath: options.egm,
|
|
214
|
+
inquirer
|
|
214
215
|
});
|
|
215
216
|
break;
|
|
216
217
|
case TILESET_TYPE._3DTILES:
|
|
@@ -4,8 +4,17 @@ import type {
|
|
|
4
4
|
Tiles3DTileJSONPostprocessed,
|
|
5
5
|
Tiles3DTilesetJSONPostprocessed
|
|
6
6
|
} from '@loaders.gl/3d-tiles';
|
|
7
|
-
import {
|
|
7
|
+
import {Tiles3DArchive} from '@loaders.gl/3d-tiles';
|
|
8
8
|
import {LoaderWithParser, load} from '@loaders.gl/core';
|
|
9
|
+
import {FileHandleFile, FileProvider} from '@loaders.gl/loader-utils';
|
|
10
|
+
import {
|
|
11
|
+
CD_HEADER_SIGNATURE,
|
|
12
|
+
ZipFileSystem,
|
|
13
|
+
parseHashTable,
|
|
14
|
+
parseZipCDFileHeader,
|
|
15
|
+
parseZipLocalFileHeader,
|
|
16
|
+
searchFromTheEnd
|
|
17
|
+
} from '@loaders.gl/zip';
|
|
9
18
|
|
|
10
19
|
/**
|
|
11
20
|
* Load nested 3DTiles tileset. If the sourceTile is not nested tileset - do nothing
|
|
@@ -104,7 +113,10 @@ export async function loadFromArchive(
|
|
|
104
113
|
}
|
|
105
114
|
if (filename) {
|
|
106
115
|
const tz3Path = `${tz3UrlParts[0]}.3tz`;
|
|
107
|
-
const
|
|
116
|
+
const fileProvider = new FileHandleFile(tz3Path);
|
|
117
|
+
const hashTable = await loadHashTable(fileProvider);
|
|
118
|
+
const archive = new Tiles3DArchive(fileProvider, hashTable, tz3Path);
|
|
119
|
+
const fileSystem = new ZipFileSystem(archive);
|
|
108
120
|
const content = await load(filename, loader, {
|
|
109
121
|
...loadOptions,
|
|
110
122
|
fetch: fileSystem.fetch.bind(fileSystem)
|
|
@@ -123,3 +135,41 @@ export async function loadFromArchive(
|
|
|
123
135
|
export function isNestedTileset(tile: Tiles3DTileJSONPostprocessed) {
|
|
124
136
|
return tile?.type === 'json' || tile?.type === '3tz';
|
|
125
137
|
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Load hash file from 3TZ
|
|
141
|
+
* @param fileProvider - binary reader of 3TZ
|
|
142
|
+
* @returns hash table of the 3TZ file content or undefined if the hash file is not presented inside
|
|
143
|
+
*/
|
|
144
|
+
async function loadHashTable(
|
|
145
|
+
fileProvider: FileProvider
|
|
146
|
+
): Promise<undefined | Record<string, bigint>> {
|
|
147
|
+
let hashTable: undefined | Record<string, bigint>;
|
|
148
|
+
|
|
149
|
+
const hashCDOffset = await searchFromTheEnd(fileProvider, CD_HEADER_SIGNATURE);
|
|
150
|
+
|
|
151
|
+
const cdFileHeader = await parseZipCDFileHeader(hashCDOffset, fileProvider);
|
|
152
|
+
|
|
153
|
+
// '@3dtilesIndex1@' is index file that must be the last in the archive. It allows
|
|
154
|
+
// to improve load and read performance when the archive contains a very large number
|
|
155
|
+
// of files.
|
|
156
|
+
if (cdFileHeader?.fileName === '@3dtilesIndex1@') {
|
|
157
|
+
const localFileHeader = await parseZipLocalFileHeader(
|
|
158
|
+
cdFileHeader.localHeaderOffset,
|
|
159
|
+
fileProvider
|
|
160
|
+
);
|
|
161
|
+
if (!localFileHeader) {
|
|
162
|
+
throw new Error('corrupted 3tz');
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const fileDataOffset = localFileHeader.fileDataOffset;
|
|
166
|
+
const hashFile = await fileProvider.slice(
|
|
167
|
+
fileDataOffset,
|
|
168
|
+
fileDataOffset + localFileHeader.compressedSize
|
|
169
|
+
);
|
|
170
|
+
|
|
171
|
+
hashTable = parseHashTable(hashFile);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return hashTable;
|
|
175
|
+
}
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
export const dumpJsonSchema = {
|
|
2
|
+
type: 'object',
|
|
3
|
+
properties: {
|
|
4
|
+
options: {
|
|
5
|
+
type: 'object',
|
|
6
|
+
properties: {
|
|
7
|
+
inputUrl: {type: 'string'},
|
|
8
|
+
outputPath: {type: 'string'},
|
|
9
|
+
tilesetName: {type: 'string'},
|
|
10
|
+
maxDepth: {type: 'number'},
|
|
11
|
+
slpk: {type: 'boolean'},
|
|
12
|
+
egmFilePath: {type: 'string'},
|
|
13
|
+
token: {type: 'string'},
|
|
14
|
+
draco: {type: 'boolean'},
|
|
15
|
+
mergeMaterials: {type: 'boolean'},
|
|
16
|
+
generateTextures: {type: 'boolean'},
|
|
17
|
+
generateBoundingVolumes: {type: 'boolean'},
|
|
18
|
+
metadataClass: {type: 'string'},
|
|
19
|
+
analyze: {type: 'boolean'}
|
|
20
|
+
},
|
|
21
|
+
required: ['inputUrl', 'outputPath', 'tilesetName']
|
|
22
|
+
},
|
|
23
|
+
tilesConverted: {
|
|
24
|
+
type: 'object',
|
|
25
|
+
patternProperties: {
|
|
26
|
+
'.*': {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
nodes: {
|
|
30
|
+
type: 'array',
|
|
31
|
+
items: {
|
|
32
|
+
type: 'object',
|
|
33
|
+
properties: {
|
|
34
|
+
nodeId: {type: ['number', 'string']},
|
|
35
|
+
done: {type: 'boolean'},
|
|
36
|
+
progress: {type: 'object', patternProperties: {'.*': {type: 'boolean'}}},
|
|
37
|
+
dumpMetadata: {
|
|
38
|
+
type: 'object',
|
|
39
|
+
properties: {
|
|
40
|
+
boundingVolumes: {
|
|
41
|
+
type: ['object', 'null'],
|
|
42
|
+
properties: {
|
|
43
|
+
mbs: {
|
|
44
|
+
type: 'array',
|
|
45
|
+
minItems: 4,
|
|
46
|
+
maxItems: 4,
|
|
47
|
+
items: {type: 'number'}
|
|
48
|
+
},
|
|
49
|
+
obb: {
|
|
50
|
+
type: 'object',
|
|
51
|
+
properties: {
|
|
52
|
+
center: {
|
|
53
|
+
type: 'array',
|
|
54
|
+
minItems: 3,
|
|
55
|
+
maxItems: 3,
|
|
56
|
+
items: {type: 'number'}
|
|
57
|
+
},
|
|
58
|
+
halfSize: {
|
|
59
|
+
type: 'array',
|
|
60
|
+
minItems: 3,
|
|
61
|
+
maxItems: 3,
|
|
62
|
+
items: {type: 'number'}
|
|
63
|
+
},
|
|
64
|
+
quaternion: {
|
|
65
|
+
type: 'array',
|
|
66
|
+
minItems: 4,
|
|
67
|
+
maxItems: 4,
|
|
68
|
+
items: {type: 'number'}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
required: ['center', 'halfSize', 'quaternion']
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
required: ['mbs', 'obb']
|
|
75
|
+
},
|
|
76
|
+
attributesCount: {type: 'number'},
|
|
77
|
+
featureCount: {type: 'number'},
|
|
78
|
+
geometry: {type: 'boolean'},
|
|
79
|
+
hasUvRegions: {type: 'boolean'},
|
|
80
|
+
materialId: {type: 'number'},
|
|
81
|
+
texelCountHint: {type: 'number'},
|
|
82
|
+
vertexCount: {type: 'number'}
|
|
83
|
+
},
|
|
84
|
+
required: [
|
|
85
|
+
'boundingVolumes',
|
|
86
|
+
'featureCount',
|
|
87
|
+
'geometry',
|
|
88
|
+
'hasUvRegions',
|
|
89
|
+
'materialId',
|
|
90
|
+
'vertexCount'
|
|
91
|
+
]
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
required: ['nodeId', 'done']
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
required: ['nodes']
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
},
|
|
102
|
+
textureSetDefinitions: {
|
|
103
|
+
type: 'array',
|
|
104
|
+
items: {
|
|
105
|
+
type: 'object',
|
|
106
|
+
properties: {
|
|
107
|
+
formats: {
|
|
108
|
+
type: 'array',
|
|
109
|
+
items: {
|
|
110
|
+
type: 'object',
|
|
111
|
+
properties: {
|
|
112
|
+
name: {type: 'string'},
|
|
113
|
+
format: {enum: ['jpg', 'png', 'ktx-etc2', 'dds', 'ktx2']}
|
|
114
|
+
},
|
|
115
|
+
required: ['name', 'format']
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
atlas: {type: 'boolean'}
|
|
119
|
+
},
|
|
120
|
+
required: ['formats']
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
attributeMetadataInfo: {
|
|
124
|
+
type: 'object',
|
|
125
|
+
properties: {
|
|
126
|
+
attributeStorageInfo: {
|
|
127
|
+
type: 'array',
|
|
128
|
+
items: {
|
|
129
|
+
type: 'object',
|
|
130
|
+
properties: {
|
|
131
|
+
key: {type: 'string'},
|
|
132
|
+
name: {type: 'string'},
|
|
133
|
+
header: {
|
|
134
|
+
type: 'array',
|
|
135
|
+
items: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
properties: {property: {type: 'string'}, valueType: {type: 'string'}},
|
|
138
|
+
required: ['property', 'valueType']
|
|
139
|
+
}
|
|
140
|
+
},
|
|
141
|
+
ordering: {type: 'array', items: {type: 'string'}},
|
|
142
|
+
attributeValues: {$ref: '#/$defs/AttributeValue'},
|
|
143
|
+
attributeByteCounts: {$ref: '#/$defs/AttributeValue'},
|
|
144
|
+
objectIds: {$ref: '#/$defs/AttributeValue'}
|
|
145
|
+
},
|
|
146
|
+
required: ['key', 'name', 'header']
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
fields: {
|
|
150
|
+
type: 'array',
|
|
151
|
+
items: {
|
|
152
|
+
type: 'object',
|
|
153
|
+
properties: {
|
|
154
|
+
name: {type: 'string'},
|
|
155
|
+
type: {$ref: '#/$defs/ESRIField'},
|
|
156
|
+
alias: {type: 'string'},
|
|
157
|
+
domain: {$ref: '#/$defs/Domain'}
|
|
158
|
+
},
|
|
159
|
+
required: ['name', 'type']
|
|
160
|
+
}
|
|
161
|
+
},
|
|
162
|
+
popupInfo: {
|
|
163
|
+
type: 'object',
|
|
164
|
+
properties: {
|
|
165
|
+
title: {type: 'string'},
|
|
166
|
+
description: {type: 'string'},
|
|
167
|
+
expressionInfos: {type: 'array', items: {}},
|
|
168
|
+
fieldInfos: {type: 'array', items: {$ref: '#/$defs/FieldInfo'}},
|
|
169
|
+
mediaInfos: {type: 'array', items: {}},
|
|
170
|
+
popupElements: {
|
|
171
|
+
type: 'array',
|
|
172
|
+
items: {
|
|
173
|
+
type: 'object',
|
|
174
|
+
properties: {
|
|
175
|
+
text: {type: 'string'},
|
|
176
|
+
type: {type: 'string'},
|
|
177
|
+
fieldInfos: {type: 'array', items: {$ref: '#/$defs/FieldInfo'}}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
},
|
|
184
|
+
required: ['attributeStorageInfo', 'fields']
|
|
185
|
+
},
|
|
186
|
+
materialDefinitions: {
|
|
187
|
+
type: 'array',
|
|
188
|
+
items: {
|
|
189
|
+
type: 'object',
|
|
190
|
+
properties: {
|
|
191
|
+
pbrMetallicRoughness: {
|
|
192
|
+
type: 'object',
|
|
193
|
+
properties: {
|
|
194
|
+
baseColorFactor: {
|
|
195
|
+
type: 'array',
|
|
196
|
+
minItems: 4,
|
|
197
|
+
maxItems: 4,
|
|
198
|
+
items: {type: 'number'}
|
|
199
|
+
},
|
|
200
|
+
baseColorTexture: {$ref: '#/$defs/I3SMaterialTexture'},
|
|
201
|
+
metallicFactor: {type: 'number'},
|
|
202
|
+
roughnessFactor: {type: 'number'},
|
|
203
|
+
metallicRoughnessTexture: {$ref: '#/$defs/I3SMaterialTexture'}
|
|
204
|
+
},
|
|
205
|
+
required: ['metallicFactor', 'roughnessFactor']
|
|
206
|
+
},
|
|
207
|
+
normalTexture: {$ref: '#/$defs/I3SMaterialTexture'},
|
|
208
|
+
occlusionTexture: {$ref: '#/$defs/I3SMaterialTexture'},
|
|
209
|
+
emissiveTexture: {$ref: '#/$defs/I3SMaterialTexture'},
|
|
210
|
+
emissiveFactor: {type: 'array', minItems: 3, maxItems: 3, items: {type: 'number'}},
|
|
211
|
+
alphaMode: {enum: ['opaque', 'mask', 'blend']},
|
|
212
|
+
alphaCutoff: {type: 'number'},
|
|
213
|
+
doubleSided: {type: 'boolean'},
|
|
214
|
+
cullFace: {enum: ['none', 'front', 'back']}
|
|
215
|
+
},
|
|
216
|
+
required: ['pbrMetallicRoughness', 'alphaMode']
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
},
|
|
220
|
+
required: ['options', 'tilesConverted'],
|
|
221
|
+
$defs: {
|
|
222
|
+
AttributeValue: {
|
|
223
|
+
type: 'object',
|
|
224
|
+
properties: {
|
|
225
|
+
valueType: {type: 'string'},
|
|
226
|
+
encoding: {type: 'string'},
|
|
227
|
+
valuesPerElement: {type: 'number'}
|
|
228
|
+
},
|
|
229
|
+
required: ['valueType']
|
|
230
|
+
},
|
|
231
|
+
ESRIField: {
|
|
232
|
+
enum: [
|
|
233
|
+
'esriFieldTypeDate',
|
|
234
|
+
'esriFieldTypeSingle',
|
|
235
|
+
'esriFieldTypeDouble',
|
|
236
|
+
'esriFieldTypeGUID',
|
|
237
|
+
'esriFieldTypeGlobalID',
|
|
238
|
+
'esriFieldTypeInteger',
|
|
239
|
+
'esriFieldTypeOID',
|
|
240
|
+
'esriFieldTypeSmallInteger',
|
|
241
|
+
'esriFieldTypeString'
|
|
242
|
+
]
|
|
243
|
+
},
|
|
244
|
+
Domain: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
properties: {
|
|
247
|
+
type: {type: 'string'},
|
|
248
|
+
name: {type: 'string'},
|
|
249
|
+
description: {type: 'string'},
|
|
250
|
+
fieldType: {type: 'string'},
|
|
251
|
+
range: {type: 'array', items: {type: 'number'}},
|
|
252
|
+
codedValues: {
|
|
253
|
+
type: 'array',
|
|
254
|
+
items: {
|
|
255
|
+
type: 'object',
|
|
256
|
+
properties: {name: {type: 'string'}, code: {type: ['string', 'number']}},
|
|
257
|
+
required: ['name', 'code']
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
mergePolicy: {type: 'string'},
|
|
261
|
+
splitPolicy: {type: 'string'}
|
|
262
|
+
},
|
|
263
|
+
required: ['type', 'name']
|
|
264
|
+
},
|
|
265
|
+
FieldInfo: {
|
|
266
|
+
type: 'object',
|
|
267
|
+
properties: {
|
|
268
|
+
fieldName: {type: 'string'},
|
|
269
|
+
visible: {type: 'boolean'},
|
|
270
|
+
isEditable: {type: 'boolean'},
|
|
271
|
+
label: {type: 'string'}
|
|
272
|
+
},
|
|
273
|
+
required: ['fieldName', 'visible', 'isEditable', 'label']
|
|
274
|
+
},
|
|
275
|
+
I3SMaterialTexture: {
|
|
276
|
+
type: 'object',
|
|
277
|
+
properties: {
|
|
278
|
+
textureSetDefinitionId: {type: 'number'},
|
|
279
|
+
texCoord: {type: 'number'},
|
|
280
|
+
factor: {type: 'number'}
|
|
281
|
+
},
|
|
282
|
+
required: ['textureSetDefinitionId']
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
};
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {isDeepStrictEqual} from 'util';
|
|
2
2
|
import {DUMP_FILE_SUFFIX} from '../../constants';
|
|
3
|
-
import {isFileExists, openJson, removeFile, writeFile} from './file-utils';
|
|
3
|
+
import {isFileExists, openJson, removeFile, renameFile, writeFile} from './file-utils';
|
|
4
4
|
import {join} from 'path';
|
|
5
5
|
import {BoundingVolumes, I3SMaterialDefinition, TextureSetDefinitionFormats} from '@loaders.gl/i3s';
|
|
6
6
|
import {AttributeMetadataInfoObject} from '../../i3s-converter/helpers/attribute-metadata-info';
|
|
7
|
+
import process from 'process';
|
|
8
|
+
import Ajv from 'ajv';
|
|
9
|
+
import {dumpJsonSchema} from '../json-schemas/conversion-dump-json-schema';
|
|
7
10
|
|
|
8
11
|
export type ConversionDumpOptions = {
|
|
9
12
|
inputUrl: string;
|
|
@@ -22,7 +25,7 @@ export type ConversionDumpOptions = {
|
|
|
22
25
|
};
|
|
23
26
|
|
|
24
27
|
type NodeDoneStatus = {
|
|
25
|
-
nodeId: number;
|
|
28
|
+
nodeId: number | string;
|
|
26
29
|
done: boolean;
|
|
27
30
|
progress?: Record<string, boolean>;
|
|
28
31
|
dumpMetadata?: DumpMetadata;
|
|
@@ -60,7 +63,7 @@ export class ConversionDump {
|
|
|
60
63
|
/** Attributes Metadata */
|
|
61
64
|
attributeMetadataInfo?: AttributeMetadataInfoObject;
|
|
62
65
|
/** Array of materials definitions */
|
|
63
|
-
materialDefinitions
|
|
66
|
+
materialDefinitions?: I3SMaterialDefinition[];
|
|
64
67
|
|
|
65
68
|
constructor() {
|
|
66
69
|
this.tilesConverted = {};
|
|
@@ -108,23 +111,34 @@ export class ConversionDump {
|
|
|
108
111
|
`${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
|
|
109
112
|
);
|
|
110
113
|
if (await isFileExists(dumpFilename)) {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
114
|
+
try {
|
|
115
|
+
const dump = await openJson(
|
|
116
|
+
join(this.options.outputPath, this.options.tilesetName),
|
|
117
|
+
`${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
const {
|
|
121
|
+
options,
|
|
122
|
+
tilesConverted,
|
|
123
|
+
textureSetDefinitions,
|
|
124
|
+
attributeMetadataInfo,
|
|
125
|
+
materialDefinitions
|
|
126
|
+
} = dump;
|
|
127
|
+
|
|
128
|
+
const ajv = new Ajv();
|
|
129
|
+
const dumpJsonValidate = ajv.compile(dumpJsonSchema);
|
|
130
|
+
const isDumpValid = dumpJsonValidate(dump);
|
|
131
|
+
|
|
132
|
+
if (isDumpValid && isDeepStrictEqual(options, JSON.parse(JSON.stringify(this.options)))) {
|
|
133
|
+
this.tilesConverted = tilesConverted;
|
|
134
|
+
this.textureSetDefinitions = textureSetDefinitions;
|
|
135
|
+
this.attributeMetadataInfo = attributeMetadataInfo;
|
|
136
|
+
this.materialDefinitions = materialDefinitions;
|
|
137
|
+
this.restored = true;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
} catch (error) {
|
|
141
|
+
console.log("Can't open dump file", error);
|
|
128
142
|
}
|
|
129
143
|
}
|
|
130
144
|
await this.deleteDumpFile();
|
|
@@ -142,8 +156,8 @@ export class ConversionDump {
|
|
|
142
156
|
if (this.attributeMetadataInfo) {
|
|
143
157
|
delete this.attributeMetadataInfo;
|
|
144
158
|
}
|
|
145
|
-
if (this.materialDefinitions
|
|
146
|
-
this.materialDefinitions
|
|
159
|
+
if (this.materialDefinitions) {
|
|
160
|
+
delete this.materialDefinitions;
|
|
147
161
|
}
|
|
148
162
|
}
|
|
149
163
|
|
|
@@ -153,6 +167,7 @@ export class ConversionDump {
|
|
|
153
167
|
private async updateDumpFile(): Promise<void> {
|
|
154
168
|
if (this.options?.outputPath && this.options.tilesetName) {
|
|
155
169
|
try {
|
|
170
|
+
const time = process.hrtime();
|
|
156
171
|
await writeFile(
|
|
157
172
|
join(this.options.outputPath, this.options.tilesetName),
|
|
158
173
|
JSON.stringify({
|
|
@@ -162,7 +177,19 @@ export class ConversionDump {
|
|
|
162
177
|
attributeMetadataInfo: this.attributeMetadataInfo,
|
|
163
178
|
materialDefinitions: this.materialDefinitions
|
|
164
179
|
}),
|
|
165
|
-
`${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
|
|
180
|
+
`${this.options.tilesetName}${DUMP_FILE_SUFFIX}.${time[0]}.${time[1]}`
|
|
181
|
+
);
|
|
182
|
+
await renameFile(
|
|
183
|
+
join(
|
|
184
|
+
this.options.outputPath,
|
|
185
|
+
this.options.tilesetName,
|
|
186
|
+
`${this.options.tilesetName}${DUMP_FILE_SUFFIX}.${time[0]}.${time[1]}`
|
|
187
|
+
),
|
|
188
|
+
join(
|
|
189
|
+
this.options.outputPath,
|
|
190
|
+
this.options.tilesetName,
|
|
191
|
+
`${this.options.tilesetName}${DUMP_FILE_SUFFIX}`
|
|
192
|
+
)
|
|
166
193
|
);
|
|
167
194
|
} catch (error) {
|
|
168
195
|
console.log("Can't update dump file", error);
|
|
@@ -218,9 +245,9 @@ export class ConversionDump {
|
|
|
218
245
|
* @param fileName - source filename
|
|
219
246
|
* @param nodeId - nodeId of the node
|
|
220
247
|
*/
|
|
221
|
-
async addNode(filename: string, nodeId: number, dumpMetadata
|
|
248
|
+
async addNode(filename: string, nodeId: number | string, dumpMetadata?: DumpMetadata) {
|
|
222
249
|
const {nodes} = this.getRecord(filename) || {nodes: []};
|
|
223
|
-
nodes.push({nodeId, done: false,
|
|
250
|
+
nodes.push({nodeId, done: false, dumpMetadata});
|
|
224
251
|
if (nodes.length === 1) {
|
|
225
252
|
this.setRecord(filename, {nodes});
|
|
226
253
|
}
|
|
@@ -250,7 +277,12 @@ export class ConversionDump {
|
|
|
250
277
|
* @param resourceType - resource type to update status
|
|
251
278
|
* @param value - value
|
|
252
279
|
*/
|
|
253
|
-
updateDoneStatus(
|
|
280
|
+
updateDoneStatus(
|
|
281
|
+
filename: string,
|
|
282
|
+
nodeId: number | string,
|
|
283
|
+
resourceType: string,
|
|
284
|
+
value: boolean
|
|
285
|
+
) {
|
|
254
286
|
const nodeDump = this.tilesConverted[filename]?.nodes.find(
|
|
255
287
|
(element) => element.nodeId === nodeId
|
|
256
288
|
);
|
|
@@ -271,7 +303,7 @@ export class ConversionDump {
|
|
|
271
303
|
* @param writeResults - array of writing resource files results
|
|
272
304
|
*/
|
|
273
305
|
async updateConvertedTilesDump(
|
|
274
|
-
changedRecords: {outputId?: number; sourceId?: string; resourceType?: string}[],
|
|
306
|
+
changedRecords: {outputId?: number | string; sourceId?: string; resourceType?: string}[],
|
|
275
307
|
writeResults: PromiseSettledResult<string | null>[]
|
|
276
308
|
) {
|
|
277
309
|
for (let i = 0; i < changedRecords.length; i++) {
|
|
@@ -299,6 +331,26 @@ export class ConversionDump {
|
|
|
299
331
|
await this.updateDumpFile();
|
|
300
332
|
}
|
|
301
333
|
|
|
334
|
+
/**
|
|
335
|
+
* Update 3d-tiles-converter dump file
|
|
336
|
+
* @param filename - source filename
|
|
337
|
+
* @param nodeId - nodeId
|
|
338
|
+
* @param done - conversion status
|
|
339
|
+
*/
|
|
340
|
+
async updateConvertedNodesDumpFile(
|
|
341
|
+
filename: string,
|
|
342
|
+
nodeId: number | string,
|
|
343
|
+
done: boolean
|
|
344
|
+
): Promise<void> {
|
|
345
|
+
const nodeDump = this.tilesConverted[filename]?.nodes.find(
|
|
346
|
+
(element) => element.nodeId === nodeId
|
|
347
|
+
);
|
|
348
|
+
if (nodeDump) {
|
|
349
|
+
nodeDump.done = done;
|
|
350
|
+
await this.updateDumpFile();
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
|
|
302
354
|
/**
|
|
303
355
|
* Check is source file conversion complete
|
|
304
356
|
* @param filename - source filename
|