@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
164 lines (132 loc) • 4.03 kB
JavaScript
import { LoadingCache } from "../../../../core/cache/LoadingCache.js";
import { strictEquals } from "../../../../core/function/strictEquals.js";
import { computeStringHash } from "../../../../core/primitives/strings/computeStringHash.js";
import { GameAssetType } from "../../../asset/GameAssetType.js";
import { Reference } from "../../../reference/v2/Reference.js";
import { Sampler2D } from "../sampler/Sampler2D.js";
import { sampler2d_ensure_uint8_RGBA } from "../sampler/sampler2d_ensure_uint8_RGBA.js";
import { CachingTextureAtlas } from "./CachingTextureAtlas.js";
import { TextureAtlas } from "./TextureAtlas.js";
export class ManagedAtlas {
/**
*
* @type {AssetManager|null}
*/
#assets = null;
/**
*
* @type {Map<string, Reference>}
*/
#references = new Map();
/**
* Pixel padding
* @type {number}
*/
#padding = 4;
/**
* @type {LoadingCache<string, AtlasPatch>}
*/
#patch_loader;
/**
*
* @type {Map<string, AtlasPatch>}
*/
#url_patch_lookup = new Map();
/**
*
* @type {Map<AtlasPatch,string[]>}
*/
#patch_url_lookup = new Map();
/**
*
* @param {AssetManager} assetManager
* @constructor
*/
constructor(assetManager) {
this.#assets = assetManager;
/**
* pre-allocate space on the atlas to avoid some initial re-sizing
* @type {TextureAtlas}
*/
const atlas = new TextureAtlas(64);
this.__caching_atlas = new CachingTextureAtlas({ atlas });
/**
*
* @type {TextureAtlas}
*/
this.atlas = atlas;
atlas.on.removed.add(this.#remove_from_lookup, this);
this.#patch_loader = new LoadingCache({
keyHashFunction: computeStringHash,
keyEqualityFunction: strictEquals,
load: async (url) => {
const sampler = await this.#resolve_texture(url);
const patch = this.__caching_atlas.add(sampler, this.#padding);
this.#add_to_lookup(url, patch);
return patch;
}
});
}
/**
*
* @param {AtlasPatch} patch
*/
#remove_from_lookup(patch) {
const urls = this.#patch_url_lookup.get(patch);
for (let i = 0; i < urls.length; i++) {
const url = urls[i];
this.#url_patch_lookup.delete(url);
}
}
/**
*
* @param {string} url
* @param {AtlasPatch} patch
*/
#add_to_lookup(url, patch) {
this.#url_patch_lookup.set(url, patch);
let urls = this.#patch_url_lookup.get(patch);
if (urls === undefined) {
urls = [];
this.#patch_url_lookup.set(patch, urls);
}
urls.push(url);
}
/**
*
* @param {string} url
* @returns {Promise<Sampler2D>}
*/
async #resolve_texture(url) {
const asset = await this.#assets.promise(url, GameAssetType.Image);
const source = asset.create();
return sampler2d_ensure_uint8_RGBA(source);
}
reset() {
this.#references.clear();
this.__caching_atlas.reset();
this.#url_patch_lookup.clear();
}
/**
*
* @param {string} key
* @returns {Promise<Reference<AtlasPatch>>}
*/
async acquire(url) {
const reference = new Reference();
let patch = this.#url_patch_lookup.get(url);
if (patch === undefined) {
// patch is not active, need to load it
patch = await this.#patch_loader.get(url);
}
reference.bind(patch);
return reference;
}
/**
*
* @param {string} key
*/
release(key) {
throw new Error('Method is deprecated, use "Reference.release" instead');
}
}