import { Utils } from '../common/Utils';
import { ImageFilterType, ImageWrapType } from '../const';
/**
* @typedef LoadTextureResourceSamplerInfo
* @property {ImageFilterType} minFilter The min filter type.
* @property {ImageFilterType} magFilter The mag filter type.
* @property {ImageWrapType} wrapS The horz wrap type.
* @property {ImageWrapType} wrapT The vert wrap type.
*/
/**
* When create texture.
* @callback onCreateTextureCallback
* @param {String} url The url.
* @param {LoadTextureResourceSamplerInfo} sampler The sampler info.
* @returns {THING.BaseTexture}
*/
/**
* When create texture in async mode.
* @callback onCreateTextureAsyncCallback
* @param {String} url The url.
* @param {LoadTextureResourceSamplerInfo} sampler The sampler info.
* @returns {Promise<any>}
*/
const __ = {
private: Symbol('private'),
}
const _defaultSampler = {
minFilter: ImageFilterType.LinearMipmapLinearFilter,
magFilter: ImageFilterType.LinearFilter,
wrapS: ImageWrapType.Repeat,
wrapT: ImageWrapType.Repeat
};
const _defaultOptions = {};
// #region Private Functions
// Build sampler key.
function _buildSamplerKey(sampler) {
if (!sampler) {
return '';
}
let minFilter = sampler.minFilter ? sampler.minFilter : _defaultSampler.minFilter;
let magFilter = sampler.magFilter ? sampler.magFilter : _defaultSampler.magFilter;
let wrapS = sampler.wrapS ? sampler.wrapS : _defaultSampler.wrapS;
let wrapT = sampler.wrapT ? sampler.wrapT : _defaultSampler.wrapT;
return `${minFilter};${magFilter};${wrapS};${wrapT}`;
}
// Build url key.
function _buildUrlKey(url, flipY) {
if (Utils.isString(url)) {
// gltf no url, so url is empty string
if (url.length) {
return url + ';' + flipY;
}
return '';
}
else if (Utils.isArray(url)) {
return url.join(';') + flipY;
}
else {
return '';
}
}
// Get texture by sampler key.
function _getTexture(textures, samplerKey) {
let texture = textures.get(samplerKey);
if (!texture) {
return null;
}
// Check whether it has been disposed
if (texture.disposed) {
textures.delete(samplerKey);
return null;
}
return texture;
}
// Clear resources.
function _clearResources(resourcesMap) {
resourcesMap.forEach(resources => {
resources.forEach(resource => {
resource.release();
});
});
resourcesMap.clear();
}
// #endregion
/**
* @class BaseTextureManager
* The base texture manager.
* @memberof THING
*/
class BaseTextureManager {
/**
* The base texture manager.
* @private
*/
constructor() {
/**
* When create texture.
* @member {OnCreateTextureCallback} onCreateTexture
* @memberof THING.BaseTextureManager
* @instance
* @private
*/
/**
* When create texture in async mode.
* @member {onCreateTextureAsyncCallback} onCreateTextureAsync
* @memberof THING.BaseTextureManager
* @instance
* @private
*/
this[__.private] = {};
let _private = this[__.private];
_private.texturesMap = new Map();
}
// #region Private
_hookTexture(texture) {
let _private = this[__.private];
// Change map key when update sampler
texture.onUpdateSampler = (scope, sampler) => {
let urlKey = _buildUrlKey(scope.url, scope.flipY);
// Delete the old texture cache
let textures = _private.texturesMap.get(urlKey);
if (!textures) {
return;
}
for (let [key, texture] of textures) {
if (texture == scope) {
textures.delete(key);
// Add texture with new sampler values into cache
let samplerKey = _buildSamplerKey(sampler);
textures.set(samplerKey, scope);
return;
}
}
};
// Change map key when update url
texture.onUpdateUrl = (scope) => {
// Delete the old texture cache
for (let [url, textures] of _private.texturesMap) {
for (let [samplerKey, texture] of textures) {
if (texture == scope) {
textures.delete(samplerKey);
let urlKey = _buildUrlKey(scope.url, scope.flipY);
if (urlKey) {
// Add texture with new url into cache
let texturesCache = _private.texturesMap.get(urlKey);
if (texturesCache) {
texturesCache.set(samplerKey, scope);
}
else {
texturesCache = new Map();
texturesCache.set(samplerKey, scope);
_private.texturesMap.set(urlKey, texturesCache);
}
}
return;
}
}
}
};
}
_updateTexture(url, samplerKey, texture) {
let _private = this[__.private];
let textures = new Map();
textures.set(samplerKey, texture);
_private.texturesMap.set(url, textures);
}
_loadTexture(url, samplerKey, texture) {
this._hookTexture(texture);
this._updateTexture(url, samplerKey, texture);
}
_createTexture(url, urlKey, sampler, options) {
let samplerKey = _buildSamplerKey(sampler);
// Create texture
if (this.onCreateTexture) {
let texture = this.onCreateTexture(url, sampler, options);
if (texture) {
this._loadTexture(urlKey, samplerKey, texture);
}
return texture;
}
// Create texture in async mode
else if (this.onCreateTextureAsync) {
return this.onCreateTextureAsync(url, sampler, options).then((texture) => {
if (texture) {
this._loadTexture(urlKey, samplerKey, texture);
}
return texture;
});
}
// Do not how to create texture, we should impl create texture interface at least
else {
return null;
}
}
// #endregion
/**
* Dispose.
* @private
*/
dispose() {
let _private = this[__.private];
_clearResources(_private.texturesMap);
}
/**
* Load texture from URL.
* @param {String} url The resource path.
* @param {LoadTextureResourceSamplerInfo} sampler The sampler info.
* @param {Object} options The options.
* @returns {THING.BaseTexture|Promise<any>}
* @private
*/
load(url, sampler = _defaultSampler, options = _defaultOptions) {
let _private = this[__.private];
// Build the url key
let urlKey = _buildUrlKey(url, Utils.parseValue(options['flipY'], true));
if (!urlKey) {
return null;
}
// Get cache map
let textures = _private.texturesMap.get(urlKey);
if (!textures) {
return this._createTexture(url, urlKey, sampler, options);
}
// Build key for sampler info
let samplerKey = _buildSamplerKey(sampler);
let texture = _getTexture(textures, samplerKey);
if (!texture) {
return this._createTexture(url, urlKey, sampler, options);
}
// Hit cache !
if (texture) {
texture.addRef();
return texture;
}
}
/**
* Load texture from URL in async mode.
* @param {String} url The resource path.
* @param {LoadTextureResourceSamplerInfo} sampler The sampler info.
* @param {Object} options The options.
* @returns {Promise<any>}
* @private
*/
loadAsync(url, sampler = _defaultSampler, options = _defaultOptions) {
let texture = this.load(url, sampler, options);
if (texture) {
if (texture.isBaseTexture) {
return texture.waitForComplete();
}
else {
return texture.then((result) => {
return result.waitForComplete();
});
}
}
else {
return Promise.reject(`Load '${url}' texture failed`);
}
}
}
export { BaseTextureManager }