|
|
@@ -90,6 +90,21 @@ class USDLoader extends Loader {
|
|
|
|
|
|
const usda = new USDAParser();
|
|
|
const usdc = new USDCParser();
|
|
|
+ const textDecoder = new TextDecoder();
|
|
|
+
|
|
|
+ function toArrayBuffer( data ) {
|
|
|
+
|
|
|
+ if ( data instanceof ArrayBuffer ) return data;
|
|
|
+
|
|
|
+ if ( data.byteOffset === 0 && data.byteLength === data.buffer.byteLength ) {
|
|
|
+
|
|
|
+ return data.buffer;
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ return data.buffer.slice( data.byteOffset, data.byteOffset + data.byteLength );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
function getLowercaseExtension( filename ) {
|
|
|
|
|
|
@@ -109,39 +124,26 @@ class USDLoader extends Loader {
|
|
|
|
|
|
for ( const filename in zip ) {
|
|
|
|
|
|
+ const fileBytes = zip[ filename ];
|
|
|
const ext = getLowercaseExtension( filename );
|
|
|
+
|
|
|
if ( ext === 'png' || ext === 'jpg' || ext === 'jpeg' || ext === 'avif' ) {
|
|
|
|
|
|
- const type = ext === 'png' ? 'image/png' : ext === 'avif' ? 'image/avif' : 'image/jpeg';
|
|
|
- const blob = new Blob( [ zip[ filename ] ], { type } );
|
|
|
- data[ filename ] = URL.createObjectURL( blob );
|
|
|
+ // Keep raw image bytes and create object URLs lazily in USDComposer.
|
|
|
+ data[ filename ] = fileBytes;
|
|
|
+ continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
- }
|
|
|
+ if ( ext !== 'usd' && ext !== 'usda' && ext !== 'usdc' ) continue;
|
|
|
|
|
|
- for ( const filename in zip ) {
|
|
|
-
|
|
|
- const ext = getLowercaseExtension( filename );
|
|
|
- if ( ext === 'usd' || ext === 'usda' || ext === 'usdc' ) {
|
|
|
-
|
|
|
- if ( isCrateFile( zip[ filename ] ) ) {
|
|
|
-
|
|
|
- // Store parsed data (specsByPath) for on-demand composition
|
|
|
- const parsedData = usdc.parseData( zip[ filename ].buffer );
|
|
|
- data[ filename ] = parsedData;
|
|
|
- // Store raw buffer for re-parsing with variant selections
|
|
|
- data[ filename + ':buffer' ] = zip[ filename ].buffer;
|
|
|
+ if ( isCrateFile( fileBytes ) ) {
|
|
|
|
|
|
- } else {
|
|
|
+ data[ filename ] = usdc.parseData( toArrayBuffer( fileBytes ) );
|
|
|
|
|
|
- const text = new TextDecoder().decode( zip[ filename ] );
|
|
|
- // Store parsed data (specsByPath) for on-demand composition
|
|
|
- data[ filename ] = usda.parseData( text );
|
|
|
- // Store raw text for re-parsing with variant selections
|
|
|
- data[ filename + ':text' ] = text;
|
|
|
+ } else {
|
|
|
|
|
|
- }
|
|
|
+ data[ filename ] = usda.parseData( textDecoder.decode( fileBytes ) );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -154,10 +156,9 @@ class USDLoader extends Loader {
|
|
|
function isCrateFile( buffer ) {
|
|
|
|
|
|
const crateHeader = new Uint8Array( [ 0x50, 0x58, 0x52, 0x2D, 0x55, 0x53, 0x44, 0x43 ] ); // PXR-USDC
|
|
|
+ const view = buffer instanceof Uint8Array ? buffer : new Uint8Array( buffer );
|
|
|
|
|
|
- if ( buffer.byteLength < crateHeader.length ) return false;
|
|
|
-
|
|
|
- const view = new Uint8Array( buffer, 0, crateHeader.length );
|
|
|
+ if ( view.byteLength < crateHeader.length ) return false;
|
|
|
|
|
|
for ( let i = 0; i < crateHeader.length; i ++ ) {
|
|
|
|
|
|
@@ -172,7 +173,7 @@ class USDLoader extends Loader {
|
|
|
function findUSD( zip ) {
|
|
|
|
|
|
const fileNames = Object.keys( zip );
|
|
|
- if ( fileNames.length < 1 ) return { file: undefined, basePath: '' };
|
|
|
+ if ( fileNames.length < 1 ) return { file: undefined, filename: '', basePath: '' };
|
|
|
|
|
|
const firstFileName = fileNames[ 0 ];
|
|
|
const ext = getLowercaseExtension( firstFileName );
|
|
|
@@ -183,7 +184,7 @@ class USDLoader extends Loader {
|
|
|
|
|
|
// Per AOUSD core spec v1.0.1 section 16.4.1.2, the first ZIP entry is the root layer.
|
|
|
// ASCII files can end in either .usda or .usd.
|
|
|
- if ( ext === 'usda' ) return { file: zip[ firstFileName ], basePath };
|
|
|
+ if ( ext === 'usda' ) return { file: zip[ firstFileName ], filename: firstFileName, basePath };
|
|
|
|
|
|
if ( ext === 'usdc' ) {
|
|
|
|
|
|
@@ -194,7 +195,7 @@ class USDLoader extends Loader {
|
|
|
// If this is not a crate file, we assume it is a plain USDA file.
|
|
|
if ( ! isCrateFile( zip[ firstFileName ] ) ) {
|
|
|
|
|
|
- return { file: zip[ firstFileName ], basePath };
|
|
|
+ return { file: zip[ firstFileName ], filename: firstFileName, basePath };
|
|
|
|
|
|
} else {
|
|
|
|
|
|
@@ -206,11 +207,11 @@ class USDLoader extends Loader {
|
|
|
|
|
|
if ( isCrate ) {
|
|
|
|
|
|
- return { file: zip[ firstFileName ], basePath };
|
|
|
+ return { file: zip[ firstFileName ], filename: firstFileName, basePath };
|
|
|
|
|
|
}
|
|
|
|
|
|
- return { file: undefined, basePath: '' };
|
|
|
+ return { file: undefined, filename: '', basePath: '' };
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -231,7 +232,7 @@ class USDLoader extends Loader {
|
|
|
if ( isCrateFile( buffer ) ) {
|
|
|
|
|
|
const composer = new USDComposer( scope.manager );
|
|
|
- const data = usdc.parseData( buffer );
|
|
|
+ const data = usdc.parseData( toArrayBuffer( buffer ) );
|
|
|
return composer.compose( data, {} );
|
|
|
|
|
|
}
|
|
|
@@ -244,7 +245,7 @@ class USDLoader extends Loader {
|
|
|
|
|
|
const zip = unzipSync( bytes );
|
|
|
const assets = parseAssets( zip );
|
|
|
- const { file, basePath } = findUSD( zip );
|
|
|
+ const { file, filename, basePath } = findUSD( zip );
|
|
|
|
|
|
if ( ! file ) {
|
|
|
|
|
|
@@ -253,16 +254,10 @@ class USDLoader extends Loader {
|
|
|
}
|
|
|
|
|
|
const composer = new USDComposer( scope.manager );
|
|
|
- let data;
|
|
|
-
|
|
|
- if ( isCrateFile( file ) ) {
|
|
|
-
|
|
|
- data = usdc.parseData( file.buffer );
|
|
|
-
|
|
|
- } else {
|
|
|
+ const data = assets[ filename ];
|
|
|
+ if ( ! data ) {
|
|
|
|
|
|
- const text = new TextDecoder().decode( file );
|
|
|
- data = usda.parseData( text );
|
|
|
+ throw new Error( 'USDLoader: Failed to parse root layer "' + filename + '".' );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -273,7 +268,7 @@ class USDLoader extends Loader {
|
|
|
// USDA (standalone, as ArrayBuffer)
|
|
|
|
|
|
const composer = new USDComposer( scope.manager );
|
|
|
- const text = new TextDecoder().decode( bytes );
|
|
|
+ const text = textDecoder.decode( bytes );
|
|
|
const data = usda.parseData( text );
|
|
|
return composer.compose( data, {} );
|
|
|
|