UNPKG

@spearwolf/twopoint5d

Version:

a library to create 2.5d realtime graphics and pixelart with three.js

167 lines 6.49 kB
import { emit, on, once, onceAsync, retain } from '@spearwolf/eventize'; import { batch, createSignal } from '@spearwolf/signalize'; import { TextureResource } from './TextureResource.js'; const OnReady = 'ready'; const OnRendererChanged = 'rendererChanged'; const OnResource = 'resource'; const joinTextureClasses = (...classes) => { const all = classes?.filter((c) => c != null); if (all && all.length) { return Array.from(new Set(all.flat()).values()); } return undefined; }; export class TextureStore { static async load(url) { return new TextureStore().load(url); } #renderer; get renderer() { return this.#renderer.value; } set renderer(value) { this.#renderer.set(value); } #resources; constructor() { this.defaultTextureClasses = []; this.#renderer = createSignal(); this.#resources = new Map(); retain(this, [OnReady, OnRendererChanged]); this.#renderer.onChange((renderer) => { emit(this, OnRendererChanged, renderer); }); } onResource(id, callback) { const resource = this.#resources.get(id); if (resource) { callback(resource); return () => { }; } return on(this, `${OnResource}:${id}`, (resource) => { callback(resource); }); } async whenReady() { await onceAsync(this, OnReady); return this; } load(url) { fetch(url).then(async (response) => { const data = await response.json(); this.parse(data); }); return this; } parse(data) { if (Array.isArray(data.defaultTextureClasses) && data.defaultTextureClasses.length) { this.defaultTextureClasses = data.defaultTextureClasses.splice(0); } const updatedResources = []; for (const [id, item] of Object.entries(data.items)) { let resource = this.#resources.get(id); const textureClasses = joinTextureClasses(item.texture, this.defaultTextureClasses); if (item.tileSet) { if (resource) { if (resource.type !== 'tileset') { throw new Error(`Resource ${id} already exists with type "${resource.type}" - cannot change to "tileset"`); } batch(() => { resource.imageUrl = item.imageUrl; resource.tileSetOptions = item.tileSet; resource.textureClasses = textureClasses; }); } else { resource = TextureResource.fromTileSet(id, item.imageUrl, item.tileSet, textureClasses); } } else if (item.atlasUrl) { if (resource) { if (resource.type !== 'atlas') { throw new Error(`Resource ${id} already exists with type "${resource.type}" - cannot change to "atlas"`); } batch(() => { resource.atlasUrl = item.atlasUrl; resource.overrideImageUrl = item.overrideImageUrl; resource.textureClasses = textureClasses; }); } else { resource = TextureResource.fromAtlas(id, item.atlasUrl, item.overrideImageUrl, textureClasses); } } else if (item.imageUrl) { if (resource) { if (resource.type !== 'image') { throw new Error(`Resource ${id} already exists with type "${resource.type}" - cannot change to "image"`); } batch(() => { resource.imageUrl = item.imageUrl; resource.textureClasses = textureClasses; }); } else { resource = TextureResource.fromImage(id, item.imageUrl, textureClasses); } } if (resource) { this.#resources.set(id, resource); on(this, resource); updatedResources.push(resource); } } emit(this, OnReady, this); updatedResources.forEach((resource) => { emit(this, `${OnResource}:${resource.id}`, resource); }); } get(id, type, callback) { const isMultipleTypes = Array.isArray(type); const values = isMultipleTypes ? new Map() : undefined; const unsubscribeFromSubType = []; let unsubscribeFromResource; let isActiveSubscription = true; const clearSubTypeSubscriptions = () => { unsubscribeFromSubType.forEach((cb) => cb()); unsubscribeFromSubType.length = 0; }; const unsubscribe = () => { isActiveSubscription = false; values.clear(); unsubscribeFromResource?.(); clearSubTypeSubscriptions(); }; once(this, OnReady, () => { if (isActiveSubscription) { unsubscribeFromResource = this.onResource(id, (resource) => { clearSubTypeSubscriptions(); resource.load(); resource.renderer = this.renderer; resource.refCount++; unsubscribeFromSubType.push(() => { resource.refCount--; }); if (isMultipleTypes) { type.forEach((t) => { unsubscribeFromSubType.push(on(resource, t, (val) => { values.set(t, val); const valuesArg = type.map((t) => values.get(t)).filter((v) => v != null); if (valuesArg.length === type.length) { callback(valuesArg); } })); }); } else { unsubscribeFromSubType.push(on(resource, type, (val) => { callback(val); })); } }); } }); return unsubscribe; } } //# sourceMappingURL=TextureStore.js.map