UNPKG

three

Version:

JavaScript 3D library

228 lines (160 loc) 6.57 kB
import { FileLoader, Loader } from 'three/webgpu'; import { MaterialXDocument } from './materialx/MaterialXDocument.js'; import { MaterialXLog } from './materialx/MaterialXLog.js'; import { isZipBuffer, readMtlxArchive, createArchiveResolver } from './materialx/MaterialXArchive.js'; const _textDecoder = new TextDecoder(); function getResourcePath( loaderPath, url ) { if ( loaderPath ) return loaderPath; const index = url.lastIndexOf( '/' ); return index === - 1 ? '' : url.slice( 0, index + 1 ); } /** * A loader for the MaterialX format. * * The node materials loaded with this loader can only be used with {@link WebGPURenderer}. * Besides plain `.mtlx` documents, the loader accepts `.mtlx.zip` archives that bundle a * document with its textures. * * ```js * const loader = new MaterialXLoader().setPath( SAMPLE_PATH ); * const { materials } = await loader.loadAsync( 'standard_surface_brass_tiled.mtlx' ); * ``` * * @augments Loader * @three_import import { MaterialXLoader } from 'three/addons/loaders/MaterialXLoader.js'; */ class MaterialXLoader extends Loader { /** * Constructs a new MaterialX loader. * * @param {LoadingManager} [manager] - The loading manager. */ constructor( manager ) { super( manager ); /** * Releases the resources of the last loaded archive, if any. * * @private * @type {?Function} */ this.archiveDisposer = null; } /** * Frees the resources of the last loaded archive. * * @return {MaterialXLoader} A reference to this loader. */ dispose() { if ( this.archiveDisposer ) { this.archiveDisposer(); this.archiveDisposer = null; } return this; } /** * Starts loading from the given URL and passes the loaded MaterialX asset * to the `onLoad()` callback. * * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. * @param {function(Object)} onLoad - Executed when the loading process has been finished. Receives the parse result, see {@link MaterialXLoader#parse}. * @param {onProgressCallback} onProgress - Executed while the loading is in progress. * @param {onErrorCallback} onError - Executed when errors occur. * @param {Object} [options] - Parse options, see {@link MaterialXLoader#parse}. * @return {MaterialXLoader} A reference to this loader. */ load( url, onLoad, onProgress, onError, options = {} ) { const _onError = function ( e ) { if ( onError ) { onError( e ); } else { console.error( e ); } }; new FileLoader( this.manager ) .setPath( this.path ) .setResponseType( 'arraybuffer' ) .load( url, ( data ) => { try { onLoad( this.parseBuffer( data, url, options ) ); } catch ( e ) { _onError( e ); } }, onProgress, _onError ); return this; } /** * Async version of {@link MaterialXLoader#load}. The progress callback can be * omitted and the parse options passed as the second argument instead. * * @param {string} url - The path/URL of the file to be loaded. This can also be a data URI. * @param {onProgressCallback|Object} [onProgress] - Executed while the loading is in progress, or the parse options. * @param {Object} [options] - Parse options, see {@link MaterialXLoader#parse}. * @return {Promise<Object>} A Promise that resolves with the parse result. */ loadAsync( url, onProgress, options = {} ) { if ( onProgress && typeof onProgress === 'object' ) { options = onProgress; onProgress = undefined; } return new Promise( ( resolve, reject ) => { this.load( url, resolve, onProgress, reject, options ); } ); } /** * Parses a raw MaterialX document or a `.mtlx.zip` archive and returns the resulting materials. * * @param {ArrayBuffer|Uint8Array|string} data - The MaterialX document or archive. * @param {string} [url=''] - The URL the data was loaded from, used to resolve relative resource paths. * @param {Object} [options] - Parse options, see {@link MaterialXLoader#parse}. * @return {Object} The parse result, see {@link MaterialXLoader#parse}. */ parseBuffer( data, url = '', options = {} ) { this.dispose(); let text; let archiveResolver = null; if ( data && ( isZipBuffer( data ) || /\.mtlx\.zip$/i.test( url ) ) ) { const archive = readMtlxArchive( data ); text = archive.text; const resolver = createArchiveResolver( archive.files ); archiveResolver = resolver.resolve; this.archiveDisposer = resolver.dispose; } else if ( typeof data === 'string' ) { text = data; } else if ( data instanceof Uint8Array ) { text = _textDecoder.decode( data ); } else { text = _textDecoder.decode( new Uint8Array( data ) ); } return this.parse( text, { ...options, archiveResolver, path: options.path || getResourcePath( this.path, url ) } ); } /** * Parses the given MaterialX document and returns the resulting materials * together with the translation log. * * @param {string} text - The raw MaterialX data as a string. * @param {Object} [options] - Parse options. * @param {string} [options.path] - The base path for resolving resources like textures. Defaults to the loader's path. * @param {string} [options.materialName] - Only translate the material with this name. Defaults to all materials. * @param {string} [options.uvSpace='bottom-left'] - The UV space of the document's textures, `'bottom-left'` or `'top-left'`. * @param {Function} [options.interfaceValidator] - Validates node interfaces, see `createStrictInterfaceValidator()` in `MaterialXInterfaceValidation.js`. * @param {boolean} [options.throwOnErrors=true] - Whether translation errors throw or are only reported in the log. * @return {{materials: Object<string,NodeMaterial>, log: Array<Object>, errors: Array<Object>, warnings: Array<Object>}} The materials keyed by name and the translation log. */ parse( text, options = {} ) { const log = new MaterialXLog(); const document = new MaterialXDocument( this.manager, options.path || this.path, log, options.archiveResolver || null, options.uvSpace ); const result = document.parse( text, options.materialName || null, { interfaceValidator: options.interfaceValidator, } ); if ( options.throwOnErrors !== false && log.errors.length > 0 ) { const details = log.errors.map( ( error ) => error.message ).join( ' ' ); throw new Error( `THREE.MaterialXLoader: MaterialX translation failed with ${log.errors.length} error(s). ${details}` ); } return result; } } export { MaterialXLoader };