UNPKG

playcanvas

Version:

PlayCanvas WebGL game engine

339 lines (336 loc) 10.2 kB
import { path } from '../../core/path.js'; import { EventHandler } from '../../core/event-handler.js'; import { TagsCache } from '../../core/tags-cache.js'; import { standardMaterialTextureParameters } from '../../scene/materials/standard-material-parameters.js'; import { Asset } from './asset.js'; class AssetRegistry extends EventHandler { static{ this.EVENT_LOAD = 'load'; } static{ this.EVENT_ADD = 'add'; } static{ this.EVENT_REMOVE = 'remove'; } static{ this.EVENT_ERROR = 'error'; } constructor(loader){ super(), this._assets = new Set(), this._idToAsset = new Map(), this._urlToAsset = new Map(), this._nameToAsset = new Map(), this._tags = new TagsCache('id'), this.prefix = null, this.bundles = null; this._loader = loader; } list(filters = {}) { const assets = Array.from(this._assets); if (filters.preload !== undefined) { return assets.filter((asset)=>asset.preload === filters.preload); } return assets; } add(asset) { if (this._assets.has(asset)) return; this._assets.add(asset); this._idToAsset.set(asset.id, asset); if (asset.file?.url) { this._urlToAsset.set(asset.file.url, asset); } if (!this._nameToAsset.has(asset.name)) { this._nameToAsset.set(asset.name, new Set()); } this._nameToAsset.get(asset.name).add(asset); asset.on('name', this._onNameChange, this); asset.registry = this; this._tags.addItem(asset); asset.tags.on('add', this._onTagAdd, this); asset.tags.on('remove', this._onTagRemove, this); this.fire('add', asset); this.fire(`add:${asset.id}`, asset); if (asset.file?.url) { this.fire(`add:url:${asset.file.url}`, asset); } if (asset.preload) { this.load(asset); } } remove(asset) { if (!this._assets.has(asset)) return false; this._assets.delete(asset); this._idToAsset.delete(asset.id); if (asset.file?.url) { this._urlToAsset.delete(asset.file.url); } asset.off('name', this._onNameChange, this); if (this._nameToAsset.has(asset.name)) { const items = this._nameToAsset.get(asset.name); items.delete(asset); if (items.size === 0) { this._nameToAsset.delete(asset.name); } } this._tags.removeItem(asset); asset.tags.off('add', this._onTagAdd, this); asset.tags.off('remove', this._onTagRemove, this); asset.fire('remove', asset); this.fire('remove', asset); this.fire(`remove:${asset.id}`, asset); if (asset.file?.url) { this.fire(`remove:url:${asset.file.url}`, asset); } return true; } get(id) { return this._idToAsset.get(Number(id)); } getByUrl(url) { return this._urlToAsset.get(url); } load(asset, options) { if ((asset.loading || asset.loaded) && !options?.force) { return; } const file = asset.file; const _fireLoad = ()=>{ this.fire('load', asset); this.fire(`load:${asset.id}`, asset); if (file && file.url) { this.fire(`load:url:${file.url}`, asset); } asset.fire('load', asset); }; const _opened = (resource)=>{ if (resource instanceof Array) { asset.resources = resource; } else { asset.resource = resource; } this._loader.patch(asset, this); if (asset.type === 'bundle') { const assetIds = asset.data.assets; for(let i = 0; i < assetIds.length; i++){ const assetInBundle = this._idToAsset.get(assetIds[i]); if (assetInBundle && !assetInBundle.loaded) { this.load(assetInBundle, { force: true }); } } if (asset.resource.loaded) { _fireLoad(); } else { this.fire('load:start', asset); this.fire(`load:start:${asset.id}`, asset); if (file && file.url) { this.fire(`load:start:url:${file.url}`, asset); } asset.fire('load:start', asset); asset.resource.on('load', _fireLoad); } } else { _fireLoad(); } }; const _loaded = (err, resource, extra)=>{ asset.loaded = true; asset.loading = false; if (err) { this.fire('error', err, asset); this.fire(`error:${asset.id}`, err, asset); asset.fire('error', err, asset); } else { if (asset.type === "script") { const handler = this._loader.getHandler("script"); if (handler._cache[asset.id] && handler._cache[asset.id].parentNode === document.head) { document.head.removeChild(handler._cache[asset.id]); } handler._cache[asset.id] = extra; } _opened(resource); } }; if (file || asset.type === 'cubemap') { this.fire('load:start', asset); this.fire(`load:${asset.id}:start`, asset); asset.loading = true; const fileUrl = asset.getFileUrl(); if (asset.type === 'bundle') { const assetIds = asset.data.assets; for(let i = 0; i < assetIds.length; i++){ const assetInBundle = this._idToAsset.get(assetIds[i]); if (!assetInBundle) { continue; } if (assetInBundle.loaded || assetInBundle.resource || assetInBundle.loading) { continue; } assetInBundle.loading = true; } } this._loader.load(fileUrl, asset.type, _loaded, asset, options); } else { const resource = this._loader.open(asset.type, asset.data); asset.loaded = true; _opened(resource); } } loadFromUrl(url, type, callback) { this.loadFromUrlAndFilename(url, null, type, callback); } loadFromUrlAndFilename(url, filename, type, callback) { const name = path.getBasename(filename || url); const file = { filename: filename || name, url: url }; let asset = this.getByUrl(url); if (!asset) { asset = new Asset(name, type, file); this.add(asset); } else if (asset.loaded) { callback(asset.loadFromUrlError || null, asset); return; } const startLoad = (asset)=>{ asset.once('load', (loadedAsset)=>{ if (type === 'material') { this._loadTextures(loadedAsset, (err, textures)=>{ callback(err, loadedAsset); }); } else { callback(null, loadedAsset); } }); asset.once('error', (err)=>{ if (err) { this.loadFromUrlError = err; } callback(err, asset); }); this.load(asset); }; if (asset.resource) { callback(null, asset); } else if (type === 'model') { this._loadModel(asset, startLoad); } else { startLoad(asset); } } _loadModel(modelAsset, continuation) { const url = modelAsset.getFileUrl(); const ext = path.getExtension(url); if (ext === '.json' || ext === '.glb') { const dir = path.getDirectory(url); const basename = path.getBasename(url); const mappingUrl = path.join(dir, basename.replace(ext, '.mapping.json')); this._loader.load(mappingUrl, 'json', (err, data)=>{ if (err) { modelAsset.data = { mapping: [] }; continuation(modelAsset); } else { this._loadMaterials(modelAsset, data, (e, materials)=>{ modelAsset.data = data; continuation(modelAsset); }); } }); } else { continuation(modelAsset); } } _loadMaterials(modelAsset, mapping, callback) { const materials = []; let count = 0; const onMaterialLoaded = (err, materialAsset)=>{ this._loadTextures(materialAsset, (err, textures)=>{ materials.push(materialAsset); if (materials.length === count) { callback(null, materials); } }); }; for(let i = 0; i < mapping.mapping.length; i++){ const path = mapping.mapping[i].path; if (path) { count++; const url = modelAsset.getAbsoluteUrl(path); this.loadFromUrl(url, 'material', onMaterialLoaded); } } if (count === 0) { callback(null, materials); } } _loadTextures(materialAsset, callback) { const textures = []; let count = 0; const data = materialAsset.data; if (data.mappingFormat !== 'path') { callback(null, textures); return; } const onTextureLoaded = (err, texture)=>{ if (err) console.error(err); textures.push(texture); if (textures.length === count) { callback(null, textures); } }; const texParams = standardMaterialTextureParameters; for(let i = 0; i < texParams.length; i++){ const path = data[texParams[i]]; if (path && typeof path === 'string') { count++; const url = materialAsset.getAbsoluteUrl(path); this.loadFromUrl(url, 'texture', onTextureLoaded); } } if (count === 0) { callback(null, textures); } } _onTagAdd(tag, asset) { this._tags.add(tag, asset); } _onTagRemove(tag, asset) { this._tags.remove(tag, asset); } _onNameChange(asset, name, nameOld) { if (this._nameToAsset.has(nameOld)) { const items = this._nameToAsset.get(nameOld); items.delete(asset); if (items.size === 0) { this._nameToAsset.delete(nameOld); } } if (!this._nameToAsset.has(asset.name)) { this._nameToAsset.set(asset.name, new Set()); } this._nameToAsset.get(asset.name).add(asset); } findByTag(...query) { return this._tags.find(query); } filter(callback) { return Array.from(this._assets).filter((asset)=>callback(asset)); } find(name, type) { const items = this._nameToAsset.get(name); if (!items) return null; for (const asset of items){ if (!type || asset.type === type) { return asset; } } return null; } findAll(name, type) { const items = this._nameToAsset.get(name); if (!items) return []; const results = Array.from(items); if (!type) return results; return results.filter((asset)=>asset.type === type); } } export { AssetRegistry };