Source: water/MagoFluid.js

import {ShaderLoader} from "../ShaderLoader.js";
import {FluidEngine} from "./FluidEngine.js";
import {MagoFluidOptions} from "./MagoFluidOptions.js";
import * as Cesium from "cesium";
import {Extent} from "../Extent.js";

import grid64 from '/src/assets/grid/64x64.glb';
import grid128 from '/src/assets/grid/128x128.glb';
import grid256 from '/src/assets/grid/256x256.glb';
/*import grid512 from '/src/assets/grid/512x512.glb';
import grid1024 from '/src/assets/grid/1024x1024.glb';*/

/**
 * MagoFluid is a class that creates a water simulation on a globe.
 * @class
 * @param {Cesium.Viewer} viewer - Cesium Viewer instance
 * @example
 * const options = {
 *    lon: 126.978388,
 *    lat: 37.566610,
 *    cellSize: 1,
 *    gridSize: 512,
 * }
 * const magoWaterSimulation = new MagoFluid(viewer);
 * magoWaterSimulation.initBase(options);
 * magoWaterSimulation.start();
 * magoWaterSimulation.addRandomSourcePosition();
 */
export class MagoFluid {

    /**
     * Constructor for MagoFluid class
     * @param viewer Cesium Viewer instance
     */
    constructor (viewer) {
        console.log('[MCT][FLUID] constructor');

        /**
         * Cesium Viewer instance
         * @type {Cesium.Viewer}
         */
        this.viewer = undefined;

        /**
         * Custom shader loader
         * @type {ShaderLoader}
         */
        this.customShaderLoader = undefined;

        /**
         * Water Simulator
         * @type {FluidEngine}
         */
        this.waterSimulator = undefined;

        //this.extent = {minLon: 0, minLat: 0, maxLon: 0, maxLat: 0};

        this.extent = new Extent(0, 0, 0, 0);
        this.gridPrimitive = undefined;
        this.customShader = undefined;

        this.waterMap = undefined;
        this.waterTextureArray = undefined;
        this.waterTextureUniform = undefined;

        this.fluxTextureArray = undefined;
        this.fluxTextureUniform = undefined;

        this.terrainMap = undefined;
        this.terrainTextureArray = undefined;
        this.terrainTextureUniform = undefined;

        this.buildingHeightArray = [];

        this.intervalObject = undefined;

        this.info = {
            status: 'off',
            totalWater: 0,
        }

        /**
         * Default options for the water simulation.
         * @type MagoFluidOptions
         */
        this.options = new MagoFluidOptions();
        this.init(viewer);
    }

    /**
     * Initializes the viewer to render points on a globe.
     * @param viewer
     */
    init(viewer) {
        console.log('[MCT][FLUID] init');
        this.viewer = viewer;
        this.waterSimulator = new FluidEngine(this.options);
        this.customShaderLoader = new ShaderLoader("/src/customShaders");

        if (this.gridPrimitive) {
            this.viewer.scene.primitives.remove(this.gridPrimitive);
        }
        if (this.intervalObject) {
            clearInterval(this.intervalObject);
        }
        this.extent = new Extent(0, 0, 0, 0);
        this.gridPrimitive = undefined;
        this.customShader = undefined;
        this.waterMap = undefined;
        this.waterTextureArray = undefined;
        this.waterTextureUniform = undefined;
        this.fluxTextureArray = undefined;
        this.fluxTextureUniform = undefined;
        this.terrainMap = undefined;
        this.terrainTextureArray = undefined;
        this.terrainTextureUniform = undefined;
        this.buildingHeightArray = [];
        this.intervalObject = undefined;
        this.info.status = 'init';
    }

    /**
     * Creates a rectangle on the globe.
     * @param extent
     * @returns {Entity}
     */
    createRectangle(extent = this.extent) {
        console.log('[MCT][FLUID] createRectangle');
        const rectangle = Cesium.Rectangle.fromDegrees(extent.getMinLon(), extent.getMinLat(), extent.getMaxLon(), extent.getMaxLat());
        return this.viewer.entities.add({
            rectangle: {
                coordinates: rectangle,
                material: Cesium.Color.BLACK.withAlpha(0.1),
                outline: true,
                outlineColor: Cesium.Color.BLACK,
                clampToGround: true
            },
            polyline: {
                positions: Cesium.Cartesian3.fromRadiansArray([
                    rectangle.west, rectangle.south,
                    rectangle.east, rectangle.south,
                    rectangle.east, rectangle.north,
                    rectangle.west, rectangle.north,
                    rectangle.west, rectangle.south
                ]),
                width: 3,
                material: Cesium.Color.RED.withAlpha(0.5),
                clampToGround: true
            }
        });
    }

    calcLonLat(center, offset)  {
        const transformMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(center);
        let translatedMatrix = Cesium.Matrix4.multiplyByTranslation(transformMatrix, offset, new Cesium.Matrix4());
        let translation = Cesium.Matrix4.getTranslation(translatedMatrix, new Cesium.Cartesian3());
        let cartographic = Cesium.Cartographic.fromCartesian(translation);
        return {
            lon: Cesium.Math.toDegrees(cartographic.longitude),
            lat: Cesium.Math.toDegrees(cartographic.latitude)
        }
    }

    calcLonLatWithCenterMatrix(centerMatrix, offset)  {
        let translatedMatrix = Cesium.Matrix4.multiplyByTranslation(centerMatrix, offset, new Cesium.Matrix4());
        let translation = Cesium.Matrix4.getTranslation(translatedMatrix, new Cesium.Cartesian3());
        let cartographic = Cesium.Cartographic.fromCartesian(translation);
        return {
            lon: Cesium.Math.toDegrees(cartographic.longitude),
            lat: Cesium.Math.toDegrees(cartographic.latitude)
        }
    }

    calcExtent(options) {
        const gridSize = !options.gridSize ? 8 : options.gridSize;
        const cellSize = !options.cellSize ? 1 : options.cellSize;

        //const extent = new Extent(0, 0, 0, 0);

        const realGridSize = gridSize * cellSize;
        const center = Cesium.Cartesian3.fromDegrees(options.lon, options.lat);
        const leftDown = new Cesium.Cartesian3(-realGridSize / 2.0, -realGridSize / 2.0, 0.0);
        const rightTop = new Cesium.Cartesian3(realGridSize / 2.0, realGridSize / 2.0, 0.0);
        const leftDownLonLat = this.calcLonLat(center, leftDown);
        const rightTopLonLat = this.calcLonLat(center, rightTop);

        const extent = Extent.createFromDegrees(leftDownLonLat.lon, leftDownLonLat.lat, rightTopLonLat.lon, rightTopLonLat.lat);
        return extent;
    }

    /**
     * Initializes the water simulation with the given options.
     * @param options
     * @returns {Promise<void>}
     */
    async initBase(options) {
        console.log('[MCT][FLUID] initBase');
        this.info.status = 'loading';
        this.clearWaterSourcePositions();
        this.clearWaterMinusSourcePositions();
        this.clearSeaWallPositions();

        await this.initWaterSimulation(options);
        await this.initializeWater();
        await this.initializeTerrain();
        //this.initWaterSource();
        this.renderFrame(true);
        this.info.status = 'ready';
    }

    /**
     * Starts the water simulation.
     * @returns {Promise<void>}
     */
    async start() {
        await this.startFrame();
        this.info.status = 'running';
    }

    /**
     * Stops the water simulation.
     */
    stop() {
        if (this.intervalObject) {
            clearInterval(this.intervalObject);
        }
        this.info.status = 'stopped';
    }

    /*initWaterSource() {
        const gridSize = this.options.gridSize;
        let cellPosition = ((gridSize * (gridSize / 2)) + (gridSize / 2)) * 4;
        console.log('[MCT][FLUID] setWaterSourcePosition', cellPosition);
        this.options.waterSourcePosition = cellPosition;
    }*/

    /*setWaterSourcePosition(lon, lat) {
        let cellPosition = this.findCellFromDegree(lon, lat);
        console.log('[MCT][FLUID] setWaterSourcePosition', cellPosition);
        this.options.waterSourcePosition = cellPosition;
    }*/

    /**
     * Clears the water source positions.
     */
    clearWaterSourcePositions() {
        this.options.waterSourcePositions = [];
    }

    /**
     * Clears the water minus source positions.
     */
    clearWaterMinusSourcePositions() {
        this.options.waterMinusSourcePositions = [];
    }

    /**
     * Clears the sea wall positions.
     */
    clearSeaWallPositions() {
        this.options.waterSeawallPositions = [];
    }

    /**
     * Adds a water source position to the simulation.
     * @param lon
     * @param lat
     * @returns {{lon: number, lat: number}}
     */
    addWaterSourcePosition(lon, lat) {
        let cellPosition = this.findCellFromDegree(lon, lat);
        let centerPosition = this.calcCellCenterPosition(cellPosition);

        this.options.waterSourcePositions.push(cellPosition * 4);
        console.log('[MCT][FLUID] setWaterSourcePosition', cellPosition * 4);
        return centerPosition;
    }

    /**
     * Adds a water minus source position to the simulation.
     * @param lon
     * @param lat
     * @returns {{lon: number, lat: number}}
     */
    addWaterMinusSourcePosition(lon, lat) {
        let cellPosition = this.findCellFromDegree(lon, lat);
        let centerPosition = this.calcCellCenterPosition(cellPosition);

        this.options.waterMinusSourcePositions.push(cellPosition * 4);
        console.log('[MCT][FLUID] setWaterMinusSourcePosition', cellPosition * 4);
        return centerPosition;
    }

    /**
     * Adds a sea wall position to the simulation.
     * @param lon
     * @param lat
     * @returns {{lon: number, lat: number}}
     */
    addSeaWallPosition(lon, lat) {
        let cellPosition = this.findCellFromDegree(lon, lat);
        let centerPosition = this.calcCellCenterPosition(cellPosition);

        this.options.waterSeawallPositions.push(cellPosition * 4);
        console.log('[MCT][FLUID] setWaterSeawallPosition', cellPosition * 4);
        return centerPosition;
    }

    /**
     * Adds a random water source position to the simulation.
     */
    addRandomSourcePosition() {
        const gridSize = this.options.gridSize;
        const max = gridSize * gridSize;

        const randomSourceCell = Math.floor(Math.random() * max) * 4;
        console.log('[MCT][FLUID] setWaterSourcePosition', randomSourceCell);
        this.options.waterSourcePositions.push(randomSourceCell);
        //this.options.waterSourceAmount = Math.floor(Math.random() * 10);
    }

    initOptions(options) {
        if (!options) {
            return;
        }

        if (options.cellSize) {
            this.options.cellSize = options.cellSize < 1 ? 1 : options.cellSize;
        }
        if (options.gridSize) {
            this.options.gridSize = options.gridSize < 8 ? 8 : options.gridSize;
        }
        if (options.interval) {
            this.options.interval = options.interval;
        }
    }

    getGridUrl(gridSize) {
        let grid;
        if (gridSize === 64) {
            grid = grid64;
        } else if (gridSize === 128) {
            grid = grid128;
        } else if (gridSize === 256) {
            grid = grid256;
        } /*else if (gridSize === 512) {
            grid = grid512;
        } else if (gridSize === 1024) {
            grid = grid1024;
        }*/ else {
            throw new Error(`[MCT][FLUID] ${gridSize}x${gridSize}.glb grid file is not found`);
        }
        return grid;
    }

    async initWaterSimulation(options) {
        this.initOptions(options);
        const gridSize = this.options.gridSize;
        const cellSize = this.options.cellSize;

        this.interval = this.options.interval;
        this.extent = this.calcExtent(options);
        const center = Cesium.Cartesian3.fromDegrees(options.lon, options.lat);
        const scaleVector = new Cesium.Cartesian3(cellSize, cellSize, 1.0);
        let transform = Cesium.Transforms.eastNorthUpToFixedFrame(center);
        transform = Cesium.Matrix4.multiplyByScale(transform, scaleVector, new Cesium.Matrix4());

        let gridModelUrl;
        if (options.gridUrl) {
            gridModelUrl = options.gridUrl;
        } else {
            gridModelUrl = this.getGridUrl(gridSize);
        }

        const gltf = await Cesium.Model.fromGltfAsync({
            url: gridModelUrl,
            //url: grid,
            modelMatrix: transform,
            enableDebugWireframe: true,
            debugWireframe: false,
            backFaceCulling: false,
            shadows: Cesium.ShadowMode.RECEIVE_ONLY,
        });
        this.gridPrimitive = gltf;

        const vertexShaderText = await this.customShaderLoader.getShaderSource('default-vertex-shader.vert');
        const fragmentShaderText = await this.customShaderLoader.getShaderSource('default-fragment-shader.frag');
        this.waterTextureArray = new Uint8Array(this.options.gridSize * this.options.gridSize * 4);
        this.waterTextureUniform = new Cesium.TextureUniform({
            typedArray: this.waterTextureArray,
            width: this.options.gridSize,
            height: this.options.gridSize,
            minificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            repeat: false
        });

        this.fluxTextureArray = new Uint8Array(this.options.gridSize * this.options.gridSize * 4);
        this.fluxTextureUniform = new Cesium.TextureUniform({
            typedArray: this.fluxTextureArray,
            width: this.options.gridSize,
            height: this.options.gridSize,
            minificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            repeat: false
        });

        /*this.waterNormalTexture = new Cesium.TextureUniform({
            url: "/data/water-diff.png",
            minificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            repeat: true
        });*/
        //this.customShader.setUniform('u_water_normal_texture', this.waterNormalTexture);

        //loadBuildingTexture();

        /*this.buildingTextureArray = new Uint8Array(this.gridSize * this.gridSize * 4);
        this.buildingTextureUniform = new Cesium.TextureUniform({
            url: "/data/grid/sample.png",
            minificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            repeat: false
        });*/

        this.terrainTextureArray = new Uint8Array(this.options.gridSize * this.options.gridSize * 4);
        this.terrainTextureUniform = new Cesium.TextureUniform({
            typedArray: this.terrainTextureArray,
            width: this.options.gridSize,
            height: this.options.gridSize,
            minificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            magnificationFilter: Cesium.TextureMagnificationFilter.NEAREST,
            repeat: false
        });

        this.customShader = new Cesium.CustomShader({
            uniforms: {
                u_water_skirt: {
                    type: Cesium.UniformType.BOOL,
                    value: this.options.waterSkirt
                }, u_water: {
                    type: Cesium.UniformType.SAMPLER_2D,
                    value: this.waterTextureUniform
                }, u_terrain: {
                    type: Cesium.UniformType.SAMPLER_2D,
                    value: this.terrainTextureUniform
                }, u_flux: {
                    type: Cesium.UniformType.SAMPLER_2D,
                    value: this.fluxTextureUniform
                }, u_cell_size: {
                    type: Cesium.UniformType.FLOAT,
                    value: this.options.cellSize
                }, u_grid_size: {
                    type: Cesium.UniformType.FLOAT,
                    value: this.options.gridSize
                }, u_max_height: {
                    type: Cesium.UniformType.FLOAT,
                    value: this.options.maxHeight
                }, u_color_intensity: {
                    type: Cesium.UniformType.FLOAT,
                    value: this.options.colorIntensity
                }, u_water_brightness: {
                    type: Cesium.UniformType.FLOAT,
                    value: this.options.waterBrightness
                }, u_height_palette: {
                    type: Cesium.UniformType.BOOL,
                    value: this.options.heightPalette
                }, u_water_color: {
                    type: Cesium.UniformType.VEC3,
                    value: this.options.waterColor,
                },
                /*u_water_normal_texture: {
                    type: Cesium.UniformType.SAMPLER_2D,
                    value: this.waterNormalTexture
                }*/
            },
            varyings: {
                v_normal: Cesium.VaryingType.VEC3,
                v_water_height: Cesium.VaryingType.FLOAT,
                v_temp_water_height: Cesium.VaryingType.FLOAT,
                v_texCoord: Cesium.VaryingType.VEC2,
                v_isNoWater: Cesium.VaryingType.FLOAT,
                //v_normal_texture: Cesium.VaryingType.VEC3,
                v_flux_value: Cesium.VaryingType.VEC2,
                v_water_color: Cesium.VaryingType.VEC3
            },
            vertexShaderText: vertexShaderText,
            fragmentShaderText: fragmentShaderText,
            lightingModel: Cesium.LightingModel.UNLIT,
            mode: Cesium.CustomShaderMode.MODIFY_MATERIAL,
            translucencyMode: Cesium.CustomShaderTranslucencyMode.TRANSLUCENT,
        });
        gltf.customShader = this.customShader;

        this.viewer.scene.primitives.add(gltf);
    }

    startFrame() {
        console.log('[MCT][FLUID] startWaterSimulation');
        let stepCount = 0;

        if (this.intervalObject) {
            clearInterval(this.intervalObject);
        }

        this.intervalObject = setInterval(() => {
            const isInitialFrame = stepCount === 0;
            this.renderFrame(isInitialFrame);
            stepCount++;
        }, this.options.interval);
    }

    /**
     * Saves the water map image as a PNG file.
     */
    saveWaterMapImage() {
        const simulationInfo = this.waterSimulator.simulationInfo;
        this.waterSimulator.saveUint8ArrayAsPNG(simulationInfo.waterUint8Array, this.options.gridSize, this.options.gridSize);
    }

    renderFrame(isFirstFrame) {
        if (!this.customShader) {
            return;
        }

        const simulationInfo = this.waterSimulator.simulationInfo;

        this.waterSimulator.calculateSimulation();
        this.info.totalWater = simulationInfo.totalWater;
        this.waterTextureUniform.typedArray = simulationInfo.waterUint8Array;
        this.fluxTextureUniform.typedArray = simulationInfo.fluxUint8Array;

        this.customShader.setUniform('u_water', this.waterTextureUniform);
        this.customShader.setUniform('u_flux', this.fluxTextureUniform);
        this.customShader.setUniform('u_color_intensity', this.options.colorIntensity);
        this.customShader.setUniform('u_water_skirt', this.options.waterSkirt);
        this.customShader.setUniform('u_water_brightness', this.options.waterBrightness);
        this.customShader.setUniform('u_height_palette', this.options.heightPalette);
        this.customShader.setUniform('u_water_color', this.options.waterColor);

        if (isFirstFrame) {
            console.log('[MCT][FLUID] render first frame');
            this.terrainTextureUniform.typedArray = this.convertMapToArray(this.terrainMap, this.options.maxHeight);
            this.waterSimulator.setTerrainTexture(this.terrainTextureUniform.typedArray);
            this.customShader.setUniform('u_terrain', this.terrainTextureUniform);
            //this.customShader.setUniform('u_water_normal_texture', this.waterNormalTexture);
        }
    }

    /**
     * Initializes the water simulation.
     * @returns {Promise<void>}
     */
    async initializeWater(){
        console.log('[MCT][FLUID] initializeWater');
        const gridSize = this.options.gridSize;
        this.waterMap = new Array(gridSize * gridSize).fill(0);
        await this.waterSimulator.resetWaterTexture();
    }

    async initializeTerrain() {
        console.log('[MCT][FLUID] initializeTerrain');
        const gridSize = this.options.gridSize;
        let cellSize = this.options.cellSize;
        this.terrainMap = new Array(gridSize * gridSize).fill(0);
        const terrainProvider = this.viewer.scene.terrainProvider;

        const extent = this.extent;
        const minLon = extent.getMinLon();
        const minLat = extent.getMinLat();

        if (terrainProvider instanceof Cesium.EllipsoidTerrainProvider) {
            /*const terrainHeight = 0;
            for (let i = 0; i < gridSize; i++) {
                for (let j = 0; j < gridSize; j++) {
                    if (j > gridSize / 3 && i > gridSize / 3) {
                        this.terrainMap[this.findIndex(j, i)] = j / 4;
                    }
                }
            }*/
        } else if (terrainProvider instanceof Cesium.CesiumTerrainProvider) {
            const leftBottomCartesian = Cesium.Cartesian3.fromDegrees(minLon, minLat);
            const centerMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(leftBottomCartesian);
            let positions = [];
            for (let i = 0; i < gridSize; i++) {
                for (let j = 0; j < gridSize; j++) {
                    let realGridSize = gridSize * cellSize;
                    let realj = j * cellSize;
                    let reali = i * cellSize;

                    let gridPosition = this.calcLonLatWithCenterMatrix(centerMatrix, new Cesium.Cartesian3(realGridSize - reali, realj, 0));
                    let position = new Cesium.Cartographic(Cesium.Math.toRadians(gridPosition.lon), Cesium.Math.toRadians(gridPosition.lat), 0);
                    positions.push(position);
                }
            }
            const updatedPositions = await Cesium.sampleTerrainMostDetailed(terrainProvider, positions, false);
            //const updatedPositions = await Cesium.sampleTerrain(terrainProvider, 13, positions, false);


            const terrainOffset = 1;
            for (let i = 0; i < gridSize; i++) {
                for (let j = 0; j < gridSize; j++) {

                    this.terrainMap[this.findIndex(j, i)] = updatedPositions[this.findIndex(j, i)].height + terrainOffset;
                }
            }
        }

        if (this.buildingHeightArray.length > 0) {
            const buildingHeightArray = this.buildingHeightArray;
            for (let i = 0; i < gridSize; i++) {
                for (let j = 0; j < gridSize; j++) {
                    const gridIndex = this.findIndex(j, i);
                    this.terrainMap[gridIndex] += buildingHeightArray[gridIndex];
                }
            }
        }
    }

    calcCellCenterPosition(index) {
        const gridSize = this.options.gridSize;
        const cellSize = this.options.cellSize;
        const realGridSize = gridSize * cellSize;
        const realj = (index % gridSize) * cellSize;
        const reali = Math.floor(index / gridSize) * cellSize;

        const leftBottomCartesian = Cesium.Cartesian3.fromDegrees(this.extent.getMinLon(), this.extent.getMinLat());
        const centerMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(leftBottomCartesian);
        let gridPosition = this.calcLonLatWithCenterMatrix(centerMatrix, new Cesium.Cartesian3(realGridSize - reali, realj, 0));

        console.log('[MCT][FLUID] calcCellCenterPosition', gridPosition);

        return gridPosition;
    }

    findCellFromDegree(lon, lat) {
        const extent = this.extent;
        const minLon = extent.getMinLon();
        const minLat = extent.getMinLat();

        const gridSize = this.options.gridSize;
        const cellSize = this.options.cellSize;
        const realGridSize = gridSize * cellSize;

        const leftBottomCartesian = Cesium.Cartesian3.fromDegrees(minLon, minLat);
        const centerMatrix = Cesium.Transforms.eastNorthUpToFixedFrame(leftBottomCartesian);
        for (let i = 0; i < gridSize - 1; i++) {
            for (let j = 0; j < gridSize - 1; j++) {
                let realj = j * cellSize;
                let reali = i * cellSize;
                let gridPosition = this.calcLonLatWithCenterMatrix(centerMatrix, new Cesium.Cartesian3(realGridSize - reali, realj, 0));
                let nextGridPosition = this.calcLonLatWithCenterMatrix(centerMatrix, new Cesium.Cartesian3(realGridSize - (reali - cellSize), (realj - cellSize), 0));

                let inLon = gridPosition.lon <= lon && nextGridPosition.lon >= lon;
                let inLat = gridPosition.lat >= lat && nextGridPosition.lat <= lat;
                if (inLon && inLat) {
                    const index = this.findIndex(j, i);
                    console.log('[MCT][FLUID] findCellFromDegree', index);
                    return index;
                }
            }
        }
        return -1;
    }

    findIndex(x, y) {
        const gridSize = this.options.gridSize;
        return x + y * gridSize;
    }

    convertMapToArray(map, maxValue) {
        const gridSize = this.options.gridSize;
        const arraySize = gridSize * gridSize * 4;
        const mapArray = new Uint8Array(arraySize);
        for (let i = 0; i < arraySize; i += 4) {
            const valueR = map[i / 4];
            const rgba = this.waterSimulator.pack(valueR / maxValue);
            mapArray[i] = rgba[0] * 255;
            mapArray[i + 1] = rgba[1] * 255;
            mapArray[i + 2] = rgba[2] * 255;
            mapArray[i + 3] = rgba[3] * 255;
        }
        return mapArray;
    }
}