Source: shaders/GridParticleProgram.js

/*
 * Copyright 2003-2006, 2009, 2017, 2018, 2019, United States Government, as represented by the Administrator of the
 * National Aeronautics and Space Administration. All rights reserved.
 *
 * The NASAWorldWind/WebWorldWind platform is licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
/**
 * @exports GridParticleProgram
 */
define([
    '../error/ArgumentError',
    './GpuProgram',
    '../util/Logger'
],
    function (
        ArgumentError, 
        GpuProgram,
        Logger) {
        'use strict';

        /**
         * Constructs a new program.
         * Initializes, compiles and links this GLSL program with the source code for its vertex and fragment shaders.
         * <p>
         * This method creates WebGL shaders for the program's shader sources and attaches them to a new GLSL program.
         * This method then compiles the shaders and then links the program if compilation is successful.
         * Use the bind method to make the program current during rendering.
         *
         * @alias GridParticleProgram
         * @constructor
         * @augments GpuProgram
         * @classdesc GridParticleProgram is a GLSL program that draws points and trails representing movement.
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @throws {ArgumentError} If the shaders cannot be compiled, or linking of the compiled shaders into a program
         * fails.
         */
        var GridParticleProgram = function (gl) {
            var vertexShaderSource =
                'attribute vec2 vertexPosition;\n' +

                'uniform mediump int drawMode;\n' +

                //uniforms for MODE_DRAW_PARTICLES
                'uniform sampler2D simParticleSampler;\n' +
                'uniform float simTextureDimension;\n' +
                'uniform vec4 bbox;\n' +

                'varying vec2 texureCoord;\n' +

                'const int MODE_TEX_COPY_FADE = 0;\n' +
                'const int MODE_DRAW_PARTICLES = 1;\n' +

                'const vec2 bitEnc = vec2(1.0, 255.0);\n' +
                'const vec2 bitDec = 1.0 / bitEnc;\n' +

                'vec2 decodePositionFromRGBA(const vec4 color) {\n' +
                '   vec4 roundedColor = floor(color * 255.0 + 0.5) / 255.0;\n' +
                '   float x = dot(roundedColor.rg, bitDec);\n' +
                '   float y = dot(roundedColor.ba, bitDec);\n' +
                '   return vec2(x, y);\n' +
                '}\n' +

                'void main() {\n' +
                '   if (drawMode == MODE_TEX_COPY_FADE) {\n' +
                '       texureCoord = vertexPosition;\n' +
                '       gl_Position = vec4(1.0 - 2.0 * vertexPosition, 0, 1);\n' +
                '   }\n' +
                '   else {\n' +
                '       float row = vertexPosition.x / simTextureDimension;\n' +
                '       vec2 uv = vec2(fract(row), floor(row) / simTextureDimension);\n' +
                '       vec4 color = texture2D(simParticleSampler, uv);\n' +
                '       vec2 pos = decodePositionFromRGBA(color);\n' +
                '       texureCoord = bbox.xy + pos * (bbox.zw - bbox.xy);' +
                '       gl_Position = vec4((2.0 * pos.x - 1.0), (2.0 * pos.y - 1.0), 0.0, 1.0);\n' +
                '       gl_PointSize = 1.0;\n' +
                '   }\n' +
                '}';

            var fragmentShaderSource =
                'precision mediump float;\n' +

                'uniform sampler2D tileOrColorsSampler;\n' +
                'uniform mediump int drawMode;\n' +

                //uniforms for MODE_TEX_COPY_FADE
                //tileOrColorsSampler is used as the tile texture sampler
                'uniform float fadeOpacity;\n' +

                //uniforms for MODE_DRAW_PARTICLES
                //tileOrColorsSampler is used as the colors gradient sampler
                'uniform sampler2D gridSampler;\n' +
                'uniform vec4 gridMinMax;\n' +

                'varying vec2 texureCoord;\n' +

                'const int MODE_TEX_COPY_FADE = 0;\n' +
                'const int MODE_DRAW_PARTICLES = 1;\n' +

                'void main() {\n' +
                '   if (drawMode == MODE_TEX_COPY_FADE) {\n' +
                '       vec4 color = texture2D(tileOrColorsSampler, 1.0 - texureCoord);\n' +
                '       gl_FragColor = vec4(floor(255.0 * color * fadeOpacity) / 255.0);\n' +
                '   }\n' +
                '   else {\n' +
                '       vec2 gridVector = mix(gridMinMax.xy, gridMinMax.zw, texture2D(gridSampler, texureCoord).rg);\n' +
                '       float normalizedDistance = length(gridVector) / length(gridMinMax.zw);\n' +
                '       vec2 textCoord = vec2(fract(16.0 * normalizedDistance), floor(16.0 * normalizedDistance) / 16.0);\n' +
                '       gl_FragColor = texture2D(tileOrColorsSampler, textCoord);\n' +
                '   }\n' +
                '}';

            GpuProgram.call(this, gl, vertexShaderSource, fragmentShaderSource, ['vertexPosition']);

            /**
             * The WebGL location for this program's 'vertexPosition' attribute.
             * @type {Number}
             * @readonly
             */
            this.vertexPositionLocation = this.attributeLocation(gl, 'vertexPosition');

            /**
             * The WebGL location for this program's 'simParticleSampler' uniform.
             * @type {WebGLUniformLocation}
             * @readonly
             */
            this.simParticleSamplerLocation =  this.uniformLocation(gl, 'simParticleSampler');

            /**
             * The WebGL location for this program's 'simTextureDimension' uniform.
             * @type {WebGLUniformLocation}
             * @readonly
             */
            this.simTextureDimensionLocation = this.uniformLocation(gl, 'simTextureDimension');

            /**
             * The WebGL location for this program's 'drawMode' uniform.
             * @type {WebGLUniformLocation}
             * @readonly
             */
            this.drawModeLocation = this.uniformLocation(gl, 'drawMode');

            /**
             * The WebGL location for this program's 'tileOrColorsSampler' uniform.
             * @type {WebGLUniformLocation}
             * @readonly
             */
            this.tileOrColorsSamplerLocation = this.uniformLocation(gl, 'tileOrColorsSampler');

            /**
             * The WebGL location for this program's 'fadeOpacity' uniform.
             * @type {WebGLUniformLocation}
             * @readonly
             */
            this.fadeOpacityLocation = this.uniformLocation(gl, 'fadeOpacity');

            /**
             * The WebGL location for this program's 'gridSampler' uniform.
             * @type {WebGLUniformLocation}
             * @readonly
             */
            this.gridSamplerLocation = this.uniformLocation(gl, 'gridSampler');

            /**
             * The WebGL location for this program's 'gridMinMax' uniform.
             * @type {WebGLUniformLocation}
             * @readonly
             */
            this.gridMinMaxLocation = this.uniformLocation(gl, 'gridMinMax');

            /**
             * The WebGL location for this program's 'bbox' uniform.
             * @type {WebGLUniformLocation}
             * @readonly
             */
            this.bboxLocation = this.uniformLocation(gl, 'bbox');

            //The two modes this program can render.
            
            //Copies and fades data from the previous frame.
            this.MODE_TEX_COPY_FADE = 0;

            //Drawn particles.
            this.MODE_DRAW_PARTICLES = 1;
        };

        /**
         * A string that uniquely identifies this program.
         * @type {string}
         * @readonly
         */
        GridParticleProgram.key = 'WorldWindGpuGridParticleProgram';

        GridParticleProgram.prototype = Object.create(GpuProgram.prototype);

        /**
         * Loads the specified number as the value of this program's 'simParticleSamplerLocation' uniform variable.
         * 
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @param {Number} unit The texture unit.
         */
        GridParticleProgram.prototype.loadSimParticleSamper = function (gl, unit) {
            gl.uniform1i(this.simParticleSamplerLocation, unit - gl.TEXTURE0);
        };

        /**
         * Loads the specified dimension as the value of this program's 'simTextureDimensionLocation' uniform variable..
         *
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @param {Number} dimension The dimension (width or height) of the sim texture. The sim texture's width and height are assumed to be equal.
         * @throws {ArgumentError} If the specified dimension is missing.
         */
        GridParticleProgram.prototype.loadSimTextureDimension = function (gl, dimension) {
            if (dimension == null) {
                throw new ArgumentError(
                    Logger.logMessage(Logger.LEVEL_SEVERE, 'GridParticleProgram', 'loadSimTextureDimension', 'missing dimension'));
            }
            gl.uniform1f(this.simTextureDimensionLocation, dimension);
        };

        /**
         * Loads the specified value as the value of this program's 'drawModeLocation' uniform variable.
         *
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @param {Number} drawMode 
         */
        GridParticleProgram.prototype.loadDrawMode = function (gl, drawMode) {
            gl.uniform1i(this.drawModeLocation, drawMode);
        };

        /**
         * Loads the specified number as the value of this program's 'tileOrColorsSamplerLocation' uniform variable.
         * 
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @param {Number} unit The texture unit.
         */
        GridParticleProgram.prototype.loadTileOrColorsSampler = function (gl, unit) {
            gl.uniform1i(this.tileOrColorsSamplerLocation, unit - gl.TEXTURE0);
        };

        /**
         * Loads the specified number as the value of this program's 'tileOrColorsSamplerLocation' uniform variable.
         * 
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @param {Number} fadeOpacity Controls how fast the particle trails fade on each frame.
         * @throws {ArgumentError} If the specified fadeOpacity is missing.
         */
        GridParticleProgram.prototype.loadFadeOpacity = function (gl, fadeOpacity) {
            if (fadeOpacity == null) {
                throw new ArgumentError(
                    Logger.logMessage(Logger.LEVEL_SEVERE, 'GridParticleProgram', 'loadFadeOpacity', 'missing fadeOpacity'));
            }
            gl.uniform1f(this.fadeOpacityLocation, fadeOpacity);
        };

        /**
         * Loads the specified number as the value of this program's 'gridSamplerLocation' uniform variable.
         * 
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @param {Number} unit The texture unit.
         */
        GridParticleProgram.prototype.loadGridSampler = function (gl, unit) {
            gl.uniform1i(this.gridSamplerLocation, unit - gl.TEXTURE0);
        };

        /**
         * Loads the min and max u and v vectors of the grid as the value of this program's 'gridMinMaxLocation' uniform variable.
         *
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @param {Number} uMin The minimum u value of the grid data.
         * @param {Number} vMin The minimum v value of the grid data.
         * @param {Number} uMax The maximum u value of the grid data.
         * @param {Number} vMax The maximum v value of the grid data.
         * @throws {ArgumentError} If the specified u and v values are missing.
         */
        GridParticleProgram.prototype.loadGridMinMax = function (gl, uMin, vMin, uMax, vMax) {
            if (uMin == null) {
                throw new ArgumentError(
                    Logger.logMessage(Logger.LEVEL_SEVERE, 'GridParticleProgram', 'loadGridMinMax', 'missing uMin'));
            }
            if (vMin == null) {
                throw new ArgumentError(
                    Logger.logMessage(Logger.LEVEL_SEVERE, 'GridParticleProgram', 'loadGridMinMax', 'missing vMin'));
            }
            if (uMax == null) {
                throw new ArgumentError(
                    Logger.logMessage(Logger.LEVEL_SEVERE, 'GridParticleProgram', 'loadGridMinMax', 'missing uMax'));
            }
            if (vMax == null) {
                throw new ArgumentError(
                    Logger.logMessage(Logger.LEVEL_SEVERE, 'GridParticleProgram', 'loadGridMinMax', 'missing vMax'));
            }
            gl.uniform4f(this.gridMinMaxLocation, uMin, vMin, uMax, vMax);
        };

        /**
         * Loads the specified bbox as the value of this program's 'bboxLocation' uniform variable.
         * 
         * @param {WebGLRenderingContext} gl The current WebGL context.
         * @param {Float32Array} bbox A normalised bounding box. (0, 0) is in the top left corner and (1, 1) is in the bottom right corner.
         * @throws {ArgumentError} If the specified bbox is missing.
         */
        GridParticleProgram.prototype.loadBbox = function (gl, bbox) {
            if (bbox == null) {
                throw new ArgumentError(
                    Logger.logMessage(Logger.LEVEL_SEVERE, 'GridParticleProgram', 'loadBbox', 'missing bbox'));
            }
            gl.uniform4fv(this.bboxLocation, bbox);
        };

        return GridParticleProgram;
    });