@woosh/meep-engine
Version:
Pure JavaScript game engine. Fully featured and production ready.
179 lines (145 loc) • 4.92 kB
JavaScript
import { Cache } from "../../../../core/cache/Cache.js";
import { HashMap } from "../../../../core/collection/map/HashMap.js";
import { computeMaterialEquality } from "../../../asset/loaders/material/computeMaterialEquality.js";
import { computeMaterialHash } from "../../../asset/loaders/material/computeMaterialHash.js";
import { ManagedMaterial } from "./ManagedMaterial.js";
export class MaterialManager {
constructor() {
/**
* Stores links from uncompiled (source) materials to managed containers
* @type {HashMap<Material, ManagedMaterial>}
* @private
*/
this.__library = new HashMap({
keyHashFunction: computeMaterialHash,
keyEqualityFunction: computeMaterialEquality,
capacity: 1024
});
/**
* Cache for unused materials, gives them a second chance to be used before being disposed completely
* @type {Cache<Material, ManagedMaterial>}
* @private
*/
this.__cache = new Cache({
keyHashFunction: computeMaterialHash,
keyEqualityFunction: computeMaterialEquality,
maxWeight: 100
});
/**
* Stores links from compiled materials back to the container
* @type {HashMap<Material, ManagedMaterial>}
* @private
*/
this.__reverse_library = new HashMap({
keyHashFunction: computeMaterialHash,
keyEqualityFunction: computeMaterialEquality,
capacity: 1024
});
this.__cache.onEvicted.add(this.__dispose_material, this);
/**
*
* @type {AbstractMaterialTransformer[]}
* @private
*/
this.__transform_chain = [];
}
/**
* handle material disposal
* @param {Material} m
* @param {ManagedMaterial} managed_material
* @private
*/
__dispose_material(m, managed_material) {
managed_material.getMaterial().dispose();
}
/**
*
* @param {ManagedMaterial} material
* @private
*/
__handle_last_reference_removed(material) {
const compiled_material = material.getMaterial();
// remove from library
const source = material.getSource();
this.__library.delete(source);
this.__reverse_library.delete(compiled_material);
// put material into cache
this.__cache.put(source, material);
}
/**
*
* @param {Function} klass
* @return {AbstractMaterialTransformer[]}
*/
getCompilationSteps(klass){
return this.__transform_chain.filter(x => x instanceof klass);
}
/**
*
* @param {AbstractMaterialTransformer} def
*/
addCompileStep(def) {
this.__transform_chain.push(def);
}
/**
*
* @param {AbstractMaterialTransformer} def
* @returns {boolean}
*/
removeCompileStep(def) {
const i = this.__transform_chain.indexOf(def);
if (i === -1) {
return false;
}
this.__transform_chain.splice(i, 1);
return true;
}
/**
*
* @param {Material} m
* @returns {Material}
* @private
*/
__compile(m) {
let r = m;
const transformers = this.__transform_chain;
const n = transformers.length;
for (let i = 0; i < n; i++) {
const transformer = transformers[i];
r = transformer.transform(r);
}
return r;
}
/**
*
* @param {Material} source
* @returns {Reference<Material>}
*/
obtain(source) {
// reverse check to see if this is actually an already compiled/managed material
let material = this.__reverse_library.get(source);
if (material !== undefined) {
// already compiled material
return material.getRef();
}
// forward check
material = this.__library.get(source);
if (material === undefined) {
// not found in library, check cache
material = this.__cache.get(source);
if (material !== null) {
// remove from cache
this.__cache.remove(source);
} else {
// material not found in cache
const mat = this.__compile(source);
material = new ManagedMaterial(mat);
material.setSource(source);
}
material.onLastReleased.addOne(this.__handle_last_reference_removed, this);
this.__library.set(source, material);
this.__reverse_library.set(material.getMaterial(), material);
}
return material.getRef();
}
}