rayzee 11.1.0 → 11.2.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/README.md +29 -7
- package/dist/{ArchiveImporter-Hn35o6Z5.js → ArchiveImporter-eNlu0_6F.js} +882 -829
- package/dist/{BidirectionalIntegrator-CBw8hkeu.js → BidirectionalIntegrator-D1-6UySB.js} +226 -226
- package/dist/{NeuralRetouch-BzNpiVHZ.js → NeuralRetouch-DFGbnmLb.js} +2 -2
- package/dist/{NeuralSuperRes-Bk7bHg2B.js → NeuralSuperRes-BtXvyJQl.js} +2 -2
- package/dist/{PathTracer-72XufKAr.js → PathTracer-BHXGJtPN.js} +2959 -3001
- package/dist/{PhysicalSky-BgjdV88k.js → PhysicalSky-DN6u8UX4.js} +1 -1
- package/dist/{RayzeeRenderer-1QRO97eq.js → RayzeeRenderer-Dw1x8mVL.js} +401 -362
- package/dist/SceneMetadata-D2Sh-0e-.js +110 -0
- package/dist/addons/archives.es.js +1 -1
- package/dist/addons/bidirectional.es.js +1 -1
- package/dist/addons/physical-sky.es.js +1 -1
- package/dist/addons/storage.es.js +1 -1
- package/dist/{identity-DrQsGT2o.js → identity-C1cLD1C0.js} +14 -4
- package/dist/{openStorage-DwcBRUj7.js → openStorage-Fe9BoS1_.js} +1 -1
- package/dist/rayzee-core.es.js +6 -6
- package/dist/rayzee.es.js +828 -828
- package/dist/rayzee.umd.js +95 -94
- package/dist/{sceneCachePolicy-C67jbci-.js → sceneCachePolicy-DA7iGoL4.js} +71 -29
- package/package.json +1 -1
- package/src/EngineDefaults.js +1 -1
- package/src/PathTracerApp.js +1 -0
- package/src/Processor/ArchiveImporter.js +130 -96
- package/src/Processor/ArchiveReader.js +46 -3
- package/src/Processor/AssetLoader.js +26 -1
- package/src/Processor/archiveFormats.js +35 -0
- package/src/RayzeeRenderer.js +43 -12
- package/src/Storage/identity.js +22 -1
- package/src/Storage/index.js +1 -1
- package/src/TSL/MaterialEvaluation.js +4 -3
- package/src/core.js +1 -1
- package/src/index.js +2 -2
- package/dist/archiveFormats-tnAoUL1j.js +0 -94
- /package/dist/{ToneMapGPU-CjU3osDd.js → ToneMapGPU-BOjpul7A.js} +0 -0
|
@@ -124,7 +124,7 @@ function w(e, t, n) {
|
|
|
124
124
|
let r = n ? new e(new SharedArrayBuffer(t * e.BYTES_PER_ELEMENT)) : new e(t);
|
|
125
125
|
return S && r.byteLength > 0 && S(r.byteLength, n), r;
|
|
126
126
|
}
|
|
127
|
-
var
|
|
127
|
+
var ee = class e {
|
|
128
128
|
constructor(e, t, n, r = b, i = !1) {
|
|
129
129
|
let a = t * n.BYTES_PER_ELEMENT, o = Math.max(1, Math.floor(r / a));
|
|
130
130
|
this.recordCount = e, this.lanesPerRecord = t, this.LaneType = n, this.recordsPerChunk = e <= o ? Math.max(e, 1) : o, this.shared = i, this.chunks = [];
|
|
@@ -288,31 +288,73 @@ var T = class e {
|
|
|
288
288
|
let n = (e) => new t(e.buffer, e.byteOffset, e.byteLength / t.BYTES_PER_ELEMENT), r = e.adopt(this.chunks.map((e) => e ? n(e) : void 0), this.recordCount, this.lanesPerRecord, this.recordsPerChunk);
|
|
289
289
|
return r.LaneType = t, r._owner = this, this._views ? this._views.push(r) : (this._weakViews ??= []).push(new WeakRef(r)), r._spilled = this._spilled ??= /* @__PURE__ */ new Set(), r;
|
|
290
290
|
}
|
|
291
|
-
},
|
|
292
|
-
|
|
291
|
+
}, T = {
|
|
292
|
+
Worker: null,
|
|
293
|
+
decodeImage: null
|
|
294
|
+
};
|
|
295
|
+
function E(e) {
|
|
296
|
+
e && Object.assign(T, e);
|
|
297
|
+
}
|
|
298
|
+
function D() {
|
|
299
|
+
return T;
|
|
300
|
+
}
|
|
301
|
+
function O() {
|
|
302
|
+
return typeof navigator < "u" && navigator.hardwareConcurrency || 4;
|
|
303
|
+
}
|
|
304
|
+
function k() {
|
|
305
|
+
return !!T.decodeImage || typeof self < "u" && (typeof createImageBitmap < "u" || typeof document < "u");
|
|
306
|
+
}
|
|
307
|
+
function A() {
|
|
308
|
+
return !!T.Worker || typeof Worker < "u";
|
|
309
|
+
}
|
|
310
|
+
var j = 0;
|
|
311
|
+
async function M(e) {
|
|
312
|
+
if (!T.Worker || j === 0 && globalThis.Worker !== void 0) return e();
|
|
313
|
+
j++ === 0 && (globalThis.Worker = T.Worker);
|
|
314
|
+
try {
|
|
315
|
+
return await e();
|
|
316
|
+
} finally {
|
|
317
|
+
--j === 0 && globalThis.Worker === T.Worker && delete globalThis.Worker;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
function N(e, t) {
|
|
321
|
+
let n = T.Worker;
|
|
322
|
+
if (!n) return new e(t);
|
|
323
|
+
let r = Object.prototype.hasOwnProperty.call(globalThis, "Worker"), i = globalThis.Worker;
|
|
324
|
+
globalThis.Worker = n;
|
|
325
|
+
try {
|
|
326
|
+
return new e(t);
|
|
327
|
+
} finally {
|
|
328
|
+
r ? globalThis.Worker = i : delete globalThis.Worker;
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
332
|
+
//#region src/Storage/CDFCache.js
|
|
333
|
+
var P = 5;
|
|
334
|
+
function F(e, t) {
|
|
293
335
|
e && t && (e.userData.__rayzeeSource = t);
|
|
294
336
|
}
|
|
295
|
-
function
|
|
337
|
+
function I(e) {
|
|
296
338
|
let t = e?.userData?.__rayzeeSource;
|
|
297
339
|
if (!t) return null;
|
|
298
340
|
let { width: n = 0, height: r = 0 } = e.image ?? {};
|
|
299
|
-
return `cdf:${
|
|
341
|
+
return `cdf:${P}:${t}|${n}x${r}|${e.userData.__rayzeeColorSpace ?? "native"}`;
|
|
300
342
|
}
|
|
301
|
-
var
|
|
343
|
+
var L = () => n(i().cacheNamespace)?.area(r.CDF) ?? null, R = [
|
|
302
344
|
"exactConditional",
|
|
303
345
|
"exactMarginal",
|
|
304
346
|
"exactRowGuide",
|
|
305
347
|
"exactMarginalGuide"
|
|
306
|
-
],
|
|
307
|
-
async function
|
|
308
|
-
let t =
|
|
348
|
+
], z = R.map((e) => `${e}.f32`);
|
|
349
|
+
async function B(e) {
|
|
350
|
+
let t = I(e), n = t ? await L()?.open(t) : null;
|
|
309
351
|
if (!n) return null;
|
|
310
352
|
try {
|
|
311
|
-
let e = await Promise.all(
|
|
353
|
+
let e = await Promise.all(z.map((e) => n.file(e)));
|
|
312
354
|
if (e.some((e) => !e)) return null;
|
|
313
355
|
let t = await Promise.all(e.map(async (e) => new Float32Array(await e.arrayBuffer()))), { totalSum: r, width: i, height: a, exactWidth: o, exactHeight: s, radianceIntegral: c } = n.extra;
|
|
314
356
|
return {
|
|
315
|
-
...Object.fromEntries(
|
|
357
|
+
...Object.fromEntries(R.map((e, n) => [e, t[n]])),
|
|
316
358
|
totalSum: r,
|
|
317
359
|
width: i,
|
|
318
360
|
height: a,
|
|
@@ -324,8 +366,8 @@ async function M(e) {
|
|
|
324
366
|
n.release();
|
|
325
367
|
}
|
|
326
368
|
}
|
|
327
|
-
async function
|
|
328
|
-
let { totalSum: n, width: r, height: i, exactWidth: a, exactHeight: o, radianceIntegral: s } = t, c =
|
|
369
|
+
async function te(e, t) {
|
|
370
|
+
let { totalSum: n, width: r, height: i, exactWidth: a, exactHeight: o, radianceIntegral: s } = t, c = R.map((e) => t[e]), l = I(e), u = l ? L() : null;
|
|
329
371
|
if (!u || c.some((e) => !e)) return !1;
|
|
330
372
|
let d = await u.create(l, {
|
|
331
373
|
label: e.name || "environment",
|
|
@@ -333,7 +375,7 @@ async function N(e, t) {
|
|
|
333
375
|
});
|
|
334
376
|
if (!d) return !1;
|
|
335
377
|
try {
|
|
336
|
-
for (let e = 0; e <
|
|
378
|
+
for (let e = 0; e < z.length; e++) await d.write(z[e], c[e]);
|
|
337
379
|
return await d.commit({
|
|
338
380
|
totalSum: n,
|
|
339
381
|
width: r,
|
|
@@ -348,8 +390,8 @@ async function N(e, t) {
|
|
|
348
390
|
}
|
|
349
391
|
//#endregion
|
|
350
392
|
//#region src/Processor/HostMemory.js
|
|
351
|
-
var
|
|
352
|
-
function
|
|
393
|
+
var V = t.FLOATS_PER_TRIANGLE * 4, H = 64, U = .9, W = 64, G = 4, K = 1536 * 1024 * 1024, q = 7040 * 1024 * 1024, J = 9216 * 1024 * 1024, Y = 6e7, X = 8e6;
|
|
394
|
+
function Z(e) {
|
|
353
395
|
if (e > 0) return {
|
|
354
396
|
gb: e,
|
|
355
397
|
assumed: !1
|
|
@@ -363,19 +405,19 @@ function W(e) {
|
|
|
363
405
|
assumed: !0
|
|
364
406
|
};
|
|
365
407
|
}
|
|
366
|
-
var
|
|
367
|
-
function
|
|
408
|
+
var Q = 1.15, ne = 12 * 1024 * 1024 * 1024;
|
|
409
|
+
function re({ triangles: e, placements: t, geometryBytes: n = 0 }) {
|
|
368
410
|
let r = {
|
|
369
|
-
triangles: e *
|
|
370
|
-
bvh: Math.ceil(e *
|
|
411
|
+
triangles: e * V,
|
|
412
|
+
bvh: Math.ceil(e * U) * H,
|
|
371
413
|
geometry: n,
|
|
372
|
-
placements: t *
|
|
373
|
-
orderMaps: e *
|
|
414
|
+
placements: t * W,
|
|
415
|
+
orderMaps: e * G
|
|
374
416
|
};
|
|
375
417
|
return r.total = r.triangles + r.bvh + r.geometry + r.placements + r.orderMaps, r;
|
|
376
418
|
}
|
|
377
|
-
function
|
|
378
|
-
let i = Math.min(e,
|
|
419
|
+
function ie(e, { chunkBytes: t = b, shared: n = !1, allocate: r = null } = {}) {
|
|
420
|
+
let i = Math.min(e, ne), a = r ?? (n ? (e) => new SharedArrayBuffer(e) : (e) => new ArrayBuffer(e)), o = [], s = 0;
|
|
379
421
|
try {
|
|
380
422
|
for (; s < i;) {
|
|
381
423
|
let e = Math.min(t, i - s);
|
|
@@ -390,7 +432,7 @@ function J(e, { chunkBytes: t = b, shared: n = !1, allocate: r = null } = {}) {
|
|
|
390
432
|
capped: e > i
|
|
391
433
|
};
|
|
392
434
|
}
|
|
393
|
-
var
|
|
435
|
+
var ae = class {
|
|
394
436
|
constructor() {
|
|
395
437
|
this.reset();
|
|
396
438
|
}
|
|
@@ -435,9 +477,9 @@ var Y = class {
|
|
|
435
477
|
samples: this.samples.slice()
|
|
436
478
|
};
|
|
437
479
|
}
|
|
438
|
-
},
|
|
439
|
-
function
|
|
440
|
-
return e >= n && t /
|
|
480
|
+
}, oe = "11.2.0", $ = 1e4, se = 15e5;
|
|
481
|
+
function ce(e, t, n = $) {
|
|
482
|
+
return e >= n && t / se < e / 3;
|
|
441
483
|
}
|
|
442
484
|
//#endregion
|
|
443
|
-
export {
|
|
485
|
+
export { g as A, M as C, C as D, x as E, o as F, _ as I, c as L, m as M, u as N, v as O, s as P, l as R, A as S, b as T, E as _, ae as a, O as b, q as c, Z as d, re as f, F as g, te as h, J as i, h as j, y as k, X as l, B as m, ce as n, K as o, ie as p, oe as r, Q as s, $ as t, Y as u, N as v, ee as w, k as x, D as y };
|
package/package.json
CHANGED
package/src/EngineDefaults.js
CHANGED
|
@@ -77,7 +77,7 @@ export const ENGINE_DEFAULTS = deepFreeze( {
|
|
|
77
77
|
|
|
78
78
|
// Read when a model loads
|
|
79
79
|
maxTextureSize: 4096, // longest edge of a material texture; clamped to the hardware ceiling
|
|
80
|
-
areaLightIntensityScale:
|
|
80
|
+
areaLightIntensityScale: 1, // power of a glTF model's placeholder area lights (RectAreaLight extras)
|
|
81
81
|
|
|
82
82
|
// Debug
|
|
83
83
|
visMode: 0, // a debug view (TSL/Debugger.js); 0 renders normally
|
package/src/PathTracerApp.js
CHANGED
|
@@ -12,11 +12,11 @@ import { HDRLoader } from 'three/addons/loaders/HDRLoader.js';
|
|
|
12
12
|
import { EXRLoader } from 'three/addons/loaders/EXRLoader.js';
|
|
13
13
|
import { zipSync, strFromU8 } from 'three/addons/libs/fflate.module.js';
|
|
14
14
|
import {
|
|
15
|
-
detectArchiveKind, readTarGz, elementFilter, listArchiveElements, openTar, indexTarHeaders } from './ArchiveReader.js';
|
|
15
|
+
detectArchiveKind, readTarGz, elementFilter, listArchiveElements, openTar, indexTarHeaders, openFolder } from './ArchiveReader.js';
|
|
16
16
|
import { openZip, readZipDirectory } from './ZipReader.js';
|
|
17
17
|
import { unpackTarGz, loadTarIndex, saveTarIndex } from './ArchiveCache.js';
|
|
18
18
|
import { setEnvironmentSource } from '../Storage/CDFCache.js';
|
|
19
|
-
import { fileIdentity, identityKey, sampleHash } from '../Storage/identity.js';
|
|
19
|
+
import { fileIdentity, folderIdentity, identityKey, sampleHash } from '../Storage/identity.js';
|
|
20
20
|
import { ENGINE_AREAS } from '../Storage/areas.js';
|
|
21
21
|
import { encodeSceneGraph, writeSceneGraph, decodeSceneGraph, SceneGraphUnsupported, SCENE_GRAPH_FORMAT, ARCHIVE_PATH, ARCHIVE_LOADER } from '../Storage/SceneGraphCodec.js';
|
|
22
22
|
import { worthStoring } from '../Storage/sceneCachePolicy.js';
|
|
@@ -27,12 +27,40 @@ import { pfmTexture } from './PBRT/PFM.js';
|
|
|
27
27
|
import { extractSceneMetadata } from './SceneMetadata.js';
|
|
28
28
|
import { ISSUE_CODES, ISSUE_SEVERITY } from '../EngineIssues.js';
|
|
29
29
|
import { ARCHIVE_FORMATS } from './archiveFormats.js';
|
|
30
|
+
import { withHostWorker } from '../Platform.js';
|
|
30
31
|
|
|
31
32
|
// Loose USD layers inside a ZIP compose into one scene; these pick out the
|
|
32
33
|
// layers and the image assets they reference.
|
|
33
34
|
const USD_LAYER_RE = /\.(usd|usda|usdc)$/i;
|
|
34
35
|
const USD_IMAGE_RE = /\.(png|jpg|jpeg|avif)$/i;
|
|
35
36
|
const MTL_TEXTURE_TIMEOUT_MS = 30000;
|
|
37
|
+
const MAIN_MODEL_FILES = [ 'scene.gltf', 'scene.glb', 'model.gltf', 'model.glb', 'main.gltf', 'main.glb', 'asset.gltf', 'asset.glb' ];
|
|
38
|
+
|
|
39
|
+
// An entry is bytes already read, or a Blob read only when a loader asks for it.
|
|
40
|
+
const asBlob = entry => ( entry instanceof Blob ? entry : new Blob( [ entry ], { type: 'application/octet-stream' } ) );
|
|
41
|
+
const bytesOf = async entry => ( entry instanceof Blob ? new Uint8Array( await entry.arrayBuffer() ) : entry );
|
|
42
|
+
const textOf = async entry => ( entry instanceof Blob ? entry.text() : strFromU8( entry ) );
|
|
43
|
+
const joinPath = ( dir, ref ) => {
|
|
44
|
+
|
|
45
|
+
const out = [];
|
|
46
|
+
for ( const part of `${dir}/${ref}`.split( '/' ) ) {
|
|
47
|
+
|
|
48
|
+
if ( part === '..' ) out.pop();
|
|
49
|
+
else if ( part && part !== '.' ) out.push( part );
|
|
50
|
+
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
return out.join( '/' );
|
|
54
|
+
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const bufferOf = async entry => {
|
|
58
|
+
|
|
59
|
+
const bytes = await bytesOf( entry );
|
|
60
|
+
return bytes.byteOffset === 0 && bytes.byteLength === bytes.buffer.byteLength ? bytes.buffer : bytes.slice().buffer;
|
|
61
|
+
|
|
62
|
+
};
|
|
63
|
+
|
|
36
64
|
/**
|
|
37
65
|
* Unpacked size past which a multi-part scene archive asks which parts to load rather than
|
|
38
66
|
* taking all of them. Not a hard limit — picking every part is a valid answer.
|
|
@@ -227,6 +255,29 @@ export class ArchiveImporter {
|
|
|
227
255
|
|
|
228
256
|
}
|
|
229
257
|
|
|
258
|
+
/**
|
|
259
|
+
* Loads a folder as it would the same folder zipped — a glTF with its .bin and textures, an OBJ with its
|
|
260
|
+
* materials, a pbrt scene — reading each file only when the scene asks for it.
|
|
261
|
+
* @param {{name: string, files: Array<{path: string, file: Blob}>}} folder - from `localFolder`
|
|
262
|
+
* @param {object} [options] - `element`, `pbrtEntry`, `promptBytes` and the pbrt options, as loadArchiveFromFile
|
|
263
|
+
*/
|
|
264
|
+
async loadFolder( folder, { pbrtEntry = null, element = null, promptBytes, ...pbrt } = {} ) {
|
|
265
|
+
|
|
266
|
+
const chosen = ( Array.isArray( element ) ? element : [ element ] ).filter( Boolean );
|
|
267
|
+
const source = openFolder( folder, { filter: chosen.length ? elementFilter( chosen ) : null } );
|
|
268
|
+
const totalBytes = source.listing.reduce( ( n, e ) => n + e.size, 0 );
|
|
269
|
+
if ( chosen.length === 0 ) this._requireElementChoice( folder.name, source.listing, totalBytes, promptBytes );
|
|
270
|
+
|
|
271
|
+
console.info(
|
|
272
|
+
`Folder "${folder.name}": ${source.listing.length} files, ${( totalBytes / 1e9 ).toFixed( 1 )} GB` +
|
|
273
|
+
( chosen.length ? `, ${chosen.length} part${chosen.length > 1 ? 's' : ''} selected.` : '.' )
|
|
274
|
+
);
|
|
275
|
+
|
|
276
|
+
const archiveId = this.loader.storage ? identityKey( await folderIdentity( folder ) ) : null;
|
|
277
|
+
return await this._loadSource( source, folder.name, { element, pbrtEntry, pbrt, archiveId, kind: 'folder' } );
|
|
278
|
+
|
|
279
|
+
}
|
|
280
|
+
|
|
230
281
|
async _loadNonPBRTArchive( entries, filename ) {
|
|
231
282
|
|
|
232
283
|
const result = await this.processObjMtlPairsInZip( entries, filename );
|
|
@@ -243,6 +294,13 @@ export class ArchiveImporter {
|
|
|
243
294
|
async _loadSeekable( file, filename, { element, promptBytes, pbrtEntry, pbrt, index = null, zip = false, archiveId = null } ) {
|
|
244
295
|
|
|
245
296
|
const source = await this._openSeekableArchive( file, filename, element, promptBytes, { index, zip } );
|
|
297
|
+
return await this._loadSource( source, filename, { element, pbrtEntry, pbrt, archiveId } );
|
|
298
|
+
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/** Loads what an opened archive or folder holds. @private */
|
|
302
|
+
async _loadSource( source, filename, { element, pbrtEntry, pbrt, archiveId, kind = 'archive' } ) {
|
|
303
|
+
|
|
246
304
|
source.archiveId = archiveId;
|
|
247
305
|
source.elements = ( Array.isArray( element ) ? element : [ element ] ).filter( Boolean );
|
|
248
306
|
if ( source.listing.some( e => e.path.toLowerCase().endsWith( '.pbrt' ) ) ) {
|
|
@@ -251,10 +309,10 @@ export class ArchiveImporter {
|
|
|
251
309
|
|
|
252
310
|
}
|
|
253
311
|
|
|
254
|
-
|
|
255
|
-
for ( const e of source.listing ) if ( e.offset !== undefined ) source.entries[ e.path ]
|
|
256
|
-
const result = await this._loadNonPBRTArchive(
|
|
257
|
-
this.loader._sourceKey = this.loader._keyed(
|
|
312
|
+
const entries = Object.create( null );
|
|
313
|
+
for ( const e of source.listing ) if ( e.offset !== undefined ) entries[ e.path ] = source.entries[ e.path ] ?? await source.slice( e.path );
|
|
314
|
+
const result = await this._loadNonPBRTArchive( entries, filename );
|
|
315
|
+
this.loader._sourceKey = this.loader._keyed( kind, archiveId );
|
|
258
316
|
return result;
|
|
259
317
|
|
|
260
318
|
}
|
|
@@ -691,7 +749,7 @@ export class ArchiveImporter {
|
|
|
691
749
|
updateLoading( { isLoading: true, status: 'Processing PBRT geometry...', progress: 10 } );
|
|
692
750
|
await this.loader.onModelLoad( this.loader.targetModel );
|
|
693
751
|
|
|
694
|
-
this.loader.dispatchEvent( { type: 'load', model: group, filename: `${loadedEntry} (from
|
|
752
|
+
this.loader.dispatchEvent( { type: 'load', model: group, filename: `${loadedEntry} (from ${filename})` } );
|
|
695
753
|
return group;
|
|
696
754
|
|
|
697
755
|
}
|
|
@@ -833,42 +891,38 @@ export class ArchiveImporter {
|
|
|
833
891
|
|
|
834
892
|
}
|
|
835
893
|
|
|
836
|
-
async findAndLoadModelFromZip( zip ) {
|
|
894
|
+
async findAndLoadModelFromZip( zip, filename = 'the ZIP archive' ) {
|
|
837
895
|
|
|
838
|
-
const
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
for ( const mainFile of mainModelFiles ) {
|
|
896
|
+
const paths = Object.keys( zip );
|
|
897
|
+
const top = paths[ 0 ]?.split( '/' )[ 0 ];
|
|
898
|
+
const root = top !== undefined && paths.every( p => p.startsWith( top + '/' ) ) ? top + '/' : '';
|
|
899
|
+
const models = paths.filter( p => p.split( '.' ).pop().toLowerCase() === 'obj' || this.loader.getFileFormat( p )?.type === 'model' );
|
|
900
|
+
if ( models.length === 0 ) throw new Error( `No supported model files found in ${filename}` );
|
|
844
901
|
|
|
845
|
-
|
|
902
|
+
// Shallowest first, glTF before other formats at the same depth.
|
|
903
|
+
const rank = p => p.split( '/' ).length * 2 + ( /\.(gltf|glb)$/i.test( p ) ? 0 : 1 );
|
|
904
|
+
const main = MAIN_MODEL_FILES.map( name => root + name ).find( p => zip[ p ] );
|
|
905
|
+
const path = main ?? models.reduce( ( best, p ) => ( rank( p ) < rank( best ) ? p : best ) );
|
|
846
906
|
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
}
|
|
907
|
+
// A loose layer is only one slice of a USD scene — hand the whole
|
|
908
|
+
// archive over so its references and payloads can resolve.
|
|
909
|
+
if ( ! main && USD_LAYER_RE.test( path ) ) return await this.loadUSDHierarchyFromZip( zip );
|
|
852
910
|
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
for ( const path in zip ) {
|
|
856
|
-
|
|
857
|
-
const extension = path.split( '.' ).pop().toLowerCase();
|
|
858
|
-
if ( extension === 'obj' || this.loader.getFileFormat( path )?.type === 'model' ) {
|
|
859
|
-
|
|
860
|
-
// A loose layer is only one slice of a USD scene — hand the whole
|
|
861
|
-
// archive over so its references and payloads can resolve.
|
|
862
|
-
if ( USD_LAYER_RE.test( path ) ) return await this.loadUSDHierarchyFromZip( zip );
|
|
863
|
-
|
|
864
|
-
console.log( `Loading model file from ZIP: ${path}` );
|
|
865
|
-
return await this.loadModelFromZipEntry( zip[ path ], path, extension, zip );
|
|
911
|
+
const others = models.filter( p => p !== path && ! USD_LAYER_RE.test( p ) );
|
|
912
|
+
if ( others.length > 0 ) {
|
|
866
913
|
|
|
867
|
-
}
|
|
914
|
+
console.warn( `${filename} holds ${others.length + 1} models; loaded "${path}". Others: ${others.join( ', ' )}` );
|
|
915
|
+
this.loader._issues?.record(
|
|
916
|
+
ISSUE_CODES.ASSET_AMBIGUOUS_ENTRY,
|
|
917
|
+
`${filename} holds ${others.length + 1} models; loaded "${path}"`,
|
|
918
|
+
{ loaded: path, alternatives: others.slice( 0, 50 ) },
|
|
919
|
+
ISSUE_SEVERITY.WARNING
|
|
920
|
+
);
|
|
868
921
|
|
|
869
922
|
}
|
|
870
923
|
|
|
871
|
-
|
|
924
|
+
console.log( `Loading model file from ${filename}: ${path}` );
|
|
925
|
+
return await this.loadModelFromZipEntry( zip[ path ], path, path.split( '.' ).pop().toLowerCase(), zip, filename );
|
|
872
926
|
|
|
873
927
|
}
|
|
874
928
|
|
|
@@ -883,11 +937,11 @@ export class ArchiveImporter {
|
|
|
883
937
|
const root = ArchiveImporter._pickUSDRootLayer( layers );
|
|
884
938
|
console.log( `Loading USD scene from ZIP: ${root} (${layers.length} layers)` );
|
|
885
939
|
|
|
886
|
-
const packed = { [ root ]: [ zip[ root ], { level: 0 } ] };
|
|
940
|
+
const packed = { [ root ]: [ await bytesOf( zip[ root ] ), { level: 0 } ] };
|
|
887
941
|
for ( const name of Object.keys( zip ) ) {
|
|
888
942
|
|
|
889
943
|
if ( name === root ) continue;
|
|
890
|
-
if ( USD_LAYER_RE.test( name ) || USD_IMAGE_RE.test( name ) ) packed[ name ] = [ zip[ name ], { level: 0 } ];
|
|
944
|
+
if ( USD_LAYER_RE.test( name ) || USD_IMAGE_RE.test( name ) ) packed[ name ] = [ await bytesOf( zip[ name ] ), { level: 0 } ];
|
|
891
945
|
|
|
892
946
|
}
|
|
893
947
|
|
|
@@ -914,13 +968,11 @@ export class ArchiveImporter {
|
|
|
914
968
|
|
|
915
969
|
}
|
|
916
970
|
|
|
917
|
-
async loadModelFromZipEntry( fileContent, filePath, extension, zipContents ) {
|
|
971
|
+
async loadModelFromZipEntry( fileContent, filePath, extension, zipContents, from = 'ZIP' ) {
|
|
918
972
|
|
|
919
973
|
try {
|
|
920
974
|
|
|
921
|
-
updateLoading( { isLoading: true, status: `Processing ${extension.toUpperCase()} from
|
|
922
|
-
const blob = new Blob( [ fileContent.buffer ], { type: 'application/octet-stream' } );
|
|
923
|
-
const blobUrl = URL.createObjectURL( blob );
|
|
975
|
+
updateLoading( { isLoading: true, status: `Processing ${extension.toUpperCase()} from ${from}...`, progress: 5 } );
|
|
924
976
|
let result;
|
|
925
977
|
|
|
926
978
|
switch ( extension ) {
|
|
@@ -937,17 +989,16 @@ export class ArchiveImporter {
|
|
|
937
989
|
|
|
938
990
|
}
|
|
939
991
|
|
|
940
|
-
URL.revokeObjectURL( blobUrl );
|
|
941
992
|
this.loader.dispatchEvent( {
|
|
942
993
|
type: 'load',
|
|
943
994
|
model: this.loader.targetModel,
|
|
944
|
-
filename: `${filePath} (from
|
|
995
|
+
filename: `${filePath} (from ${from})`
|
|
945
996
|
} );
|
|
946
997
|
return result;
|
|
947
998
|
|
|
948
999
|
} catch ( error ) {
|
|
949
1000
|
|
|
950
|
-
console.error( `Error loading ${extension} from
|
|
1001
|
+
console.error( `Error loading ${extension} from ${from}:`, error );
|
|
951
1002
|
this.loader.dispatchEvent( { type: 'error', message: error.message, filename: filePath } );
|
|
952
1003
|
throw error;
|
|
953
1004
|
|
|
@@ -959,7 +1010,7 @@ export class ArchiveImporter {
|
|
|
959
1010
|
|
|
960
1011
|
if ( extension === 'gltf' ) {
|
|
961
1012
|
|
|
962
|
-
const gltfContent =
|
|
1013
|
+
const gltfContent = await textOf( fileContent );
|
|
963
1014
|
const manager = new LoadingManager();
|
|
964
1015
|
const gltfDir = filePath.split( '/' ).slice( 0, - 1 ).join( '/' );
|
|
965
1016
|
|
|
@@ -969,32 +1020,13 @@ export class ArchiveImporter {
|
|
|
969
1020
|
|
|
970
1021
|
try {
|
|
971
1022
|
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
this.loader._throwDeferred();
|
|
980
|
-
|
|
981
|
-
} catch ( error ) {
|
|
982
|
-
|
|
983
|
-
reject( error );
|
|
984
|
-
return;
|
|
985
|
-
|
|
986
|
-
}
|
|
987
|
-
|
|
988
|
-
this.loader.releaseTargetModel();
|
|
989
|
-
this.loader.targetModel = gltf.scene;
|
|
990
|
-
this.loader.sceneMetadata = extractSceneMetadata( gltf );
|
|
991
|
-
this.loader.onModelLoad( this.loader.targetModel ).then( () => resolve( gltf ) );
|
|
992
|
-
|
|
993
|
-
},
|
|
994
|
-
error => reject( error )
|
|
995
|
-
);
|
|
996
|
-
|
|
997
|
-
} );
|
|
1023
|
+
const gltf = await withHostWorker( () => new Promise( ( resolve, reject ) => loader.parse( gltfContent, '', resolve, reject ) ) );
|
|
1024
|
+
this.loader._throwDeferred();
|
|
1025
|
+
this.loader.releaseTargetModel();
|
|
1026
|
+
this.loader.targetModel = gltf.scene;
|
|
1027
|
+
this.loader.sceneMetadata = extractSceneMetadata( gltf );
|
|
1028
|
+
await this.loader.onModelLoad( this.loader.targetModel );
|
|
1029
|
+
return gltf;
|
|
998
1030
|
|
|
999
1031
|
} finally {
|
|
1000
1032
|
|
|
@@ -1004,7 +1036,7 @@ export class ArchiveImporter {
|
|
|
1004
1036
|
|
|
1005
1037
|
} else {
|
|
1006
1038
|
|
|
1007
|
-
return await this.loader.loadGLBFromArrayBuffer( fileContent
|
|
1039
|
+
return await this.loader.loadGLBFromArrayBuffer( await bufferOf( fileContent ), filePath );
|
|
1008
1040
|
|
|
1009
1041
|
}
|
|
1010
1042
|
|
|
@@ -1012,7 +1044,7 @@ export class ArchiveImporter {
|
|
|
1012
1044
|
|
|
1013
1045
|
async handleObjFromZip( fileContent, filePath, zipContents ) {
|
|
1014
1046
|
|
|
1015
|
-
const objContent =
|
|
1047
|
+
const objContent = await textOf( fileContent );
|
|
1016
1048
|
const mtlMatch = objContent.match( /mtllib\s+([^\s]+)/ );
|
|
1017
1049
|
let materials = null;
|
|
1018
1050
|
|
|
@@ -1100,7 +1132,7 @@ export class ArchiveImporter {
|
|
|
1100
1132
|
if ( zipContents[ path ] ) {
|
|
1101
1133
|
|
|
1102
1134
|
const { MTLLoader } = await import( 'three/examples/jsm/loaders/MTLLoader.js' );
|
|
1103
|
-
const mtlContent =
|
|
1135
|
+
const mtlContent = await textOf( zipContents[ path ] );
|
|
1104
1136
|
const manager = new LoadingManager();
|
|
1105
1137
|
manager.setURLModifier( url => this.resolveZipResource( url, objDir, zipContents ) );
|
|
1106
1138
|
const mtlLoader = new MTLLoader( manager );
|
|
@@ -1118,25 +1150,30 @@ export class ArchiveImporter {
|
|
|
1118
1150
|
|
|
1119
1151
|
resolveZipResource( url, baseDir, zipContents ) {
|
|
1120
1152
|
|
|
1121
|
-
|
|
1122
|
-
const possiblePaths = [
|
|
1123
|
-
normalizedUrl,
|
|
1124
|
-
`${baseDir}/${normalizedUrl}`,
|
|
1125
|
-
normalizedUrl.split( '/' ).pop()
|
|
1126
|
-
];
|
|
1153
|
+
if ( /^(data|blob|https?):/i.test( url ) ) return url;
|
|
1127
1154
|
|
|
1128
|
-
|
|
1155
|
+
const raw = url.replace( /^\.\/|^\//, '' );
|
|
1156
|
+
let decoded = raw;
|
|
1157
|
+
try {
|
|
1129
1158
|
|
|
1130
|
-
|
|
1159
|
+
decoded = decodeURIComponent( raw );
|
|
1131
1160
|
|
|
1132
|
-
|
|
1133
|
-
return URL.createObjectURL( fileBlob );
|
|
1161
|
+
} catch {
|
|
1134
1162
|
|
|
1135
|
-
|
|
1163
|
+
// a literal '%'
|
|
1164
|
+
|
|
1165
|
+
}
|
|
1166
|
+
|
|
1167
|
+
for ( const ref of new Set( [ raw, decoded ] ) ) {
|
|
1168
|
+
|
|
1169
|
+
const name = ref.split( '/' ).pop();
|
|
1170
|
+
const path = [ joinPath( baseDir, ref ), ref, name ].find( p => zipContents[ p ] )
|
|
1171
|
+
?? ( name ? Object.keys( zipContents ).find( p => p.endsWith( `/${name}` ) ) : undefined );
|
|
1172
|
+
if ( path ) return URL.createObjectURL( asBlob( zipContents[ path ] ) );
|
|
1136
1173
|
|
|
1137
1174
|
}
|
|
1138
1175
|
|
|
1139
|
-
console.warn( `Resource not found in
|
|
1176
|
+
console.warn( `Resource not found in the archive: ${url}` );
|
|
1140
1177
|
return url;
|
|
1141
1178
|
|
|
1142
1179
|
}
|
|
@@ -1151,13 +1188,13 @@ export class ArchiveImporter {
|
|
|
1151
1188
|
const mtlDir = mtlFile.path.split( '/' ).slice( 0, - 1 ).join( '/' );
|
|
1152
1189
|
|
|
1153
1190
|
manager.setURLModifier( url => this.resolveTextureInZip( url, objDir, mtlDir, mtlFile, zip, createdUrls ) );
|
|
1154
|
-
const mtlContent = this.prepareFixedMtlContent( mtlFile );
|
|
1191
|
+
const mtlContent = await this.prepareFixedMtlContent( mtlFile );
|
|
1155
1192
|
const materials = new MTLLoader( manager ).parse( mtlContent, mtlDir );
|
|
1156
1193
|
await this.preloadMtlTextures( materials );
|
|
1157
1194
|
|
|
1158
1195
|
const objLoader = new OBJLoader( manager );
|
|
1159
1196
|
objLoader.setMaterials( materials );
|
|
1160
|
-
const objContent =
|
|
1197
|
+
const objContent = await textOf( objFile.content );
|
|
1161
1198
|
const object = objLoader.parse( objContent );
|
|
1162
1199
|
|
|
1163
1200
|
this.loader.releaseTargetModel();
|
|
@@ -1175,9 +1212,9 @@ export class ArchiveImporter {
|
|
|
1175
1212
|
|
|
1176
1213
|
}
|
|
1177
1214
|
|
|
1178
|
-
prepareFixedMtlContent( mtlFile ) {
|
|
1215
|
+
async prepareFixedMtlContent( mtlFile ) {
|
|
1179
1216
|
|
|
1180
|
-
const mtlContent =
|
|
1217
|
+
const mtlContent = await textOf( mtlFile.content );
|
|
1181
1218
|
return mtlContent
|
|
1182
1219
|
.replace( new RegExp( `${mtlFile.path.split( '/' ).pop()}\\s+`, 'g' ), ' ' )
|
|
1183
1220
|
.replace( /([a-zA-Z_]+)([\\/])/g, '$1 $2' );
|
|
@@ -1210,8 +1247,7 @@ export class ArchiveImporter {
|
|
|
1210
1247
|
|
|
1211
1248
|
if ( zip[ location ] ) {
|
|
1212
1249
|
|
|
1213
|
-
const
|
|
1214
|
-
const blobUrl = URL.createObjectURL( blob );
|
|
1250
|
+
const blobUrl = URL.createObjectURL( asBlob( zip[ location ] ) );
|
|
1215
1251
|
createdUrls.push( blobUrl );
|
|
1216
1252
|
return blobUrl;
|
|
1217
1253
|
|
|
@@ -1231,8 +1267,7 @@ export class ArchiveImporter {
|
|
|
1231
1267
|
|
|
1232
1268
|
if ( zipPath.endsWith( textureFilename ) ) {
|
|
1233
1269
|
|
|
1234
|
-
const
|
|
1235
|
-
const blobUrl = URL.createObjectURL( blob );
|
|
1270
|
+
const blobUrl = URL.createObjectURL( asBlob( zip[ zipPath ] ) );
|
|
1236
1271
|
createdUrls.push( blobUrl );
|
|
1237
1272
|
return blobUrl;
|
|
1238
1273
|
|
|
@@ -1247,8 +1282,7 @@ export class ArchiveImporter {
|
|
|
1247
1282
|
const zipFilename = zipPath.split( '/' ).pop();
|
|
1248
1283
|
if ( zipFilename.includes( textureFilename ) || textureFilename.includes( zipFilename ) ) {
|
|
1249
1284
|
|
|
1250
|
-
const
|
|
1251
|
-
const blobUrl = URL.createObjectURL( blob );
|
|
1285
|
+
const blobUrl = URL.createObjectURL( asBlob( zip[ zipPath ] ) );
|
|
1252
1286
|
createdUrls.push( blobUrl );
|
|
1253
1287
|
return blobUrl;
|
|
1254
1288
|
|