API Docs for:

File: /home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/generation/geometryBuilder.js

/**
 A **GeometryBuilder** is a builder that you can use to procedurally generate {{#crossLink "Geometry"}}Geometries{{/crossLink}}.

 <a href="../../examples/#geometry_generation_wavyBlocks"><img src="http://i.giphy.com/26gJAMkOxAW5fWlb2.gif"></img></a>

 ## Overview

 * Implements the [Builder pattern](https://en.wikipedia.org/wiki/Builder_pattern).
 * Helps us improve WebGL performance by combining many shapes into the same {{#crossLink "Geometry"}}Geometries{{/crossLink}},
 thus reducing the number of draw calls.
 * A plain JavaScript class, ie. not a xeogl {{#crossLink "Component"}}{{/crossLink}} subclass.

 ## Examples

 * [Generating a 3D sine wave from boxes](../../examples/#geometry_generation_wavyBlocks)

 ## Usage

 * Works by accumulating additions to an internal buffer of geometry vertex and index arrays.
 * Call {{#crossLink "GeometryBuilder/setShape:method"}}setShape(){{/crossLink}} to set its current mesh, and
 {{#crossLink "GeometryBuilder/setMatrix:method"}}setMatrix(){{/crossLink}} to set its current modelling transform.
 * Then, whenever you call {{#crossLink "GeometryBuilder/addShape:method"}}addShape(){{/crossLink}}, it appends the shape, transformed
 by the matrix, to its internal buffer.
 * Finally, call {{#crossLink "GeometryBuilder/build:method"}}build(){{/crossLink}} to dump its buffer into a target {{#crossLink "Geometry"}}{{/crossLink}}.
 * Retains its buffer so that you can call {{#crossLink "GeometryBuilder/build:method"}}build(){{/crossLink}} again, to dump its
 buffer into other {{#crossLink "Geometry"}}Geometries{{/crossLink}} as needed.
 * Call {{#crossLink "GeometryBuilder/reset:method"}}reset(){{/crossLink}} to empty its buffer.

 In the example below we'll use a GeometryBuilder to create something like the screen capture shown above.

 ````javascript
 // Intatiate a GeometryBuilder; note that it's a
 // plain JavaScript object, not a xeogl Component subtype
 var geometryBuilder = new xeogl.GeometryBuilder();

 // Set the current shape we'll be adding to our GeometryBuilder;
 // this can be a Geometry, or just an object containing vertex and indices arrays.
 geometryBuilder.setShape(new xeogl.BoxGeometry());

 // Now add that shape many times, each time setting a different modelling matrix
 // on the GeometryBuilder. As we do this, we are accumulating geometry in a buffer
 // within the GeometryBuilder.

 var matrix = xeogl.math.mat4();
 var height = 3;
 var height2 = height * 2;
 var x;
 var y;
 var z;
 var size = 200;

 for (x = -size; x <= size; x += 2) {
     for (z = -size; z <= size; z += 2) {

        y = ((Math.sin(x * 0.05) * height + Math.sin(z * 0.05) * height)) + height2;

        xeogl.math.identityMat4(matrix);            // Fresh matrix
        xeogl.math.scaleMat4c(.90, y, .90, matrix); // Post-concatenate scaling
        xeogl.math.translateMat4c(x, y, z, matrix); // Post-concatenate translation

        geometryBuilder.setMatrix(matrix);          // Set the current modeling transform matrix

        geometryBuilder.addShape();                 // Add current shape to the buffer, transformed by the matrix
     }
 }

 var geometry = new xeogl.Geometry();               // Dump the buffer into a Geometry component

 geometryBuilder.build(geometry);

 var mesh = new xeogl.Mesh({                    // Create an Mesh with our Geometry attached
    geometry: geometry,
    material: new xeogl.PhongMaterial({
        diffuse: [0.6, 0.6, 0.7]
    })
 });

 mesh.scene.camera.eye = [-200, 50, -200];   // Set initial Camera position

 new xeogl.CameraControl();                         // Allow camera interaction
 ````
 @class GeometryBuilder
 @module xeogl
 @submodule generation
 @constructor
 */
(function () {

    "use strict";

    xeogl.GeometryBuilder = function () {
        this.reset();
    };

    /**
     * Sets the shape that will be added to this GeometryBuilder on each subsequent call to {{#crossLink "GeometryBuilder/addShape:method"}}addShape(){{/crossLink}}.
     *
     * The shape can be either a {{#crossLink "Geometry"}}{{/crossLink}} or a JavaScript object containing vertex and index arrays.
     *
     * @method setShape
     * @param {Geometry|*} shape The shape to add.
     * @returns this
     */
    xeogl.GeometryBuilder.prototype.setShape = function (shape) {
        this._shape = shape;
        return this;
    };

    /**
     * Sets the modeling transform matrix to apply to each shape added with subsequent calls to {{#crossLink "GeometryBuilder/addShape:method"}}addShape(){{/crossLink}}.
     *
     * @method setMatrix
     * @param {Float32Array} matrix a 16-element transform matrix.
     * @returns this
     */
    xeogl.GeometryBuilder.prototype.setMatrix = function (matrix) {
        this._matrix.set(matrix);
        return this;
    };

    /**
     * Adds a shape to this GeometryBuilder. The shape geometry is the one last added
     * by {{#crossLink "GeometryBuilder/addShape:method"}}addShape(){{/crossLink}}, and will be transformed by the
     * matrix that was set by the last call to {{#crossLink "GeometryBuilder/build:method"}}setMatrix(){{/crossLink}}.
     *
     * A subsequent call to {{#crossLink "GeometryBuilder/build:method"}}build(){{/crossLink}} will add all the
     * accumulated transformed shapes to a target {{#crossLink "Geometry"}}{{/crossLink}}.
     *
     * @method addShape
     * @returns this
     */
    xeogl.GeometryBuilder.prototype.addShape = (function () {

        var math = xeogl.math;

        var tempVec3a = math.vec3();
        var tempVec3b = math.vec3();

        return function () {

            var i;
            var len;
            var indicesBump = (this._positions) ? (this._positions.length / 3) : 0;

            if (this._shape.positions) {

                if (!this._positions) {
                    this._positions = [];
                }

                var positions = this._shape.positions;

                if (!this._matrix) {

                    for (i = 0, len = positions.length; i < len; i++) {
                        this._positions.push(positions[i]);
                    }

                } else {

                    for (i = 0, len = positions.length; i < len; i += 3) {

                        tempVec3a[0] = positions[i + 0];
                        tempVec3a[1] = positions[i + 1];
                        tempVec3a[2] = positions[i + 2];

                        math.transformPoint3(this._matrix, tempVec3a, tempVec3b);

                        this._positions.push(tempVec3b[0]);
                        this._positions.push(tempVec3b[1]);
                        this._positions.push(tempVec3b[2]);
                    }
                }
            }

            if (this._shape.normals) {
                if (!this._normals) {
                    this._normals = [];
                }
                for (i = 0, len = this._shape.normals.length; i < len; i++) {
                    this._normals.push(this._shape.normals[i]);
                }
            }

            if (this._shape.uv) {
                if (!this._uv) {
                    this._uv = [];
                }
                for (i = 0, len = this._shape.uv.length; i < len; i++) {
                    this._uv.push(this._shape.uv[i]);
                }
            }

            if (this._shape.colors) {
                if (!this._colors) {
                    this._colors = [];
                }
                for (i = 0, len = this._shape.colors.length; i < len; i++) {
                    this._colors.push(this._shape.colors[i]);
                }
            }

            if (this._shape.indices) {
                if (!this._indices) {
                    this._indices = [];
                }
                for (i = 0, len = this._shape.indices.length; i < len; i++) {
                    this._indices.push(this._shape.indices[i] + indicesBump);
                }
            }
        };
    })();

    /**
     * Returns the accumulated state from previous calls to {{#crossLink "GeometryBuilder/setMatrix:method"}}setMatrix(){{/crossLink}} and
     * {{#crossLink "GeometryBuilder/setShape:method"}}setShape(){{/crossLink}}.
     *
     * Retains all that state afterwards, so that you can continue to call this method to add the state to
     * other {{#crossLink "Geometry"}}Geometries{{/crossLink}}.
     *
     * @method build
     * @returns {Object}
     */
    xeogl.GeometryBuilder.prototype.build = function (geometry) {
        return {
            primitive: this.primitive || "triangles",
            positions: this._positions,
            normals: this._normals,
            uv: this._uv,
            colors: this._colors,
            indices: this._indices
        };
    };

    /**
     *
     * Resets this GeometryBuilder, clearing all the state previously accumulated with {{#crossLink "GeometryBuilder/setMatrix:method"}}setMatrix(){{/crossLink}} and
     * {{#crossLink "GeometryBuilder/setShape:method"}}setShape(){{/crossLink}}.
     * @method reset
     * @returns this
     */
    xeogl.GeometryBuilder.prototype.reset = function () {
        this._positions = null;
        this._normals = null;
        this._uv = null;
        this._colors = null;
        this._indices = null;
        this._matrix = xeogl.math.identityMat4(xeogl.math.mat4());
        return this;
    };

})();