Home Reference Source Test

src/manager/TextureManager.js

import Manager from './Manager';
import DOMManager from './DOMManager';
import AudioManager from './AudioManager';


/**
 * Manages textures in the engine so the RendererManager and Renderables
 * can reference one location for Textures. Essentially a texture library.
 */
export class _TextureManager extends Manager {
    constructor() {
        super();
        this.textures = {};
        this.textureIDs = [];
    }

    /**
     * Returns a texture JSON Object containing the texture, texture data, name and id.
     * 
     * @param {number} texID Name of the texture.
     * 
     * @returns {Texture Object} Returns a JSON object with Texture data.
     */
    getTexture(texID) {
        if (this.textures[texID] == null)
            return null;
        return this.textures[texID];
    }

    _setAvailableTextureID() {
        for (let i = 0; i < this.textureIDs.length; i++)
            if (!this.textureIDs[i]) {
                this.textureIDs[i] = true;
                return i;
            }
        this.textureIDs.push(true);
        return this.textureIDs.length - 1;
    }

    /**
     * Creates a Texture JSON Object and initializes the Texture with WebGL.
     * The Texture is added to the textures array.
     * 
     * @param {string} texName Name of the texture.
     * @param {string} textureImageAsset Path to the texture image to load.
     * @param {number} frameWidth Width of sprite in spritesheet. -1 for full.
     * @param {number} frameHeight Height of sprite in spritesheet. -1 for full.
     * @param {JSON Object} glyphInfo Object defining additional info for letters to be used as a mapping for writing text.
     * Fonts should have these for displaying letters. Format the ojject like so:
     * {
     *     //height of all letters
     *     height: 8, 
     * 
     *     //letter corrosponding to the location and size defined inside.
     *     'A': {
     *         //the x position in pixels for this character.
     *         x: 0,
     * 
     *         //width in pixels for this character.
     *         width: 8
     *     }
     * }
     * 
     * @returns {Promise} A pending promise which will return the texture reference after complete.
     */
    addTexture(texName, textureImageAsset, frameWidth, frameHeight, glyphInfo = null) {
        return this._createTextureFromAsset(textureImageAsset).then((textureData) => {
            let texObj = this.textures[texName] = Object.assign({
                name: texName,
                id: this._setAvailableTextureID(),
                frameWidth,
                frameHeight,
                glyphInfo,
            }, textureData);
            this.textures[texName].framesWidth = texObj.width / texObj.frameWidth;
            this.textures[texName].framesHeight = texObj.height / texObj.frameHeight;
            return this.textures[texName];
        })
    }

    /**
     * 
     * @param {string} texName The name of the texture.
     * @param {Array *} textureData An array object of data, array type depending on the textureInternalFormat.
     * @param {GLint} textureInternalFormat Internal texture format type.
     * @param {GLint} textureFormat Texture format type.
     * @param {number} frameWidth Width of each sub sprite frame.
     * @param {number} frameHeight Height of each sub sprite frame.
     * @param {number} width Width of the texture.
     * @param {number} height Height of the texture.
     */
    addDataTexture(texName, textureData, textureInternalFormat, textureFormat, textureByteType, frameWidth, frameHeight, width, height) {
        let tex = this._createTextureFromData(textureData, textureInternalFormat, textureFormat, textureByteType, width, height);
        this.textures[texName] = Object.assign({
            name: texName,
            id: this._setAvailableTextureID(),
            frameWidth,
            frameHeight,
            width,
            height,
            framesWidth: width / frameWidth,
            framesHeight: height / frameHeight,
            textureInternalFormat,
            textureFormat,
            textureByteType,
        }, {tex});
        return this.textures[texName];
    }

    removeTexture(texName) {
        if (this.textures[texName] == null)
            return;
        
        let id = this.textures[texName].id;
        this.textureIDs[id] = false;
        this.textures[texName] = null;
    }

    updateDataTexture(texName, textureData, x1, y1, width, height) {
        let texture = this.getTexture(texName);
        DOMManager.GL.bindTexture(DOMManager.GL.TEXTURE_2D, texture.tex);
        DOMManager.GL.texSubImage2D(DOMManager.GL.TEXTURE_2D, 0, x1, y1, width, height, texture.textureFormat, texture.textureByteType, textureData);
        return texture;
    }

    addGlyphInfoToTexture(texName, glyphInfo) {
        let texture = this.getTexture(texName);
        if (texture == null) {
            console.error('Texture does not exist!');
            return false;
        }
        texture.glyphInfo = glyphInfo;
    }

    _createTextureFromData(texData, textureInternalFormat, textureFormat, textureByteType, width, height) {
        let tex = DOMManager.GL.createTexture();

        DOMManager.GL.bindTexture(DOMManager.GL.TEXTURE_2D, tex);
        DOMManager.GL.pixelStorei(DOMManager.GL.UNPACK_FLIP_Y_WEBGL, false);
        
        DOMManager.GL.texParameteri(DOMManager.GL.TEXTURE_2D, DOMManager.GL.TEXTURE_MIN_FILTER, DOMManager.GL.NEAREST);
        DOMManager.GL.texParameteri(DOMManager.GL.TEXTURE_2D, DOMManager.GL.TEXTURE_MAG_FILTER, DOMManager.GL.NEAREST);
        DOMManager.GL.texParameteri(DOMManager.GL.TEXTURE_2D, DOMManager.GL.TEXTURE_WRAP_S, DOMManager.GL.CLAMP_TO_EDGE);
        DOMManager.GL.texParameteri(DOMManager.GL.TEXTURE_2D, DOMManager.GL.TEXTURE_WRAP_T, DOMManager.GL.CLAMP_TO_EDGE);

        DOMManager.GL.texImage2D(DOMManager.GL.TEXTURE_2D, 0, textureInternalFormat, width, height, 0, textureFormat, textureByteType, texData);

        return tex;
    }

    /**
     * Private function used for initializing a Texture from a path and
     * binding it with WebGL.
     * 
     * @param {string} asset Path to texture file.
     * 
     * @returns {Promise} Returns a pending promise.
     */
    _createTextureFromAsset(asset) {
        let texInfo = {
            tex: DOMManager.GL.createTexture(),
            width: 0,
            height: 0,
            framesWidth: 0,
            framesHeight: 0,
        };

        return new Promise((res, rej) => {
            let assetLoaded = new Image();
            assetLoaded.addEventListener('load', () => {
                texInfo.width = assetLoaded.width;
                texInfo.height = assetLoaded.height;

                DOMManager.GL.bindTexture(DOMManager.GL.TEXTURE_2D, texInfo.tex);
               
                DOMManager.GL.texParameteri(DOMManager.GL.TEXTURE_2D, DOMManager.GL.TEXTURE_WRAP_S, DOMManager.GL.CLAMP_TO_EDGE);
                DOMManager.GL.texParameteri(DOMManager.GL.TEXTURE_2D, DOMManager.GL.TEXTURE_WRAP_T, DOMManager.GL.CLAMP_TO_EDGE);
        
                DOMManager.GL.bindTexture(DOMManager.GL.TEXTURE_2D, texInfo.tex);
                DOMManager.GL.texImage2D(DOMManager.GL.TEXTURE_2D, 0, DOMManager.GL.RGBA, DOMManager.GL.RGBA, DOMManager.GL.UNSIGNED_BYTE, assetLoaded);
                DOMManager.GL.generateMipmap(DOMManager.GL.TEXTURE_2D);
                return res(texInfo);
            });
            assetLoaded.src = asset;
        })
    }
}

/**
 * Singleton reference to the WebGL Program Manager.
 */
const TextureManager = new _TextureManager();
export default TextureManager;