/** * An Asset Manager for AR * @author Alexandre Martins (https://github.com/alemart/encantar-js) * @license LGPL-3.0-or-later */ /** * The Asset Manager is used to preload resources (models, textures, etc.) * You'll typically preload resources before starting the AR session */ class AssetManager { /** * Get an object URL corresponding to a preloaded asset * @param {string} filename * @returns {string} * @throws Throws an error if the asset has not been successfully preloaded */ url(filename) { return this._get(filename).url; } /** * Get a File corresponding to a preloaded asset * @param {string} filename * @returns {File} * @throws Throws an error if the asset has not been successfully preloaded */ file(filename) { return this._get(filename).file; } /** * Check if an asset has been preloaded successfully * @param {string} filename * @returns {boolean} */ has(filename) { return this._assetMap.has(filename); } /** * Preload one or more assets * @param {string|string[]} url URL(s) of the asset(s) * @param {object} [options] * @param {number} [options.timeout] timeout, in seconds * @returns {Promise} */ preload(url, options = {}) { if(Array.isArray(url)) return Promise.all(url.map(url => this.preload(url, options))); if(typeof url != 'string') return Promise.reject(new TypeError()); const filename = url.substring(1 + url.lastIndexOf('/')); if(this._assetMap.has(filename)) return Promise.resolve(); return new Promise((resolve, reject) => { const seconds = options.timeout !== undefined ? options.timeout : Infinity; const timeoutFn = () => reject(new Error(`Can't preload assets: timeout!`)); const timeoutId = isFinite(seconds) ? setTimeout(timeoutFn, seconds * 1000) : undefined; fetch(url) .then(response => { if(!response.ok) throw new Error(`HTTP ${response.status}: ${response.statusText}`); return response.blob(); }) .then(blob => { const file = new File([ blob ], filename, { type: blob.type }); const url = URL.createObjectURL(file); const asset = { file, url }; this._assetMap.set(filename, asset); }) .catch(error => { console.warn(`Can't preload asset "${filename}"! ${error.message} (${url})`); }) .finally(() => { clearTimeout(timeoutId); resolve(); }); }); } /** * Get a preloaded asset * @param {string} filename * @returns {object} * @throws {Error} * @internal */ _get(filename) { const asset = this._assetMap.get(filename); if(!asset) throw new Error(`Asset "${filename}" has not been preloaded!`); return asset; } /** * Constructor */ constructor() { this._assetMap = new Map(); } }