@spearwolf/twopoint5d
Version:
a library to create 2.5d realtime graphics and pixelart with three.js
167 lines • 6.49 kB
JavaScript
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