Using Web-GL with Typescript

Sample 5 - Animated texture.


This page extends the previous example by adding run time texture buffer updates. The rational behind dynamic texture buffer updates permits the use of a sprite sheet ot texture altas to simulate animation.
At the bottom of the code sections, you can see an example of the code in action.
The new code sections are shown in bold, for the sake of brevity, unchanged sections are omitted and denoted by ...
The example can be downloaded from Github Git

<!DOCTYPE html>

<html lang="en">
<head>
    <meta charset="utf-8" />
    <title>TypeScript HTML App</title>
    <link rel="stylesheet" href="app.css" type="text/css" />

    <script id="shader-vs" type="x-shader/x-vertex">
            ...
        </script>

    <script id="shader-fs" type="x-shader/x-fragment">
        ...
    </script>

    <script src="gl-matrix.js"></script>
    <script src="app.js"></script>
</head>

<body>
    <canvas id="CanvasGL" style="border: none;" width="500" height="500"></canvas>
</body>
</html>
The Typescript code below initialises the canvas, shaders, buffers and sets up the camera to draw a simple coloured Quad.

declare var mat4: any;
declare var vec3: any;

class AnimatedTexturedPlane {

    ...

    private textureCoords;
    private animTexture;

    // count = texturewidth / number of frames (1/17 = 0.0588)
    private count: number = 0.0588;
    private startX: number = 0;
    private endX: number = this.count;
    private delay: boolean = false;

    constructor() {

        ...

    }

    ...

    private InitTexture() {
        this.animTexture = this.gl.createTexture();
        this.gl.bindTexture(this.gl.TEXTURE_2D, this.animTexture);

        ...

        this.animTexture.image = new Image();
        var __this = this;
        this.animTexture.image.onload = function () {

            __this.gl.bindTexture(__this.gl.TEXTURE_2D, __this.animTexture);
            __this.gl.texImage2D(__this.gl.TEXTURE_2D, 0, __this.gl.RGBA, __this.gl.RGBA, __this.gl.UNSIGNED_BYTE, __this.animTexture.image);

            // WebGL1 has different requirements for power of 2 images vs non power of 2 images so check if the image is a
            // power of 2 in both dimensions.
            if (__this.IsPowerOf2(__this.animTexture.image.width) && __this.IsPowerOf2(__this.animTexture.image.height)) {
                // Yes, it's a power of 2. Generate mips.
                __this.gl.generateMipmap(__this.gl.TEXTURE_2D);
            } else {
                // No, it's not a power of 2. Turn of mips and set wrapping to clamp to edge
                __this.gl.texParameteri(__this.gl.TEXTURE_2D, __this.gl.TEXTURE_WRAP_S, __this.gl.CLAMP_TO_EDGE);
                __this.gl.texParameteri(__this.gl.TEXTURE_2D, __this.gl.TEXTURE_WRAP_T, __this.gl.CLAMP_TO_EDGE);
                __this.gl.texParameteri(__this.gl.TEXTURE_2D, __this.gl.TEXTURE_MIN_FILTER, __this.gl.LINEAR);
            }

            // Enable transparency
            __this.gl.blendFunc(__this.gl.SRC_ALPHA, __this.gl.ONE_MINUS_SRC_ALPHA);
            __this.gl.enable(__this.gl.BLEND);

        }
        this.animTexture.image.src = "Explosion.png";
    }

    ...

    private InitBuffers() {

        ...

        this.textureCoords = [
            this.startX, 0.0, //uv0
            this.endX,   0.0, //uv1
            this.endX,   1.0, //uv2
            this.startX, 1.0  //uv3
        ];
        this.gl.bufferData(this.gl.ARRAY_BUFFER, new Float32Array(this.textureCoords), this.gl.DYNAMIC_DRAW);

        ...

    }

    private DrawScene() {

        ...

        // Bind the texture to texture unit 0
        this.gl.bindTexture(this.gl.TEXTURE_2D, this.animTexture);

        ...

        this.gl.drawElements(this.gl.TRIANGLES, this.indices.length, this.gl.UNSIGNED_SHORT, 0);
        this.UpdateTextureBuffer();
    }

    ...

    private UpdateTextureBuffer() {

        this.delay = !this.delay;
        if (this.delay) {

            // Bind the buffer
            this.gl.bindBuffer(this.gl.ARRAY_BUFFER, this.cubeVertexTextureCoordBuffer);

            this.startX += this.count;
            this.endX += this.count;
            if (this.endX >= 1) {
                this.startX = 0;
                this.endX = this.count;
            }

            var data = [
                this.startX, 0.0, //uv0
                this.endX,   0.0, //uv1
                this.endX,   1.0, //uv2
                this.startX, 1.0  //uv3
            ];

            // Update the buffer values
            this.gl.bufferSubData(this.gl.ARRAY_BUFFER, 0, new Float32Array(data));

            // Un-bind the buffer
            this.gl.bindBuffer(this.gl.ARRAY_BUFFER, null);
        }
    }
}

window.onload = () => {
    var animatedTexturedPlane = new AnimatedTexturedPlane();
}