mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-17 04:21:39 +08:00
[ts][webcomponents] Sharable cache for AssetManager.
This commit is contained in:
parent
85de53624e
commit
2e5a099ef4
@ -35,17 +35,16 @@ export class AssetManagerBase implements Disposable {
|
|||||||
private pathPrefix: string = "";
|
private pathPrefix: string = "";
|
||||||
private textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture;
|
private textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture;
|
||||||
private downloader: Downloader;
|
private downloader: Downloader;
|
||||||
private assets: StringMap<any> = {};
|
private cache: AssetCache;
|
||||||
private assetsRefCount: StringMap<number> = {};
|
|
||||||
private assetsLoaded: StringMap<Promise<any>> = {};
|
|
||||||
private errors: StringMap<string> = {};
|
private errors: StringMap<string> = {};
|
||||||
private toLoad = 0;
|
private toLoad = 0;
|
||||||
private loaded = 0;
|
private loaded = 0;
|
||||||
|
|
||||||
constructor (textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture, pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
constructor (textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture, pathPrefix: string = "", downloader = new Downloader(), cache = new AssetCache()) {
|
||||||
this.textureLoader = textureLoader;
|
this.textureLoader = textureLoader;
|
||||||
this.pathPrefix = pathPrefix;
|
this.pathPrefix = pathPrefix;
|
||||||
this.downloader = downloader;
|
this.downloader = downloader;
|
||||||
|
this.cache = cache;
|
||||||
}
|
}
|
||||||
|
|
||||||
private start (path: string): string {
|
private start (path: string): string {
|
||||||
@ -56,8 +55,8 @@ export class AssetManagerBase implements Disposable {
|
|||||||
private success (callback: (path: string, data: any) => void, path: string, asset: any) {
|
private success (callback: (path: string, data: any) => void, path: string, asset: any) {
|
||||||
this.toLoad--;
|
this.toLoad--;
|
||||||
this.loaded++;
|
this.loaded++;
|
||||||
this.assets[path] = asset;
|
this.cache.assets[path] = asset;
|
||||||
this.assetsRefCount[path] = (this.assetsRefCount[path] || 0) + 1;
|
this.cache.assetsRefCount[path] = (this.cache.assetsRefCount[path] || 0) + 1;
|
||||||
if (callback) callback(path, asset);
|
if (callback) callback(path, asset);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,7 +93,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
|
|
||||||
if (this.reuseAssets(path, success, error)) return;
|
if (this.reuseAssets(path, success, error)) return;
|
||||||
|
|
||||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||||
this.downloader.downloadBinary(path, (data: Uint8Array): void => {
|
this.downloader.downloadBinary(path, (data: Uint8Array): void => {
|
||||||
this.success(success, path, data);
|
this.success(success, path, data);
|
||||||
resolve(data);
|
resolve(data);
|
||||||
@ -125,7 +124,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
|
|
||||||
if (this.reuseAssets(path, success, error)) return;
|
if (this.reuseAssets(path, success, error)) return;
|
||||||
|
|
||||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||||
this.downloader.downloadJson(path, (data: object): void => {
|
this.downloader.downloadJson(path, (data: object): void => {
|
||||||
this.success(success, path, data);
|
this.success(success, path, data);
|
||||||
resolve(data);
|
resolve(data);
|
||||||
@ -140,11 +139,17 @@ export class AssetManagerBase implements Disposable {
|
|||||||
reuseAssets (path: string,
|
reuseAssets (path: string,
|
||||||
success: (path: string, data: any) => void = () => { },
|
success: (path: string, data: any) => void = () => { },
|
||||||
error: (path: string, message: string) => void = () => { }) {
|
error: (path: string, message: string) => void = () => { }) {
|
||||||
const loadedStatus = this.assetsLoaded[path];
|
const loadedStatus = this.cache.assetsLoaded[path];
|
||||||
const alreadyExistsOrLoading = loadedStatus !== undefined;
|
const alreadyExistsOrLoading = loadedStatus !== undefined;
|
||||||
if (alreadyExistsOrLoading) {
|
if (alreadyExistsOrLoading) {
|
||||||
loadedStatus
|
loadedStatus
|
||||||
.then(data => this.success(success, path, data))
|
.then(data => {
|
||||||
|
if (data instanceof Image) {
|
||||||
|
data = this.textureLoader(data);
|
||||||
|
this.cache.assetsLoaded[path] = Promise.resolve(data);
|
||||||
|
}
|
||||||
|
return this.success(success, path, data)
|
||||||
|
})
|
||||||
.catch(errorMsg => this.error(error, path, errorMsg));
|
.catch(errorMsg => this.error(error, path, errorMsg));
|
||||||
}
|
}
|
||||||
return alreadyExistsOrLoading;
|
return alreadyExistsOrLoading;
|
||||||
@ -158,7 +163,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
|
|
||||||
if (this.reuseAssets(path, success, error)) return;
|
if (this.reuseAssets(path, success, error)) return;
|
||||||
|
|
||||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||||
let isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
|
let isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
|
||||||
let isWebWorker = !isBrowser; // && typeof importScripts !== 'undefined';
|
let isWebWorker = !isBrowser; // && typeof importScripts !== 'undefined';
|
||||||
if (isWebWorker) {
|
if (isWebWorker) {
|
||||||
@ -171,7 +176,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
return blob ? createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" }) : null;
|
return blob ? createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" }) : null;
|
||||||
}).then((bitmap) => {
|
}).then((bitmap) => {
|
||||||
if (bitmap) {
|
if (bitmap) {
|
||||||
const texture = this.textureLoader(bitmap)
|
const texture = this.textureLoader(bitmap);
|
||||||
this.success(success, path, texture);
|
this.success(success, path, texture);
|
||||||
resolve(texture);
|
resolve(texture);
|
||||||
};
|
};
|
||||||
@ -180,7 +185,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
let image = new Image();
|
let image = new Image();
|
||||||
image.crossOrigin = "anonymous";
|
image.crossOrigin = "anonymous";
|
||||||
image.onload = () => {
|
image.onload = () => {
|
||||||
const texture = this.textureLoader(image)
|
const texture = this.textureLoader(image);
|
||||||
this.success(success, path, texture);
|
this.success(success, path, texture);
|
||||||
resolve(texture);
|
resolve(texture);
|
||||||
};
|
};
|
||||||
@ -206,7 +211,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
|
|
||||||
if (this.reuseAssets(path, success, error)) return;
|
if (this.reuseAssets(path, success, error)) return;
|
||||||
|
|
||||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||||
this.downloader.downloadText(path, (atlasText: string): void => {
|
this.downloader.downloadText(path, (atlasText: string): void => {
|
||||||
try {
|
try {
|
||||||
let atlas = new TextureAtlas(atlasText);
|
let atlas = new TextureAtlas(atlasText);
|
||||||
@ -224,7 +229,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
},
|
},
|
||||||
(imagePath: string, message: string) => {
|
(imagePath: string, message: string) => {
|
||||||
if (!abort) {
|
if (!abort) {
|
||||||
const errorMsg = `Couldn't load texture atlas ${path} page image: ${imagePath}`;
|
const errorMsg = `Couldn't load texture ${path} page image: ${imagePath}`;
|
||||||
this.error(error, path, errorMsg);
|
this.error(error, path, errorMsg);
|
||||||
reject(errorMsg);
|
reject(errorMsg);
|
||||||
}
|
}
|
||||||
@ -254,7 +259,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
|
|
||||||
if (this.reuseAssets(path, success, error)) return;
|
if (this.reuseAssets(path, success, error)) return;
|
||||||
|
|
||||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||||
this.downloader.downloadText(path, (atlasText: string): void => {
|
this.downloader.downloadText(path, (atlasText: string): void => {
|
||||||
try {
|
try {
|
||||||
const atlas = new TextureAtlas(atlasText);
|
const atlas = new TextureAtlas(atlasText);
|
||||||
@ -319,13 +324,17 @@ export class AssetManagerBase implements Disposable {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setCache (cache: AssetCache) {
|
||||||
|
this.cache = cache;
|
||||||
|
}
|
||||||
|
|
||||||
get (path: string) {
|
get (path: string) {
|
||||||
return this.assets[this.pathPrefix + path];
|
return this.cache.assets[this.pathPrefix + path];
|
||||||
}
|
}
|
||||||
|
|
||||||
require (path: string) {
|
require (path: string) {
|
||||||
path = this.pathPrefix + path;
|
path = this.pathPrefix + path;
|
||||||
let asset = this.assets[path];
|
let asset = this.cache.assets[path];
|
||||||
if (asset) return asset;
|
if (asset) return asset;
|
||||||
let error = this.errors[path];
|
let error = this.errors[path];
|
||||||
throw Error("Asset not found: " + path + (error ? "\n" + error : ""));
|
throw Error("Asset not found: " + path + (error ? "\n" + error : ""));
|
||||||
@ -333,22 +342,22 @@ export class AssetManagerBase implements Disposable {
|
|||||||
|
|
||||||
remove (path: string) {
|
remove (path: string) {
|
||||||
path = this.pathPrefix + path;
|
path = this.pathPrefix + path;
|
||||||
let asset = this.assets[path];
|
let asset = this.cache.assets[path];
|
||||||
if (asset.dispose) asset.dispose();
|
if (asset.dispose) asset.dispose();
|
||||||
delete this.assets[path];
|
delete this.cache.assets[path];
|
||||||
delete this.assetsRefCount[path];
|
delete this.cache.assetsRefCount[path];
|
||||||
delete this.assetsLoaded[path];
|
delete this.cache.assetsLoaded[path];
|
||||||
return asset;
|
return asset;
|
||||||
}
|
}
|
||||||
|
|
||||||
removeAll () {
|
removeAll () {
|
||||||
for (let path in this.assets) {
|
for (let path in this.cache.assets) {
|
||||||
let asset = this.assets[path];
|
let asset = this.cache.assets[path];
|
||||||
if (asset.dispose) asset.dispose();
|
if (asset.dispose) asset.dispose();
|
||||||
}
|
}
|
||||||
this.assets = {};
|
this.cache.assets = {};
|
||||||
this.assetsLoaded = {};
|
this.cache.assetsLoaded = {};
|
||||||
this.assetsRefCount = {};
|
this.cache.assetsRefCount = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
isLoadingComplete (): boolean {
|
isLoadingComplete (): boolean {
|
||||||
@ -369,7 +378,7 @@ export class AssetManagerBase implements Disposable {
|
|||||||
|
|
||||||
// dispose asset only if it's not used by others
|
// dispose asset only if it's not used by others
|
||||||
disposeAsset (path: string) {
|
disposeAsset (path: string) {
|
||||||
if (--this.assetsRefCount[path] === 0) {
|
if (--this.cache.assetsRefCount[path] === 0) {
|
||||||
this.remove(path)
|
this.remove(path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,6 +392,27 @@ export class AssetManagerBase implements Disposable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class AssetCache {
|
||||||
|
public assets: StringMap<any> = {};
|
||||||
|
public assetsRefCount: StringMap<number> = {};
|
||||||
|
public assetsLoaded: StringMap<Promise<any>> = {};
|
||||||
|
|
||||||
|
static AVAILABLE_CACHES = new Map<string, AssetCache>();
|
||||||
|
static getCache (id: string) {
|
||||||
|
const cache = AssetCache.AVAILABLE_CACHES.get(id);
|
||||||
|
if (cache) return cache;
|
||||||
|
|
||||||
|
const newCache = new AssetCache();
|
||||||
|
AssetCache.AVAILABLE_CACHES.set(id, newCache);
|
||||||
|
return newCache;
|
||||||
|
}
|
||||||
|
|
||||||
|
async addAsset(path: string, asset: any) {
|
||||||
|
this.assetsLoaded[path] = Promise.resolve(asset);
|
||||||
|
this.assets[path] = await asset;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export class Downloader {
|
export class Downloader {
|
||||||
private callbacks: StringMap<Array<Function>> = {};
|
private callbacks: StringMap<Array<Function>> = {};
|
||||||
rawDataUris: StringMap<string> = {};
|
rawDataUris: StringMap<string> = {};
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { AssetManager, Color, Disposable, Input, LoadingScreen, ManagedWebGLRenderingContext, Physics, SceneRenderer, TimeKeeper, Vector2, Vector3 } from "@esotericsoftware/spine-webgl"
|
import { AssetCache, AssetManager, Color, Disposable, Input, LoadingScreen, ManagedWebGLRenderingContext, Physics, SceneRenderer, TimeKeeper, Vector2, Vector3 } from "@esotericsoftware/spine-webgl"
|
||||||
import { SpineWebComponentSkeleton } from "./SpineWebComponentSkeleton.js"
|
import { SpineWebComponentSkeleton } from "./SpineWebComponentSkeleton.js"
|
||||||
import { AttributeTypes, castValue, Point, Rectangle } from "./wcUtils.js"
|
import { AttributeTypes, castValue, Point, Rectangle } from "./wcUtils.js"
|
||||||
|
|
||||||
@ -48,10 +48,11 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
static getOrCreateOverlay (overlayId: string | null): SpineWebComponentOverlay {
|
static getOrCreateOverlay (overlayId: string | null): SpineWebComponentOverlay {
|
||||||
let overlay = SpineWebComponentOverlay.OVERLAY_LIST.get(overlayId || SpineWebComponentOverlay.OVERLAY_ID);
|
const id = overlayId || SpineWebComponentOverlay.OVERLAY_ID;
|
||||||
|
let overlay = SpineWebComponentOverlay.OVERLAY_LIST.get(id);
|
||||||
if (!overlay) {
|
if (!overlay) {
|
||||||
overlay = document.createElement('spine-overlay') as SpineWebComponentOverlay;
|
overlay = document.createElement('spine-overlay') as SpineWebComponentOverlay;
|
||||||
overlay.setAttribute('overlay-id', SpineWebComponentOverlay.OVERLAY_ID);
|
overlay.setAttribute('overlay-id', id);
|
||||||
document.body.appendChild(overlay);
|
document.body.appendChild(overlay);
|
||||||
}
|
}
|
||||||
return overlay;
|
return overlay;
|
||||||
@ -232,6 +233,9 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
|||||||
overlayId = SpineWebComponentOverlay.OVERLAY_ID;
|
overlayId = SpineWebComponentOverlay.OVERLAY_ID;
|
||||||
this.setAttribute('overlay-id', overlayId);
|
this.setAttribute('overlay-id', overlayId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.assetManager.setCache(AssetCache.getCache(overlayId));
|
||||||
|
|
||||||
const existingOverlay = SpineWebComponentOverlay.OVERLAY_LIST.get(overlayId);
|
const existingOverlay = SpineWebComponentOverlay.OVERLAY_LIST.get(overlayId);
|
||||||
if (existingOverlay && existingOverlay !== this) {
|
if (existingOverlay && existingOverlay !== this) {
|
||||||
throw new Error(`"SpineWebComponentOverlay - You cannot have two spine-overlay with the same overlay-id: ${overlayId}"`);
|
throw new Error(`"SpineWebComponentOverlay - You cannot have two spine-overlay with the same overlay-id: ${overlayId}"`);
|
||||||
|
|||||||
@ -31,7 +31,6 @@ import { AssetManagerBase, Downloader } from "@esotericsoftware/spine-core"
|
|||||||
import { ManagedWebGLRenderingContext } from "./WebGL.js";
|
import { ManagedWebGLRenderingContext } from "./WebGL.js";
|
||||||
import { GLTexture } from "./GLTexture.js";
|
import { GLTexture } from "./GLTexture.js";
|
||||||
|
|
||||||
|
|
||||||
export class AssetManager extends AssetManagerBase {
|
export class AssetManager extends AssetManagerBase {
|
||||||
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
||||||
super((image: HTMLImageElement | ImageBitmap) => {
|
super((image: HTMLImageElement | ImageBitmap) => {
|
||||||
|
|||||||
@ -27,7 +27,7 @@
|
|||||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
*****************************************************************************/
|
*****************************************************************************/
|
||||||
|
|
||||||
import { Restorable, BlendMode } from "@esotericsoftware/spine-core";
|
import { Restorable } from "@esotericsoftware/spine-core";
|
||||||
|
|
||||||
export class ManagedWebGLRenderingContext {
|
export class ManagedWebGLRenderingContext {
|
||||||
public canvas: HTMLCanvasElement | OffscreenCanvas;
|
public canvas: HTMLCanvasElement | OffscreenCanvas;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user