Home Reference Source Test

src/component/Renderable2DGrid.js

import Renderable from './Renderable';
import Matrix3 from '../render/WebGL/Matrix3';
import ProgramManager from '../manager/ProgramManager';
import TextureManager from '../manager/TextureManager';
import RenderManager from '../manager/RenderManager';
import Renderable2DMultitex from './Renderable2DMultitex';

var dataTexCounter = 0;

/**
 * Renderable2DGrid is a renderable component which uses two textures to draw
 * tilemaps using the GPU, and just one renderable. Renderable2DGrid will expect one
 * texture for sprites, and another containing the data for which tile goes where.
 * Additionaly, viewport support is added to make large maps require one time loads into self
 * managed data textures.
 */
export default class Renderable2DGrid extends Renderable2DMultitex {
    /**
     * Constructor for Renderable2D grid. Sets up data and initializes a data texture.
     * 
     * @param {string} mapTilesTextureName Name of sprite sheet texture.
     * @param {Array} gridData Array of indexes for each tile on the map.
     * @param {number} width Width of the map.
     * @param {number} height Height of the map.
     * @param {number} tileViewWidth Width of the viewport.
     * @param {number} tileViewHeight Height of the viewport.
     */
    constructor(mapTilesTextureName, gridData, width, height, tileViewWidth, tileViewHeight) {
        super(ProgramManager.getProgram('2DGrid'));
        this.className = 'Renderable2DGrid';

        this.dataArray = new Uint16Array(width * height);

        /*
        [0][0] = 0 = Map data tiles width.

        [0][1] = 1 = Map data tiles height.

        [0][2] = 2 = Map tiles in the texture width.

        [0][3] = 3 = Map tiles in the texture height.


        [1][0] = 4 = Map viewport width.

        [1][1] = 5 = Map viewport height.

        [1][2] = 6 = Map viewport x1.

        [1][3] = 7 = Map viewport y1.


        [2][0] = 8 = TileSize width normalized.

        [2][1] = 9 = TileSize height normalized. 
        */
        this.shaderTileData = [];
        this.mapTilesTexture = TextureManager.getTexture(mapTilesTextureName);
        this.addTexture(this.mapTilesTexture);
        this.mapTilesDataTextureName = mapTilesTextureName + 'Data' + dataTexCounter++;
        this.setGridData(gridData, width, height, tileViewWidth, tileViewHeight);
    }

    updateDataArray(data, x1, y1, width, height) {
        if (data == null)
            return;
        let dataY = 0;
        for (let y = y1; y < y1 + height; y++) {
            this.dataArray.set(data.slice(dataY * width, (dataY + 1) * width), x1 + (y * width));
            dataY++;
        }
    }

    /**
     * Updates the data texture by quadrant. Quadrant is specified by the x1, y1, 
     * width, and height parameters.
     * 
     * @param {Array} data Array of values indicating the index of each sprite.
     * @param {number} x1 Top left x position to update.
     * @param {number} y1 Top left y position to update.
     * @param {number} width Width (in tiles) of data to update.
     * @param {number} height Height (in tiles) of data to update.
     */
    updateGridData(data, x1, y1, width, height) {
        this.updateDataArray(data, x1, y1, width, height);
        this.buildShaderTileData(x1, y1, width, height);
        TextureManager.updateDataTexture(this.mapTilesDataTextureName, this.dataArray, x1, y1, width, height);
        this.requestRedraw();
    }

    /**
     * 
     * 
     * @param {*} data 
     * @param {*} x1 
     * @param {*} y1 
     * @param {*} width 
     * @param {*} height 
     */
    updateGridDataViewport(data, x1, y1, width, height, viewportWidth, viewportHeight) {
        this.updateDataArray(data, x1, y1, width, height);
        this.buildShaderTileData(x1, y1, viewportWidth, viewportHeight);
        TextureManager.updateDataTexture(this.mapTilesDataTextureName, this.dataArray, x1, y1, width, height);
        this.requestRedraw();
    }

    setGridData(data, width, height, tileViewWidth, tileViewHeight) {
        this.updateDataArray(data, 0, 0, width, height);
        let texture = TextureManager.addDataTexture(this.mapTilesDataTextureName, this.dataArray, RenderManager.GL.R16UI, RenderManager.GL.RED_INTEGER, RenderManager.GL.UNSIGNED_SHORT, -1, -1, width, height);
        this.addTexture(texture);
        /*
        [0][0] = 0 = Map data tiles width.
        [0][1] = 1 = Map data tiles height.
        [0][2] = 2 = Map tiles in the texture width.
        [0][3] = 3 = Map tiles in the texture height.

        [1][0] = 4 = Map viewport width.
        [1][1] = 5 = Map viewport height.
        [1][2] = 6 = Map viewport x1.
        [1][3] = 7 = Map viewport y1.

        [2][0] = 8 = TileSize width normalized.
        [2][1] = 9 = TileSize height normalized. 
        */
        this.shaderTileData = [
            width, height, this.mapTilesTexture.framesWidth, this.mapTilesTexture.framesHeight, 
            tileViewWidth, tileViewHeight, 0, 0, 
            1 / this.mapTilesTexture.framesWidth, 1 / this.mapTilesTexture.framesHeight, 1, 1, 
            1, 1, 1, 1
        ];
    }

    /**
     * Sets the viewport of the map grid to x1, y1, width, height (in tiles).
     * 
     * @param {number} viewportX1 Top left x position of the viewport.
     * @param {number} viewportY1 Top left y position of the viewport.
     * @param {number} viewportWidth Width of the viewport
     * @param {number} viewportHeight 
     */
    buildShaderTileData(viewportX1, viewportY1, viewportWidth, viewportHeight) {
        this.shaderTileData[4] = viewportWidth;
        this.shaderTileData[5] = viewportHeight;
        this.shaderTileData[6] = viewportX1;
        this.shaderTileData[7] = viewportY1;
    }

    onEnd() {
        this.removeFromViewports();
        TextureManager.removeTexture(this.mapTilesDataTextureName);
    }

    /**
     * Updates the uniforms of this renderable. Requires a position matrix for 
     * perspective calculations by the RendererManager.
     * 
     * @param {Matrix3} positionMatrix Position matrix of this renderable.
     */
    setUniformData(positionMatrix) {
        if (this.textures.length < 2)
            return false;
        this.program.setUniforms({
            'u_color': this.color.color,
            'u_matrix': positionMatrix,
            'u_depth': this.depth,
            'u_tileData': this.shaderTileData,
            'u_texture': 0,
            'u_mapDataTexture': 1,
        });
        return true;
    }
}