@loaders.gl/scene 5.0.0-beta.4 → 5.0.0-beta.6
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/DRACO-README.md +1 -1
- package/GLTF-README.md +1 -1
- package/OBJ-README.md +1 -1
- package/README.md +6 -1
- package/THIRD-PARTY-NOTICES.md +6 -0
- package/dist/dist.dev.js +2959 -750
- package/dist/dist.min.js +18 -18
- package/dist/draco/draco-javascript-loader-with-parser.d.ts +2 -2
- package/dist/draco/draco-loader-types.cjs +3 -3
- package/dist/draco/draco-loader-types.cjs.map +2 -2
- package/dist/draco/draco-loader-types.d.ts +1 -1
- package/dist/draco/draco-loader-types.d.ts.map +1 -1
- package/dist/draco/draco-loader-types.js +1 -1
- package/dist/draco/draco-loader-types.js.map +1 -1
- package/dist/draco/draco-loader.d.ts +2 -2
- package/dist/draco/draco-wasm-loader-with-parser.d.ts +2 -2
- package/dist/draco/draco3d-loader-with-parser.d.ts +2 -2
- package/dist/draco/lib/utils/version.cjs +1 -1
- package/dist/draco/lib/utils/version.js +1 -1
- package/dist/draco-worker-node.js +401 -113
- package/dist/draco-worker-node.js.map +4 -4
- package/dist/draco-worker.js +395 -113
- package/dist/draco-worker.js.map +4 -4
- package/dist/draco-writer-worker-node.cjs +655 -85
- package/dist/draco-writer-worker-node.cjs.map +4 -4
- package/dist/draco-writer-worker.js +625 -69
- package/dist/draco-writer-worker.js.map +4 -4
- package/dist/gltf/lib/extensions/KHR_gaussian_splatting.cjs +11 -4
- package/dist/gltf/lib/extensions/KHR_gaussian_splatting.cjs.map +2 -2
- package/dist/gltf/lib/extensions/KHR_gaussian_splatting.d.ts.map +1 -1
- package/dist/gltf/lib/extensions/KHR_gaussian_splatting.js +12 -2
- package/dist/gltf/lib/extensions/KHR_gaussian_splatting.js.map +1 -1
- package/dist/gltf/lib/parsers/parse-gltf.cjs.map +2 -2
- package/dist/gltf/lib/parsers/parse-gltf.d.ts +2 -0
- package/dist/gltf/lib/parsers/parse-gltf.d.ts.map +1 -1
- package/dist/gltf/lib/parsers/parse-gltf.js.map +1 -1
- package/dist/gltf/lib/types/gltf-additional-extension-schemas.cjs +22 -0
- package/dist/gltf/lib/types/gltf-additional-extension-schemas.cjs.map +2 -2
- package/dist/gltf/lib/types/gltf-additional-extension-schemas.d.ts +23 -0
- package/dist/gltf/lib/types/gltf-additional-extension-schemas.d.ts.map +1 -1
- package/dist/gltf/lib/types/gltf-additional-extension-schemas.js +22 -0
- package/dist/gltf/lib/types/gltf-additional-extension-schemas.js.map +1 -1
- package/dist/gltf/lib/types/gltf-extension-schemas.cjs +8 -0
- package/dist/gltf/lib/types/gltf-extension-schemas.cjs.map +2 -2
- package/dist/gltf/lib/types/gltf-extension-schemas.d.ts +27 -0
- package/dist/gltf/lib/types/gltf-extension-schemas.d.ts.map +1 -1
- package/dist/gltf/lib/types/gltf-extension-schemas.js +8 -1
- package/dist/gltf/lib/types/gltf-extension-schemas.js.map +1 -1
- package/dist/gltf/lib/types/gltf-json-schema.cjs.map +1 -1
- package/dist/gltf/lib/types/gltf-json-schema.d.ts +2 -2
- package/dist/gltf/lib/types/gltf-json-schema.d.ts.map +1 -1
- package/dist/gltf/lib/types/gltf-version-zod-schemas.d.ts +34 -0
- package/dist/gltf/lib/types/gltf-version-zod-schemas.d.ts.map +1 -1
- package/dist/gltf/lib/types/gltf-zod-schema.cjs +1 -1
- package/dist/gltf/lib/types/gltf-zod-schema.cjs.map +2 -2
- package/dist/gltf/lib/types/gltf-zod-schema.d.ts +51 -0
- package/dist/gltf/lib/types/gltf-zod-schema.d.ts.map +1 -1
- package/dist/gltf/lib/types/gltf-zod-schema.js +2 -2
- package/dist/gltf/lib/types/gltf-zod-schema.js.map +1 -1
- package/dist/gltf/lib/utils/version.cjs +1 -1
- package/dist/gltf/lib/utils/version.js +1 -1
- package/dist/index.cjs +4 -1
- package/dist/index.cjs.map +2 -2
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/mtl-loader-types.cjs +1 -1
- package/dist/obj/mtl-loader-types.cjs +1 -1
- package/dist/obj/mtl-loader-types.js +1 -1
- package/dist/obj/obj-loader-types.cjs +3 -3
- package/dist/obj/obj-loader-types.cjs.map +2 -2
- package/dist/obj/obj-loader-types.d.ts +1 -1
- package/dist/obj/obj-loader-types.d.ts.map +1 -1
- package/dist/obj/obj-loader-types.js +2 -2
- package/dist/obj/obj-loader-types.js.map +1 -1
- package/dist/obj/obj-loader.cjs +5 -5
- package/dist/obj/obj-loader.cjs.map +2 -2
- package/dist/obj/obj-loader.d.ts +1 -1
- package/dist/obj/obj-loader.d.ts.map +1 -1
- package/dist/obj/obj-loader.js +1 -1
- package/dist/obj/obj-loader.js.map +1 -1
- package/dist/obj/obj-writer.cjs +1 -1
- package/dist/obj/obj-writer.js +1 -1
- package/dist/obj-loader-types.cjs +3 -3
- package/dist/obj-loader-types.cjs.map +2 -2
- package/dist/obj-loader.cjs +5 -5
- package/dist/obj-loader.cjs.map +2 -2
- package/dist/obj-worker.js +568 -287
- package/dist/obj-writer.cjs +1 -1
- package/dist/stl/parse-stl.cjs +100 -0
- package/dist/stl/parse-stl.cjs.map +7 -0
- package/dist/stl/parse-stl.d.ts +4 -0
- package/dist/stl/parse-stl.d.ts.map +1 -0
- package/dist/stl/parse-stl.js +84 -0
- package/dist/stl/parse-stl.js.map +1 -0
- package/dist/stl-loader-types.cjs +58 -0
- package/dist/stl-loader-types.cjs.map +7 -0
- package/dist/stl-loader-types.d.ts +32 -0
- package/dist/stl-loader-types.d.ts.map +1 -0
- package/dist/stl-loader-types.js +25 -0
- package/dist/stl-loader-types.js.map +1 -0
- package/dist/stl-loader.cjs +38 -0
- package/dist/stl-loader.cjs.map +7 -0
- package/dist/stl-loader.d.ts +17 -0
- package/dist/stl-loader.d.ts.map +1 -0
- package/dist/stl-loader.js +13 -0
- package/dist/stl-loader.js.map +1 -0
- package/dist/usd/index.cjs.map +1 -1
- package/dist/usd/index.d.ts +1 -1
- package/dist/usd/index.d.ts.map +1 -1
- package/dist/usd/lib/parse-usd.cjs +42 -5
- package/dist/usd/lib/parse-usd.cjs.map +2 -2
- package/dist/usd/lib/parse-usd.d.ts.map +1 -1
- package/dist/usd/lib/parse-usd.js +53 -6
- package/dist/usd/lib/parse-usd.js.map +1 -1
- package/dist/usd/lib/parse-usdc.cjs +187 -0
- package/dist/usd/lib/parse-usdc.cjs.map +7 -0
- package/dist/usd/lib/parse-usdc.d.ts +4 -0
- package/dist/usd/lib/parse-usdc.d.ts.map +1 -0
- package/dist/usd/lib/parse-usdc.js +176 -0
- package/dist/usd/lib/parse-usdc.js.map +1 -0
- package/dist/usd/lib/parse-usdz.cjs +22 -0
- package/dist/usd/lib/parse-usdz.cjs.map +2 -2
- package/dist/usd/lib/parse-usdz.d.ts.map +1 -1
- package/dist/usd/lib/parse-usdz.js +26 -0
- package/dist/usd/lib/parse-usdz.js.map +1 -1
- package/dist/usd/lib/usd-types.cjs.map +1 -1
- package/dist/usd/lib/usd-types.d.ts +14 -1
- package/dist/usd/lib/usd-types.d.ts.map +1 -1
- package/dist/usd/lib/usd-types.js +1 -1
- package/dist/usd/lib/usd-types.js.map +1 -1
- package/dist/usd/libs/README.md +27 -0
- package/dist/usd/libs/THREE-LICENSE +21 -0
- package/dist/usd/libs/usdc-parser.d.ts +78 -0
- package/dist/usd/libs/usdc-parser.d.ts.map +1 -0
- package/dist/usd/libs/usdc-parser.js +1669 -0
- package/dist/usd/libs/usdc-parser.js.map +1 -0
- package/dist/usd/usd-loader-types.cjs +4 -4
- package/dist/usd/usd-loader-types.cjs.map +2 -2
- package/dist/usd/usd-loader-types.d.ts +7 -7
- package/dist/usd/usd-loader-types.d.ts.map +1 -1
- package/dist/usd/usd-loader-types.js +6 -6
- package/dist/usd/usd-loader-types.js.map +1 -1
- package/dist/usd/usd-loader.cjs.map +1 -1
- package/dist/usd/usd-loader.d.ts +4 -4
- package/dist/usd/usd-loader.d.ts.map +1 -1
- package/dist/usd/usd-loader.js +2 -2
- package/dist/usd/usd-loader.js.map +1 -1
- package/dist/usd-loader-types.cjs +4 -4
- package/dist/usd-loader-types.cjs.map +2 -2
- package/dist/usd-loader.cjs.map +1 -1
- package/package.json +14 -8
- package/src/draco/draco-loader-types.ts +1 -1
- package/src/gltf/lib/extensions/KHR_gaussian_splatting.ts +14 -4
- package/src/gltf/lib/parsers/parse-gltf.ts +2 -0
- package/src/gltf/lib/types/gltf-additional-extension-schemas.ts +32 -0
- package/src/gltf/lib/types/gltf-extension-schemas.ts +12 -0
- package/src/gltf/lib/types/gltf-json-schema.ts +2 -1
- package/src/gltf/lib/types/gltf-zod-schema.ts +2 -1
- package/src/index.ts +3 -1
- package/src/obj/obj-loader-types.ts +1 -1
- package/src/obj/obj-loader.ts +1 -1
- package/src/stl/parse-stl.ts +90 -0
- package/src/stl-loader-types.ts +28 -0
- package/src/stl-loader.ts +12 -0
- package/src/usd/index.ts +1 -0
- package/src/usd/lib/parse-usd.ts +68 -7
- package/src/usd/lib/parse-usdc.ts +188 -0
- package/src/usd/lib/parse-usdz.ts +28 -0
- package/src/usd/lib/usd-types.ts +16 -2
- package/src/usd/libs/README.md +27 -0
- package/src/usd/libs/THREE-LICENSE +21 -0
- package/src/usd/libs/usdc-parser.d.ts +18 -0
- package/src/usd/libs/usdc-parser.js +1669 -0
- package/src/usd/usd-loader-types.ts +5 -5
- package/src/usd/usd-loader.ts +2 -2
package/src/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// loaders.gl
|
|
2
1
|
// SPDX-License-Identifier: MIT
|
|
3
2
|
// Copyright (c) vis.gl contributors
|
|
3
|
+
// loaders.gl
|
|
4
4
|
|
|
5
5
|
export * from './usd/index';
|
|
6
6
|
export type * from './gltf/index';
|
|
@@ -23,3 +23,5 @@ export {DRACO_EXTERNAL_LIBRARIES, DRACO_EXTERNAL_LIBRARY_URLS} from './draco/dra
|
|
|
23
23
|
|
|
24
24
|
export {MaterialXLoader} from './materialx-loader-types';
|
|
25
25
|
export type {MaterialXDocument, MaterialXElement} from './materialx/parse-materialx';
|
|
26
|
+
|
|
27
|
+
export {STLLoader} from './stl-loader-types';
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import type {Loader, LoaderOptions} from '@loaders.gl/core/loader-utils';
|
|
6
6
|
import type {Mesh, MeshArrowTable, ArrowTableBatch} from '@loaders.gl/core/schema';
|
|
7
7
|
import {OBJFormat} from './obj-format';
|
|
8
|
-
import {serializeArrowWorkerTable} from '@loaders.gl/core/
|
|
8
|
+
import {serializeArrowWorkerTable} from '@loaders.gl/core/arrow-worker-transport';
|
|
9
9
|
|
|
10
10
|
// __VERSION__ is injected by babel-plugin-version-inline
|
|
11
11
|
// @ts-ignore TS2304: Cannot find name '__VERSION__'.
|
package/src/obj/obj-loader.ts
CHANGED
|
@@ -15,7 +15,7 @@ import type {Mesh, MeshArrowTable} from '@loaders.gl/core/schema';
|
|
|
15
15
|
import {
|
|
16
16
|
deserializeArrowWorkerTable,
|
|
17
17
|
serializeArrowWorkerTable
|
|
18
|
-
} from '@loaders.gl/core/
|
|
18
|
+
} from '@loaders.gl/core/arrow-worker-transport';
|
|
19
19
|
import {
|
|
20
20
|
convertMeshToTable,
|
|
21
21
|
convertTableToMesh,
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) vis.gl contributors
|
|
3
|
+
// loaders.gl
|
|
4
|
+
import type {Mesh} from '@loaders.gl/core/schema';
|
|
5
|
+
import {deduceMeshSchema, getMeshBoundingBox} from '@loaders.gl/core/schema-utils';
|
|
6
|
+
|
|
7
|
+
/** Parses ASCII or little-endian binary STL without changing authored normals. */
|
|
8
|
+
export function parseSTL(data: ArrayBuffer): Mesh {
|
|
9
|
+
const view = new DataView(data);
|
|
10
|
+
const binary = data.byteLength >= 84 && 84 + view.getUint32(80, true) * 50 === data.byteLength;
|
|
11
|
+
let positions: Float32Array;
|
|
12
|
+
let normals: Float32Array;
|
|
13
|
+
let header: string;
|
|
14
|
+
if (binary) {
|
|
15
|
+
const triangleCount = view.getUint32(80, true);
|
|
16
|
+
positions = new Float32Array(triangleCount * 9);
|
|
17
|
+
normals = new Float32Array(triangleCount * 9);
|
|
18
|
+
header = new TextDecoder().decode(data.slice(0, 80));
|
|
19
|
+
for (let triangle = 0; triangle < triangleCount; triangle++) {
|
|
20
|
+
const offset = 84 + triangle * 50;
|
|
21
|
+
for (let component = 0; component < 9; component++) {
|
|
22
|
+
positions[triangle * 9 + component] = view.getFloat32(offset + 12 + component * 4, true);
|
|
23
|
+
normals[triangle * 9 + component] = view.getFloat32(offset + (component % 3) * 4, true);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
} else {
|
|
27
|
+
const lines = new TextDecoder()
|
|
28
|
+
.decode(data)
|
|
29
|
+
.trim()
|
|
30
|
+
.split(/\r?\n/)
|
|
31
|
+
.map(line => line.trim())
|
|
32
|
+
.filter(Boolean);
|
|
33
|
+
const vertices: number[] = [];
|
|
34
|
+
const normalValues: number[] = [];
|
|
35
|
+
header = lines[0] || '';
|
|
36
|
+
let lineIndex = 0;
|
|
37
|
+
while (lineIndex < lines.length) {
|
|
38
|
+
if (!/^solid(?:\s|$)/i.test(lines[lineIndex++]))
|
|
39
|
+
throw new Error('STL: expected solid header or exact binary length');
|
|
40
|
+
while (lineIndex < lines.length && !/^endsolid(?:\s|$)/i.test(lines[lineIndex])) {
|
|
41
|
+
const normal = parseVector(lines[lineIndex++], 'facet normal');
|
|
42
|
+
if (!/^outer\s+loop$/i.test(lines[lineIndex++] || ''))
|
|
43
|
+
throw new Error('STL: expected outer loop');
|
|
44
|
+
for (let vertex = 0; vertex < 3; vertex++) {
|
|
45
|
+
vertices.push(...parseVector(lines[lineIndex++], 'vertex'));
|
|
46
|
+
normalValues.push(...normal);
|
|
47
|
+
}
|
|
48
|
+
if (
|
|
49
|
+
!/^endloop$/i.test(lines[lineIndex++] || '') ||
|
|
50
|
+
!/^endfacet$/i.test(lines[lineIndex++] || '')
|
|
51
|
+
)
|
|
52
|
+
throw new Error('STL: incomplete facet');
|
|
53
|
+
}
|
|
54
|
+
if (lineIndex >= lines.length) throw new Error('STL: missing endsolid');
|
|
55
|
+
lineIndex++;
|
|
56
|
+
}
|
|
57
|
+
if (!lines.length) throw new Error('STL: empty input');
|
|
58
|
+
positions = new Float32Array(vertices);
|
|
59
|
+
normals = new Float32Array(normalValues);
|
|
60
|
+
}
|
|
61
|
+
if (!positions.every(Number.isFinite) || !normals.every(Number.isFinite))
|
|
62
|
+
throw new Error('STL: non-finite coordinates');
|
|
63
|
+
const attributes = {POSITION: {value: positions, size: 3}, NORMAL: {value: normals, size: 3}};
|
|
64
|
+
return {
|
|
65
|
+
topology: 'triangle-list',
|
|
66
|
+
mode: 4,
|
|
67
|
+
attributes,
|
|
68
|
+
schema: deduceMeshSchema(attributes),
|
|
69
|
+
header: {
|
|
70
|
+
vertexCount: positions.length / 3,
|
|
71
|
+
boundingBox: getMeshBoundingBox(attributes) ?? undefined
|
|
72
|
+
},
|
|
73
|
+
loaderData: {header, format: binary ? 'binary' : 'ascii'}
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/** Reads exactly three finite decimal components after an ASCII record prefix. */
|
|
78
|
+
function parseVector(line: string | undefined, prefix: string): number[] {
|
|
79
|
+
const tokens = line?.split(/\s+/) || [];
|
|
80
|
+
const prefixTokens = prefix.split(' ');
|
|
81
|
+
const components = tokens.slice(prefixTokens.length);
|
|
82
|
+
if (
|
|
83
|
+
prefixTokens.some((token, index) => tokens[index]?.toLowerCase() !== token) ||
|
|
84
|
+
components.length !== 3 ||
|
|
85
|
+
components.some(value => !/^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:e[+-]?\d+)?$/i.test(value))
|
|
86
|
+
) {
|
|
87
|
+
throw new Error(`STL: invalid ${prefix} record`);
|
|
88
|
+
}
|
|
89
|
+
return components.map(Number);
|
|
90
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) vis.gl contributors
|
|
3
|
+
/** Package version injected by the build. */
|
|
4
|
+
// @ts-ignore __VERSION__ is injected during compilation.
|
|
5
|
+
const VERSION = typeof __VERSION__ !== 'undefined' ? __VERSION__ : 'latest';
|
|
6
|
+
// loaders.gl
|
|
7
|
+
import type {Mesh} from '@loaders.gl/core/schema';
|
|
8
|
+
|
|
9
|
+
/** Loads the STL parser on demand. */
|
|
10
|
+
async function preload() {
|
|
11
|
+
const {STLLoaderWithParser} = await import('@loaders.gl/scene/stl-loader');
|
|
12
|
+
return STLLoaderWithParser;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/** Metadata-only loader for ASCII and binary STL triangle meshes. */
|
|
16
|
+
export const STLLoader = {
|
|
17
|
+
name: 'STL',
|
|
18
|
+
id: 'stl',
|
|
19
|
+
module: 'scene',
|
|
20
|
+
version: VERSION,
|
|
21
|
+
extensions: ['stl'],
|
|
22
|
+
mimeTypes: ['model/stl'],
|
|
23
|
+
binary: true,
|
|
24
|
+
dataType: null as unknown as Mesh,
|
|
25
|
+
batchType: null as never,
|
|
26
|
+
options: {},
|
|
27
|
+
preload
|
|
28
|
+
} as const;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) vis.gl contributors
|
|
3
|
+
// loaders.gl
|
|
4
|
+
import {STLLoader} from './stl-loader-types';
|
|
5
|
+
import {parseSTL} from './stl/parse-stl';
|
|
6
|
+
const {preload: _preload, ...metadata} = STLLoader;
|
|
7
|
+
/** Parser-bearing STL loader returning renderer-independent triangle meshes. */
|
|
8
|
+
export const STLLoaderWithParser = {
|
|
9
|
+
...metadata,
|
|
10
|
+
parse: async (data: ArrayBuffer) => parseSTL(data),
|
|
11
|
+
parseSync: parseSTL
|
|
12
|
+
} as const;
|
package/src/usd/index.ts
CHANGED
package/src/usd/lib/parse-usd.ts
CHANGED
|
@@ -1,11 +1,20 @@
|
|
|
1
|
-
// loaders.gl
|
|
2
1
|
// SPDX-License-Identifier: MIT
|
|
3
2
|
// Copyright (c) vis.gl contributors
|
|
3
|
+
import {parseUSDC} from './parse-usdc';
|
|
4
|
+
// loaders.gl
|
|
4
5
|
|
|
5
6
|
import type {LoaderContext} from '@loaders.gl/core/loader-utils';
|
|
6
7
|
import {parseUSDA} from './parse-usda';
|
|
7
8
|
import {parseUSDZArchive} from './parse-usdz';
|
|
8
|
-
import type {
|
|
9
|
+
import type {
|
|
10
|
+
USDAssetPath,
|
|
11
|
+
USDEmbeddedAsset,
|
|
12
|
+
USDLoaderOptions,
|
|
13
|
+
USDPrim,
|
|
14
|
+
USDStage,
|
|
15
|
+
USDValue,
|
|
16
|
+
USDVariant
|
|
17
|
+
} from './usd-types';
|
|
9
18
|
|
|
10
19
|
type USDParseEnvironment = {
|
|
11
20
|
/** Fetch implementation used for external layers. */
|
|
@@ -54,23 +63,45 @@ export async function parseUSD(
|
|
|
54
63
|
let stage: USDStage;
|
|
55
64
|
if (isZIPArchive(data)) {
|
|
56
65
|
const archive = parseUSDZArchive(data);
|
|
57
|
-
const rootFilename =
|
|
58
|
-
if (!rootFilename) {
|
|
59
|
-
throw new Error('USDZ
|
|
66
|
+
const rootFilename = archive.keys().next().value;
|
|
67
|
+
if (!rootFilename || !/\.(?:usd[ac]?)$/i.test(rootFilename)) {
|
|
68
|
+
throw new Error('USDZ archive has no supported USD root layer.');
|
|
60
69
|
}
|
|
61
70
|
|
|
62
71
|
const archiveBase = 'https://usd.archive/';
|
|
72
|
+
const assets: Record<string, USDEmbeddedAsset> = Object.create(null);
|
|
63
73
|
for (const [filename, content] of archive) {
|
|
64
|
-
|
|
74
|
+
const assetUrl = new URL(filename, archiveBase);
|
|
75
|
+
if (
|
|
76
|
+
filename.startsWith('/') ||
|
|
77
|
+
/^[a-z][a-z\d+.-]*:/i.test(filename) ||
|
|
78
|
+
filename.includes('\\') ||
|
|
79
|
+
/[\u0000-\u001f\u007f]/.test(filename) ||
|
|
80
|
+
filename.trim() !== filename ||
|
|
81
|
+
filename.split('/').some(segment => {
|
|
82
|
+
const normalizedSegment = segment.replace(/%2e/gi, '.');
|
|
83
|
+
return normalizedSegment === '.' || normalizedSegment === '..';
|
|
84
|
+
}) ||
|
|
85
|
+
assetUrl.origin !== new URL(archiveBase).origin ||
|
|
86
|
+
assetUrl.search ||
|
|
87
|
+
assetUrl.hash ||
|
|
88
|
+
assets[assetUrl.href]
|
|
89
|
+
) {
|
|
90
|
+
throw new Error(`USDZ entry "${filename}" has an ambiguous package URL.`);
|
|
91
|
+
}
|
|
92
|
+
environment.archiveFiles.set(assetUrl.href, content);
|
|
93
|
+
assets[assetUrl.href] = createEmbeddedAsset(filename, content);
|
|
65
94
|
}
|
|
66
95
|
const rootUrl = new URL(rootFilename, archiveBase).href;
|
|
67
96
|
stage = parseUSDLayer(archive.get(rootFilename)!, rootUrl);
|
|
68
97
|
stage.format = 'usdz';
|
|
98
|
+
stage.assets = assets;
|
|
69
99
|
} else {
|
|
70
100
|
stage = parseUSDLayer(data, url);
|
|
71
101
|
}
|
|
72
102
|
|
|
73
103
|
if (stage.url) {
|
|
104
|
+
setAttributeSourceUrls(stage, stage.url);
|
|
74
105
|
environment.layers.add(stage.url);
|
|
75
106
|
}
|
|
76
107
|
if (environment.options.usd?.compose !== false) {
|
|
@@ -83,6 +114,14 @@ export async function parseUSD(
|
|
|
83
114
|
return stage;
|
|
84
115
|
}
|
|
85
116
|
|
|
117
|
+
/** Retains encoded file data and a conservative filename-based image MIME hint. */
|
|
118
|
+
function createEmbeddedAsset(path: string, data: ArrayBuffer): USDEmbeddedAsset {
|
|
119
|
+
const extension = path.slice(path.lastIndexOf('.') + 1).toLowerCase();
|
|
120
|
+
const mimeType =
|
|
121
|
+
extension === 'png' ? 'image/png' : /^(?:jpe?g)$/.test(extension) ? 'image/jpeg' : undefined;
|
|
122
|
+
return mimeType ? {path, data, mimeType} : {path, data};
|
|
123
|
+
}
|
|
124
|
+
|
|
86
125
|
/** Selects a source location suitable for resolving referenced layers. */
|
|
87
126
|
function getSourceLocation(
|
|
88
127
|
configuredLocation: string | undefined,
|
|
@@ -126,11 +165,32 @@ const USD_LOADER_DEFAULT_OPTIONS = {
|
|
|
126
165
|
function parseUSDLayer(data: ArrayBuffer, url?: string): USDStage {
|
|
127
166
|
const signature = TEXT_DECODER.decode(data.slice(0, 8));
|
|
128
167
|
if (signature === CRATE_SIGNATURE) {
|
|
129
|
-
|
|
168
|
+
return parseUSDC(data, url);
|
|
130
169
|
}
|
|
131
170
|
return parseUSDA(TEXT_DECODER.decode(data), url);
|
|
132
171
|
}
|
|
133
172
|
|
|
173
|
+
/** Retains each attribute's authored layer before composition merges prims from different files. */
|
|
174
|
+
function setAttributeSourceUrls(stage: USDStage, sourceUrl: string): void {
|
|
175
|
+
const pendingContents: (USDPrim | USDVariant)[] = [...stage.rootPrims];
|
|
176
|
+
while (pendingContents.length) {
|
|
177
|
+
const contents = pendingContents.pop()!;
|
|
178
|
+
for (const attribute of Object.values(contents.attributes)) {
|
|
179
|
+
attribute.sourceUrl = sourceUrl;
|
|
180
|
+
}
|
|
181
|
+
for (const child of contents.children) {
|
|
182
|
+
pendingContents.push(child);
|
|
183
|
+
}
|
|
184
|
+
if ('variants' in contents) {
|
|
185
|
+
for (const variants of Object.values(contents.variants)) {
|
|
186
|
+
for (const variant of Object.values(variants)) {
|
|
187
|
+
pendingContents.push(variant);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
134
194
|
/** Recursively applies variants, references, overrides, and class filtering. */
|
|
135
195
|
async function composePrims(
|
|
136
196
|
prims: USDPrim[],
|
|
@@ -227,6 +287,7 @@ async function loadReferencedStage(
|
|
|
227
287
|
content = response.arrayBuffer();
|
|
228
288
|
}
|
|
229
289
|
const stage = parseUSDLayer(await content, url);
|
|
290
|
+
setAttributeSourceUrls(stage, url);
|
|
230
291
|
environment.layers.add(url);
|
|
231
292
|
return stage;
|
|
232
293
|
})();
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) vis.gl contributors
|
|
3
|
+
import {USDCParser} from '../libs/usdc-parser.js';
|
|
4
|
+
import type {USDPrim, USDStage, USDValue} from './usd-types';
|
|
5
|
+
|
|
6
|
+
/** Decodes the supported binary crate subset into the same stage model as USDA. */
|
|
7
|
+
export function parseUSDC(data: ArrayBuffer, url?: string): USDStage {
|
|
8
|
+
if (data.byteLength < 88 || data.byteLength > 64 * 1024 * 1024)
|
|
9
|
+
throw new Error('USDC: invalid input size');
|
|
10
|
+
const bytes = new Uint8Array(data);
|
|
11
|
+
if (bytes[8] !== 0 || bytes[9] !== 8 || bytes[10] !== 0)
|
|
12
|
+
throw new Error('USDC: initial decoder requires crate version 0.8.0');
|
|
13
|
+
const reader = new DataView(data);
|
|
14
|
+
const table = reader.getBigUint64(16, true);
|
|
15
|
+
if (table > BigInt(data.byteLength - 8)) throw new Error('USDC: invalid section table');
|
|
16
|
+
const offset = Number(table),
|
|
17
|
+
count = reader.getBigUint64(offset, true);
|
|
18
|
+
if (count > 16n || offset + 8 + Number(count) * 32 > data.byteLength)
|
|
19
|
+
throw new Error('USDC: invalid section table length');
|
|
20
|
+
const sectionNames = new Set<string>();
|
|
21
|
+
for (let index = 0; index < Number(count); index++) {
|
|
22
|
+
const entry = offset + 8 + index * 32;
|
|
23
|
+
const nameBytes = bytes.subarray(entry, entry + 16);
|
|
24
|
+
const terminator = nameBytes.indexOf(0);
|
|
25
|
+
const name = new TextDecoder().decode(
|
|
26
|
+
nameBytes.subarray(0, terminator < 0 ? nameBytes.length : terminator)
|
|
27
|
+
);
|
|
28
|
+
if (sectionNames.has(name)) throw new Error('USDC: duplicate section');
|
|
29
|
+
sectionNames.add(name);
|
|
30
|
+
const start = reader.getBigUint64(entry + 16, true),
|
|
31
|
+
size = reader.getBigUint64(entry + 24, true);
|
|
32
|
+
if (start < 88n || start + size > BigInt(data.byteLength))
|
|
33
|
+
throw new Error('USDC: section out of bounds');
|
|
34
|
+
}
|
|
35
|
+
for (const name of ['TOKENS', 'STRINGS', 'FIELDS', 'FIELDSETS', 'PATHS', 'SPECS'])
|
|
36
|
+
if (!sectionNames.has(name)) throw new Error('USDC: missing required section');
|
|
37
|
+
const {specsByPath} = new USDCParser().parseData(data);
|
|
38
|
+
const prims = new Map<string, USDPrim>();
|
|
39
|
+
for (const [path, specification] of Object.entries(specsByPath)) {
|
|
40
|
+
if (specification.specType === 7 && path === '/') continue;
|
|
41
|
+
if (specification.specType === 1 || specification.specType === 8) continue;
|
|
42
|
+
if (specification.specType !== 6 || !/^\/(?:[A-Za-z_][\w:]*\/)*[A-Za-z_][\w:]*$/.test(path))
|
|
43
|
+
throw new Error('USDC: unsupported prim specification');
|
|
44
|
+
const fields = specification.fields;
|
|
45
|
+
const specifier = fields.specifier ?? 0;
|
|
46
|
+
if (specifier !== 0 && specifier !== 1 && specifier !== 2)
|
|
47
|
+
throw new Error('USDC: invalid prim specifier');
|
|
48
|
+
const metadata = convertFields(
|
|
49
|
+
fields,
|
|
50
|
+
new Set(['specifier', 'typeName', 'primChildren', 'properties'])
|
|
51
|
+
);
|
|
52
|
+
prims.set(path, {
|
|
53
|
+
name: path.slice(path.lastIndexOf('/') + 1),
|
|
54
|
+
path,
|
|
55
|
+
sourceUrl: url,
|
|
56
|
+
type: String(fields.typeName || ''),
|
|
57
|
+
specifier: (['def', 'over', 'class'] as const)[specifier],
|
|
58
|
+
attributes: Object.create(null),
|
|
59
|
+
metadata,
|
|
60
|
+
variants: Object.create(null),
|
|
61
|
+
children: []
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
const roots: USDPrim[] = [];
|
|
65
|
+
for (const prim of prims.values()) {
|
|
66
|
+
const parentPath = prim.path.slice(0, prim.path.lastIndexOf('/'));
|
|
67
|
+
if (!parentPath) roots.push(prim);
|
|
68
|
+
else {
|
|
69
|
+
const parent = prims.get(parentPath);
|
|
70
|
+
if (!parent) throw new Error('USDC: missing parent prim');
|
|
71
|
+
parent.children.push(prim);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
orderPrims(roots, specsByPath['/']?.fields.primChildren);
|
|
75
|
+
for (const prim of prims.values())
|
|
76
|
+
orderPrims(prim.children, specsByPath[prim.path]?.fields.primChildren);
|
|
77
|
+
for (const [path, specification] of Object.entries(specsByPath)) {
|
|
78
|
+
if (specification.specType !== 1 && specification.specType !== 8) continue;
|
|
79
|
+
const separator = path.indexOf('.');
|
|
80
|
+
const prim = prims.get(path.slice(0, separator));
|
|
81
|
+
if (separator < 0 || !prim) throw new Error('USDC: property has no owning prim');
|
|
82
|
+
const name = path.slice(separator + 1),
|
|
83
|
+
fields = specification.fields;
|
|
84
|
+
const type = specification.specType === 8 ? 'rel' : String(fields.typeName || '');
|
|
85
|
+
const value = convertAttributeValue(
|
|
86
|
+
specification.specType === 8 ? fields.targetPaths : fields.default,
|
|
87
|
+
type
|
|
88
|
+
);
|
|
89
|
+
const metadata = convertFields(
|
|
90
|
+
fields,
|
|
91
|
+
new Set(['typeName', 'default', 'targetPaths', 'timeSamples'])
|
|
92
|
+
);
|
|
93
|
+
if (fields.timeSamples !== undefined)
|
|
94
|
+
metadata.timeSamples = convertTimeSamples(fields.timeSamples, type);
|
|
95
|
+
prim.attributes[name] = {
|
|
96
|
+
name,
|
|
97
|
+
type,
|
|
98
|
+
value,
|
|
99
|
+
metadata
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
return {
|
|
103
|
+
format: 'usdc',
|
|
104
|
+
url,
|
|
105
|
+
metadata: convertFields(specsByPath['/']?.fields || {}, new Set(['primChildren'])),
|
|
106
|
+
rootPrims: roots,
|
|
107
|
+
layers: []
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** Converts raw field dictionaries while retaining authored values and time samples. */
|
|
112
|
+
function convertFields(
|
|
113
|
+
fields: Record<string, unknown>,
|
|
114
|
+
excluded: Set<string>
|
|
115
|
+
): Record<string, USDValue> {
|
|
116
|
+
const result: Record<string, USDValue> = Object.create(null);
|
|
117
|
+
for (const [name, value] of Object.entries(fields))
|
|
118
|
+
if (!excluded.has(name)) result[name] = convertValue(value);
|
|
119
|
+
return result;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/** Converts decoded typed arrays to the existing renderer-independent USD value contract. */
|
|
123
|
+
function convertValue(value: unknown): USDValue {
|
|
124
|
+
if (value === undefined || value === null) return null;
|
|
125
|
+
if (typeof value === 'number' || typeof value === 'string' || typeof value === 'boolean')
|
|
126
|
+
return value;
|
|
127
|
+
if (Array.isArray(value)) return value.map(convertValue);
|
|
128
|
+
if (ArrayBuffer.isView(value) && !(value instanceof DataView))
|
|
129
|
+
return Array.from(value as unknown as ArrayLike<number>);
|
|
130
|
+
if (typeof value === 'object') return convertFields(value as Record<string, unknown>, new Set());
|
|
131
|
+
throw new Error('USDC: unsupported decoded value');
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** Preserves USDA-compatible asset/path values and vector-array tuple boundaries. */
|
|
135
|
+
function convertAttributeValue(raw: unknown, type: string): USDValue {
|
|
136
|
+
const value = convertValue(raw);
|
|
137
|
+
if (type === 'asset' && typeof value === 'string') return {assetPath: value};
|
|
138
|
+
if (type === 'rel' && Array.isArray(value))
|
|
139
|
+
return value.map(path => (typeof path === 'string' ? {path} : path));
|
|
140
|
+
const tuple =
|
|
141
|
+
/^(?:(?:point|normal|vector|color|texCoord)([234])[fdh]|(?:float|double|half|int)([234]))\[\]$/.exec(
|
|
142
|
+
type
|
|
143
|
+
);
|
|
144
|
+
if (tuple && Array.isArray(value)) {
|
|
145
|
+
const components = Number(tuple[1] || tuple[2]);
|
|
146
|
+
if (value.length % components) throw new Error('USDC: incomplete vector tuple');
|
|
147
|
+
const tuples: USDValue[] = [];
|
|
148
|
+
for (let offset = 0; offset < value.length; offset += components)
|
|
149
|
+
tuples.push(value.slice(offset, offset + components));
|
|
150
|
+
return tuples;
|
|
151
|
+
}
|
|
152
|
+
return value;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Applies authored child-name order while retaining any other decoded specifications. */
|
|
156
|
+
function orderPrims(prims: USDPrim[], names: unknown): void {
|
|
157
|
+
if (names === undefined) return;
|
|
158
|
+
if (!Array.isArray(names)) throw new Error('USDC: invalid primChildren order');
|
|
159
|
+
const remaining = new Map(prims.map(prim => [prim.name, prim]));
|
|
160
|
+
const ordered: USDPrim[] = [];
|
|
161
|
+
for (const name of names) {
|
|
162
|
+
const prim = typeof name === 'string' ? remaining.get(name) : undefined;
|
|
163
|
+
if (!prim) throw new Error('USDC: invalid primChildren reference');
|
|
164
|
+
ordered.push(prim);
|
|
165
|
+
remaining.delete(name as string);
|
|
166
|
+
}
|
|
167
|
+
for (const prim of remaining.values()) ordered.push(prim);
|
|
168
|
+
for (let index = 0; index < ordered.length; index++) prims[index] = ordered[index];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/** Converts every authored sample through the same type-aware path as an attribute default. */
|
|
172
|
+
function convertTimeSamples(raw: unknown, type: string): USDValue {
|
|
173
|
+
if (!raw || typeof raw !== 'object') throw new Error('USDC: invalid time samples');
|
|
174
|
+
const samples = raw as {
|
|
175
|
+
/** Authored sample coordinates. */
|
|
176
|
+
times?: unknown;
|
|
177
|
+
/** Authored sample values before type-aware conversion. */
|
|
178
|
+
values?: unknown;
|
|
179
|
+
};
|
|
180
|
+
const times = convertValue(samples.times);
|
|
181
|
+
if (
|
|
182
|
+
!Array.isArray(times) ||
|
|
183
|
+
!Array.isArray(samples.values) ||
|
|
184
|
+
times.length !== samples.values.length
|
|
185
|
+
)
|
|
186
|
+
throw new Error('USDC: invalid time samples');
|
|
187
|
+
return {times, values: samples.values.map(value => convertAttributeValue(value, type))};
|
|
188
|
+
}
|
|
@@ -12,20 +12,29 @@ export function parseUSDZArchive(arrayBuffer: ArrayBuffer): Map<string, ArrayBuf
|
|
|
12
12
|
const directoryOffset = findCentralDirectory(view);
|
|
13
13
|
const entryCount = view.getUint16(directoryOffset + 10, true);
|
|
14
14
|
let entryOffset = view.getUint32(directoryOffset + 16, true);
|
|
15
|
+
const dataLimit = entryOffset;
|
|
15
16
|
const files = new Map<string, ArrayBuffer>();
|
|
16
17
|
const textDecoder = new TextDecoder();
|
|
18
|
+
let extractedByteLength = 0;
|
|
17
19
|
|
|
18
20
|
for (let entryIndex = 0; entryIndex < entryCount; entryIndex++) {
|
|
21
|
+
if (entryOffset + 46 > directoryOffset) {
|
|
22
|
+
throw new Error('USDZ archive contains a truncated ZIP central-directory entry.');
|
|
23
|
+
}
|
|
19
24
|
if (view.getUint32(entryOffset, true) !== CENTRAL_DIRECTORY_ENTRY) {
|
|
20
25
|
throw new Error('USDZ archive contains an invalid ZIP central-directory entry.');
|
|
21
26
|
}
|
|
22
27
|
|
|
23
28
|
const compression = view.getUint16(entryOffset + 10, true);
|
|
24
29
|
const compressedLength = view.getUint32(entryOffset + 20, true);
|
|
30
|
+
const uncompressedLength = view.getUint32(entryOffset + 24, true);
|
|
25
31
|
const filenameLength = view.getUint16(entryOffset + 28, true);
|
|
26
32
|
const extraLength = view.getUint16(entryOffset + 30, true);
|
|
27
33
|
const commentLength = view.getUint16(entryOffset + 32, true);
|
|
28
34
|
const localOffset = view.getUint32(entryOffset + 42, true);
|
|
35
|
+
if (entryOffset + 46 + filenameLength + extraLength + commentLength > directoryOffset) {
|
|
36
|
+
throw new Error('USDZ archive contains a truncated ZIP filename or extra field.');
|
|
37
|
+
}
|
|
29
38
|
const filename = textDecoder.decode(
|
|
30
39
|
new Uint8Array(arrayBuffer, entryOffset + 46, filenameLength)
|
|
31
40
|
);
|
|
@@ -33,6 +42,9 @@ export function parseUSDZArchive(arrayBuffer: ArrayBuffer): Map<string, ArrayBuf
|
|
|
33
42
|
if (compression !== 0) {
|
|
34
43
|
throw new Error(`USDZ entry "${filename}" is compressed; USDZ requires stored ZIP entries.`);
|
|
35
44
|
}
|
|
45
|
+
if (localOffset + 30 > dataLimit || compressedLength !== uncompressedLength) {
|
|
46
|
+
throw new Error(`USDZ entry "${filename}" has invalid stored ZIP bounds.`);
|
|
47
|
+
}
|
|
36
48
|
if (view.getUint32(localOffset, true) !== LOCAL_FILE_HEADER) {
|
|
37
49
|
throw new Error(`USDZ entry "${filename}" has an invalid local ZIP header.`);
|
|
38
50
|
}
|
|
@@ -40,6 +52,22 @@ export function parseUSDZArchive(arrayBuffer: ArrayBuffer): Map<string, ArrayBuf
|
|
|
40
52
|
const localNameLength = view.getUint16(localOffset + 26, true);
|
|
41
53
|
const localExtraLength = view.getUint16(localOffset + 28, true);
|
|
42
54
|
const contentOffset = localOffset + 30 + localNameLength + localExtraLength;
|
|
55
|
+
if ((view.getUint16(entryOffset + 8, true) | view.getUint16(localOffset + 6, true)) & 1) {
|
|
56
|
+
throw new Error(`USDZ entry "${filename}" is encrypted.`);
|
|
57
|
+
}
|
|
58
|
+
if (
|
|
59
|
+
contentOffset + compressedLength > dataLimit ||
|
|
60
|
+
view.getUint16(localOffset + 8, true) !== 0 ||
|
|
61
|
+
textDecoder.decode(new Uint8Array(arrayBuffer, localOffset + 30, localNameLength)) !==
|
|
62
|
+
filename ||
|
|
63
|
+
files.has(filename)
|
|
64
|
+
) {
|
|
65
|
+
throw new Error(`USDZ entry "${filename}" has invalid or duplicate stored ZIP data.`);
|
|
66
|
+
}
|
|
67
|
+
extractedByteLength += compressedLength;
|
|
68
|
+
if (extractedByteLength > arrayBuffer.byteLength) {
|
|
69
|
+
throw new Error('USDZ archive exceeds its stored data budget.');
|
|
70
|
+
}
|
|
43
71
|
files.set(filename, arrayBuffer.slice(contentOffset, contentOffset + compressedLength));
|
|
44
72
|
entryOffset += 46 + filenameLength + extraLength + commentLength;
|
|
45
73
|
}
|
package/src/usd/lib/usd-types.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
// loaders.gl
|
|
2
1
|
// SPDX-License-Identifier: MIT
|
|
3
2
|
// Copyright (c) vis.gl contributors
|
|
3
|
+
// loaders.gl
|
|
4
4
|
|
|
5
5
|
import type {LoaderOptions} from '@loaders.gl/core/loader-utils';
|
|
6
6
|
|
|
@@ -31,6 +31,8 @@ export type USDValue =
|
|
|
31
31
|
|
|
32
32
|
/** Parsed OpenUSD attribute declaration. */
|
|
33
33
|
export type USDAttribute = {
|
|
34
|
+
/** URL of the layer that authored this attribute, retained through composition. */
|
|
35
|
+
sourceUrl?: string;
|
|
34
36
|
/** Attribute name. */
|
|
35
37
|
name: string;
|
|
36
38
|
/** Authored OpenUSD type name. */
|
|
@@ -73,10 +75,20 @@ export type USDPrim = {
|
|
|
73
75
|
children: USDPrim[];
|
|
74
76
|
};
|
|
75
77
|
|
|
78
|
+
/** Encoded file extracted from a USDZ package, without image decoding or renderer objects. */
|
|
79
|
+
export type USDEmbeddedAsset = {
|
|
80
|
+
/** Original archive-relative filename, including its directories. */
|
|
81
|
+
readonly path: string;
|
|
82
|
+
/** Exact stored bytes, owned by the returned stage rather than the input archive. */
|
|
83
|
+
readonly data: ArrayBuffer;
|
|
84
|
+
/** MIME type hint inferred from a PNG or JPEG filename; other formats omit this field. */
|
|
85
|
+
readonly mimeType?: string;
|
|
86
|
+
};
|
|
87
|
+
|
|
76
88
|
/** Parsed OpenUSD stage. */
|
|
77
89
|
export type USDStage = {
|
|
78
90
|
/** Container format used for the root layer. */
|
|
79
|
-
format: 'usda' | 'usdz';
|
|
91
|
+
format: 'usda' | 'usdc' | 'usdz';
|
|
80
92
|
/** URL of the root layer when available. */
|
|
81
93
|
url?: string;
|
|
82
94
|
/** Root-layer metadata. */
|
|
@@ -85,6 +97,8 @@ export type USDStage = {
|
|
|
85
97
|
rootPrims: USDPrim[];
|
|
86
98
|
/** URLs of all layers used to compose the stage. */
|
|
87
99
|
layers: string[];
|
|
100
|
+
/** USDZ files keyed by resolved package URL; absent for standalone layers. Includes layers and textures. */
|
|
101
|
+
assets?: Record<string, USDEmbeddedAsset>;
|
|
88
102
|
};
|
|
89
103
|
|
|
90
104
|
/** Options for {@link USDLoader}. */
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# USDC decoder source
|
|
2
|
+
|
|
3
|
+
`usdc-parser.js` is adapted from THREE.js r187, commit/tag r187:
|
|
4
|
+
https://github.com/mrdoob/three.js/blob/r187/examples/jsm/loaders/usd/USDCParser.js
|
|
5
|
+
|
|
6
|
+
Copyright © 2010–2026 three.js authors; MIT terms are in THREE-LICENSE.
|
|
7
|
+
This renderer-free source has no imports or package dependencies. It remains
|
|
8
|
+
vendored JavaScript; the adjacent declaration and handwritten parse-usdc.ts
|
|
9
|
+
adapter define the checked TypeScript boundary. Existing libs build/coverage
|
|
10
|
+
policy applies to this vendored decoder; adapter code remains fully instrumented.
|
|
11
|
+
|
|
12
|
+
Local changes bound aggregate allocations, integer precision and seek offsets,
|
|
13
|
+
support bounded dictionary values and reject unsupported list operations/encoding cases, validate LZ4
|
|
14
|
+
copies, use null-prototype dictionaries and correct signed inline integers.
|
|
15
|
+
The adapter restricts crate versions, validates section bounds, and converts the
|
|
16
|
+
supported specifications into USDStage. Broader upstream behavior is not promised.
|
|
17
|
+
|
|
18
|
+
Dictionary decoding uses string-table keys and forward offsets to complete value
|
|
19
|
+
representations, retaining false, empty strings, empty dictionaries and asset paths.
|
|
20
|
+
It rejects duplicate/invalid keys, cycles, nesting beyond 64, and unsupported
|
|
21
|
+
complex dictionary value types. These checks extend the r187 source while
|
|
22
|
+
preserving its MIT attribution; no THREE runtime is imported.
|
|
23
|
+
|
|
24
|
+
The authored-value extension decodes inline half and signed/unsigned 64-bit scalars,
|
|
25
|
+
string arrays, and compressed half/double arrays. Dictionary array support is an
|
|
26
|
+
explicit allowlist; unsupported representations fail before payload decoding.
|
|
27
|
+
Half integer compression uses ties-to-even rounding, and lookup indices are checked.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License
|
|
2
|
+
|
|
3
|
+
Copyright © 2010-2026 three.js authors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
|
13
|
+
all copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Copyright (c) vis.gl contributors
|
|
3
|
+
/** Typed boundary around the pinned, dependency-free THREE r187 crate decoder. */
|
|
4
|
+
export class USDCParser {
|
|
5
|
+
/** Decodes binary crate specifications; unsupported value encodings throw. */
|
|
6
|
+
parseData(buffer: ArrayBuffer): {
|
|
7
|
+
/** Specifications keyed by absolute Sdf path. */
|
|
8
|
+
specsByPath: Record<
|
|
9
|
+
string,
|
|
10
|
+
{
|
|
11
|
+
/** SdfSpecType numeric identifier. */
|
|
12
|
+
specType: number;
|
|
13
|
+
/** Authored field values before USDStage conversion. */
|
|
14
|
+
fields: Record<string, unknown>;
|
|
15
|
+
}
|
|
16
|
+
>;
|
|
17
|
+
};
|
|
18
|
+
}
|