API Docs for: 0.1.0
Show:

File: source/js/app/buffers.js

/**
 * @module Buffers
 */
define(['gl', 'shaders', 'lighting'], function (gl, shaderProgram, lighting) {

    /**
     * @class Buffers
     */
    return {

        /**
         * Initialises the buffers.
         * @method initBuffers
         * @param  {AstronomicalObject} obj The object for which we're initialising buffers.
         */
        initBuffers: function (obj) {
            if (obj.spherical) {
                this.initSphericalBuffers(obj);
            } else {
                this.initCuboidalBuffers(obj);
            }
        },

        /**
         * Draws the necessary elements of the object onto the canvas.
         * @method drawElements
         * @param  {AstronomicalObject} obj The object which needs to be drawn.
         */
        drawElements: function (obj) {
            if (obj.spherical) {
                this.drawSphericalElements(obj);
            } else {
                this.drawCuboidalElements(obj);
            }
        },

        /**
         * Called by initBuffers(), this initialises the buffers for spherical objects, e.g. planets, the moon, the Sun.
         * @method initSphericalBuffers
         * @param  {AstronomicalObject} obj The spherical object we're initialising buffers for.
         */
        initSphericalBuffers: function (obj) {
            var radius = obj.radius;
            var latitudeBands = 30;
            var longitudeBands = 30;

            var vertexPositionData = [];
            var normalData = [];
            var textureCoordData = [];
            for (var latNumber = 0; latNumber <= latitudeBands; latNumber++) {
                var theta = latNumber * Math.PI / latitudeBands;
                var sinTheta = Math.sin(theta);
                var cosTheta = Math.cos(theta);

                for (var longNumber = 0; longNumber <= longitudeBands; longNumber++) {
                    var phi = longNumber * 2 * Math.PI / longitudeBands;
                    var sinPhi = Math.sin(phi);
                    var cosPhi = Math.cos(phi);

                    var x = cosPhi * sinTheta;
                    var y = cosTheta;
                    var z = sinPhi * sinTheta;
                    var u = 1 - (longNumber / longitudeBands);
                    var v = 1 - (latNumber / latitudeBands);

                    normalData.push(x);
                    normalData.push(y);
                    normalData.push(z);
                    textureCoordData.push(u);
                    textureCoordData.push(v);
                    vertexPositionData.push(radius * x);
                    vertexPositionData.push(radius * y);
                    vertexPositionData.push(radius * z);
                }
            }

            var indexData = this._getIndexData(latitudeBands, longitudeBands);

            obj.vertexNormalBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.vertexNormalBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(normalData), gl.STATIC_DRAW);
            obj.vertexNormalBuffer.itemSize = 3;
            obj.vertexNormalBuffer.numItems = normalData.length / 3;

            obj.vertexTextureCoordBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.vertexTextureCoordBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoordData), gl.STATIC_DRAW);
            obj.vertexTextureCoordBuffer.itemSize = 2;
            obj.vertexTextureCoordBuffer.numItems = textureCoordData.length / 2;

            obj.vertexPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.vertexPositionBuffer);
            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertexPositionData), gl.STATIC_DRAW);
            obj.vertexPositionBuffer.itemSize = 3;
            obj.vertexPositionBuffer.numItems = vertexPositionData.length / 3;

            obj.vertexIndexBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.vertexIndexBuffer);
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indexData), gl.STATIC_DRAW);
            obj.vertexIndexBuffer.itemSize = 1;
            obj.vertexIndexBuffer.numItems = indexData.length;
        },

        /**
         * A private method called by initSphericalBuffers, which calculates index data used by gl.ELEMENT_ARRAY_BUFFER.
         * @method _getIndexData
         * @param  {int} latitudeBands  Latitude bands of the sphere
         * @param  {int} longitudeBands Longitude bands of the sphere
         * @return {array}              Index data.
         */
        _getIndexData: function (latitudeBands, longitudeBands) {
            var indexData = [];
            for (var latNumber = 0; latNumber < latitudeBands; latNumber++) {
                for (var longNumber = 0; longNumber < longitudeBands; longNumber++) {
                    var first = (latNumber * (longitudeBands + 1)) + longNumber;
                    var second = first + longitudeBands + 1;
                    indexData.push(first);
                    indexData.push(second);
                    indexData.push(first + 1);

                    indexData.push(second);
                    indexData.push(second + 1);
                    indexData.push(first + 1);
                }
            }
            return indexData;
        },

        /**
         * Called by initBuffers(), this initialises the buffers for cuboidal objects, e.g. Saturn's rings
         * @method initCuboidalBuffers
         * @param  {AstronomicalObject} obj The cuboidal object we're initialising buffers for.
         */
        initCuboidalBuffers: function (obj) {
            var width  = obj.radius,
                depth  = width,
                height = 0.1;

            obj.cubeVertexPositionBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.cubeVertexPositionBuffer);
            var vertices = [
                    // Front face
                    -width, -height,  depth,
                    width, -height,  depth,
                    width,  height,  depth,
                    -width,  height,  depth,
                    // Back face
                    -width, -height, -depth,
                    -width,  height, -depth,
                    width,  height, -depth,
                    width, -height, -depth,
                    // Top face
                    -width,  height, -depth,
                    -width,  height,  depth,
                    width,  height,  depth,
                    width,  height, -depth,
                    // Bottom face
                    -width, -height, -depth,
                    width, -height, -depth,
                    width, -height,  depth,
                    -width, -height,  depth,
                    // Right face
                    width, -height, -depth,
                    width,  height, -depth,
                    width,  height,  depth,
                    width, -height,  depth,
                    // Left face
                    -width, -height, -depth,
                    -width, -height,  depth,
                    -width,  height,  depth,
                    -width,  height, -depth
                ];

            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
            obj.cubeVertexPositionBuffer.itemSize = 3;
            obj.cubeVertexPositionBuffer.numItems = 24;
            obj.cubeVertexTextureCoordBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.cubeVertexTextureCoordBuffer);
            var textureCoords = [
                // Front face
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0,
                // Back face
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0,
                0.0, 0.0,
                // Top face
                0.0, 1.0,
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                // Bottom face
                1.0, 1.0,
                0.0, 1.0,
                0.0, 0.0,
                1.0, 0.0,
                // Right face
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0,
                0.0, 0.0,
                // Left face
                0.0, 0.0,
                1.0, 0.0,
                1.0, 1.0,
                0.0, 1.0
            ];

            gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(textureCoords), gl.STATIC_DRAW);
            obj.cubeVertexTextureCoordBuffer.itemSize = 2;
            obj.cubeVertexTextureCoordBuffer.numItems = 24;
            obj.cubeVertexIndexBuffer = gl.createBuffer();
            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.cubeVertexIndexBuffer);
            var cubeVertexIndices = [
                0, 1, 2,      0, 2, 3,    // Front face
                4, 5, 6,      4, 6, 7,    // Back face
                8, 9, 10,     8, 10, 11,  // Top face
                12, 13, 14,   12, 14, 15, // Bottom face
                16, 17, 18,   16, 18, 19, // Right face
                20, 21, 22,   20, 22, 23  // Left face
            ];
            gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(cubeVertexIndices), gl.STATIC_DRAW);
            obj.cubeVertexIndexBuffer.itemSize = 1;
            obj.cubeVertexIndexBuffer.numItems = 36;
        },

        /**
         * Called by drawElements(), this draws the elements that comprise spherical objects.
         * @method drawSphericalElements
         * @param  {AstronomicalObject} obj The spherical object we're drawing
         */
        drawSphericalElements: function (obj) {
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.vertexPositionBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, obj.vertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);

            gl.bindBuffer(gl.ARRAY_BUFFER, obj.vertexTextureCoordBuffer);
            gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, obj.vertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

            gl.bindBuffer(gl.ARRAY_BUFFER, obj.vertexNormalBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexNormalAttribute, obj.vertexNormalBuffer.itemSize, gl.FLOAT, false, 0, 0);

            // transparency
            gl.disable(gl.BLEND);
            gl.enable(gl.DEPTH_TEST);
            gl.uniform1f(shaderProgram.alphaUniform, 1.0);
            gl.uniform1f(shaderProgram.materialShininessUniform, lighting.getShininess());

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.vertexIndexBuffer);
            gl.drawElements(gl.TRIANGLES, obj.vertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
        },

        /**
         * Called by drawElements(), this draws the elements that comprise cuboidal objects.
         * @method drawCuboidalElements
         * @param  {AstronomicalObject} obj The cuboidal object we're drawing
         */
        drawCuboidalElements: function (obj) {
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.cubeVertexPositionBuffer);
            gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute, obj.cubeVertexPositionBuffer.itemSize, gl.FLOAT, false, 0, 0);
            gl.bindBuffer(gl.ARRAY_BUFFER, obj.cubeVertexTextureCoordBuffer);
            gl.vertexAttribPointer(shaderProgram.textureCoordAttribute, obj.cubeVertexTextureCoordBuffer.itemSize, gl.FLOAT, false, 0, 0);

            // transparency
            gl.blendFunc(gl.SRC_ALPHA, gl.ONE);
            gl.enable(gl.BLEND);
            gl.enable(gl.DEPTH_TEST);
            gl.uniform1f(shaderProgram.alphaUniform, 1.0);
            gl.uniform1f(shaderProgram.materialShininessUniform, 0);

            gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, obj.cubeVertexIndexBuffer);
            gl.drawElements(gl.TRIANGLES, obj.cubeVertexIndexBuffer.numItems, gl.UNSIGNED_SHORT, 0);
        }
    };
});