UNPKG

playcanvas

Version:

Open-source WebGL/WebGPU 3D engine for the web

238 lines (237 loc) 6.14 kB
import { path } from "../../core/path.js"; import { Tags } from "../../core/tags.js"; import { EventHandler } from "../../core/event-handler.js"; import { findAvailableLocale } from "../i18n/utils.js"; import { ABSOLUTE_URL } from "./constants.js"; import { AssetFile } from "./asset-file.js"; import { getApplication } from "../globals.js"; import { http } from "../../platform/net/http.js"; let assetIdCounter = -1; const VARIANT_SUPPORT = { pvr: "extCompressedTexturePVRTC", dxt: "extCompressedTextureS3TC", etc2: "extCompressedTextureETC", etc1: "extCompressedTextureETC1", basis: "canvas" // dummy, basis is always supported }; const VARIANT_DEFAULT_PRIORITY = ["pvr", "dxt", "etc2", "etc1", "basis"]; class Asset extends EventHandler { static EVENT_LOAD = "load"; static EVENT_UNLOAD = "unload"; static EVENT_REMOVE = "remove"; static EVENT_ERROR = "error"; static EVENT_CHANGE = "change"; static EVENT_PROGRESS = "progress"; static EVENT_ADDLOCALIZED = "add:localized"; static EVENT_REMOVELOCALIZED = "remove:localized"; _file = null; _i18n = {}; _preload = false; _resources = []; id = assetIdCounter--; loaded = false; loading = false; options = {}; registry = null; tags = new Tags(this); type; urlObject = null; constructor(name, type, file, data = {}, options = {}) { super(); this._name = name || ""; this.type = type; this._data = data || {}; this.options = options || {}; if (file) this.file = file; } set name(value) { if (this._name === value) { return; } const old = this._name; this._name = value; this.fire("name", this, this._name, old); } get name() { return this._name; } set file(value) { if (value && value.variants && ["texture", "textureatlas", "bundle"].indexOf(this.type) !== -1) { const app = this.registry?._loader?._app || getApplication(); const device = app?.graphicsDevice; if (device) { for (let i = 0, len = VARIANT_DEFAULT_PRIORITY.length; i < len; i++) { const variant = VARIANT_DEFAULT_PRIORITY[i]; if (value.variants[variant] && device[VARIANT_SUPPORT[variant]]) { value = value.variants[variant]; break; } if (app.enableBundles) { const bundles = app.bundles.listBundlesForAsset(this); if (bundles && bundles.find((b) => { return b?.file?.variants[variant]; })) { break; } } } } } const oldFile = this._file; const newFile = value ? new AssetFile(value.url, value.filename, value.hash, value.size, value.opt, value.contents) : null; if (!!newFile !== !!oldFile || newFile && !newFile.equals(oldFile)) { this._file = newFile; this.fire("change", this, "file", newFile, oldFile); this.reload(); } } get file() { return this._file; } set data(value) { const old = this._data; this._data = value; if (value !== old) { this.fire("change", this, "data", value, old); if (this.loaded) { this.registry._loader.patch(this, this.registry); } } } get data() { return this._data; } set resource(value) { const _old = this._resources[0]; this._resources[0] = value; this.fire("change", this, "resource", value, _old); } get resource() { return this._resources[0]; } set resources(value) { const _old = this._resources; this._resources = value; this.fire("change", this, "resources", value, _old); } get resources() { return this._resources; } set preload(value) { value = !!value; if (this._preload === value) { return; } this._preload = value; if (this._preload && !this.loaded && !this.loading && this.registry) { this.registry.load(this); } } get preload() { return this._preload; } set loadFaces(value) { value = !!value; if (!this.hasOwnProperty("_loadFaces") || value !== this._loadFaces) { this._loadFaces = value; if (this.loaded) { this.registry._loader.patch(this, this.registry); } } } get loadFaces() { return this._loadFaces; } getFileUrl() { const file = this.file; if (!file || !file.url) { return null; } let url = file.url; if (this.registry && this.registry.prefix && !ABSOLUTE_URL.test(url)) { url = this.registry.prefix + url; } if (this.type !== "script" && file.hash) { const separator = url.indexOf("?") !== -1 ? "&" : "?"; url += `${separator}t=${file.hash}`; } return url; } getAbsoluteUrl(relativePath) { if (relativePath.startsWith("blob:") || relativePath.startsWith("data:")) { return relativePath; } const base = path.getDirectory(this.file.url); return path.join(base, relativePath); } getLocalizedAssetId(locale) { locale = findAvailableLocale(locale, this._i18n); return this._i18n[locale] || null; } addLocalizedAssetId(locale, assetId) { this._i18n[locale] = assetId; this.fire("add:localized", locale, assetId); } removeLocalizedAssetId(locale) { const assetId = this._i18n[locale]; if (assetId) { delete this._i18n[locale]; this.fire("remove:localized", locale, assetId); } } ready(callback, scope) { scope = scope || this; if (this.loaded) { callback.call(scope, this); } else { this.once("load", (asset) => { callback.call(scope, asset); }); } } reload() { if (this.loaded) { this.loaded = false; this.registry.load(this); } } unload() { if (!this.loaded && this._resources.length === 0) { return; } this.fire("unload", this); this.registry.fire(`unload:${this.id}`, this); const old = this._resources; if (this.urlObject) { URL.revokeObjectURL(this.urlObject); this.urlObject = null; } this.resources = []; this.loaded = false; if (this.file) { this.registry._loader.clearCache(this.getFileUrl(), this.type); } for (let i = 0; i < old.length; ++i) { old[i]?.destroy?.(); } } static fetchArrayBuffer(loadUrl, callback, asset, maxRetries = 0) { if (asset?.file?.contents) { setTimeout(() => { callback(null, asset.file.contents); }); } else { http.get(loadUrl, { cache: true, responseType: "arraybuffer", retry: maxRetries > 0, maxRetries, progress: asset }, callback); } } } export { Asset };