API Docs for:

File: /home/lindsay/xeolabs/xeogl-next/xeogl/examples/js/models/buildableModel.js

/**
 A **BuildableModel** is a {{#crossLink "Model"}}{{/crossLink}} that provides a "stateful builder" API through which you can
 procedurally generate xeogl content.

 <a href="../../examples/#models_generation_city"><img src="http://i.giphy.com/l0HlPJO1AN01Lz27e.gif"></img></a>

 ## Overview

 * A BuilderModel implements the [Builder pattern](https://en.wikipedia.org/wiki/Builder_pattern).
 * Create various assets within a BuilderModel, such as {{#crossLink "Geometry"}}Geometries{{/crossLink}}
 and {{#crossLink "Material"}}Materials{{/crossLink}}, then create {{#crossLink "Mesh"}}Meshes{{/crossLink}} that use those assets.
 * The BuilderModel then owns those components and will destroy them when you
 call its {{#crossLink "BuildableModel/clear:method"}}clear(){{/crossLink}} or {{#crossLink "Component/destroy:method"}}destroy(){{/crossLink}} methods.
 * A BuildableModel can be transformed within World space by attaching it to a {{#crossLink "Transform"}}{{/crossLink}}.
 * A BuildableModel provides its World-space boundary as a {{#crossLink "Boundary3D"}}{{/crossLink}}.

 ## Examples

 * [Generating a city with a BuildableModel](../../examples/#models_generation_city)</li>

 ## Usage

 A BuildableModel containing ten textured boxes with random sizes and positions:

 ````javascript
 var model = new xeogl.BuildableModel();

 // Add a BoxGeometry asset
 buildableModel.createAsset("boxGeometry", {
     type: "xeogl.BoxGeometry"
 });

 // Add a PhongMaterial asset
 buildableModel.createAsset("gridMaterial", {
     type: "xeogl.PhongMaterial",
     ambient: [0.9, 0.3, 0.9],
     shininess: 30,
     diffuseMap: {
         src: "textures/diffuse/gridMaterial.jpg"
     }
 });

 // Set the BoxGeometry asset as the current geometry
 buildableModel.setGeometry("boxGeometry");

 // Set the PhongMaterial asset as the current material
 buildableModel.setMaterial("gridMaterial");

 // Build ten meshes with random sizes and positions,
 // that each get the current geometry and material
 for (var i = 0; i < 10; i++) {

     buildableModel.setScale(Math.random() * 10 + 1, Math.random() * 10 + 1, Math.random() * 10 + 1);
     buildableModel.setPosition(Math.random() * 100 - 50, Math.random() * 100 - 50, Math.random() * 100 - 50);

     buildableModel.createMesh();
 }
 ````

 @class BuildableModel
 @module xeogl
 @submodule models
 @constructor
 @param [scene] {Scene} Parent {{#crossLink "Scene"}}Scene{{/crossLink}} - creates this BuildableModel in the default
 {{#crossLink "Scene"}}Scene{{/crossLink}} when omitted.
 @param [cfg] {*} Configs
 @param [cfg.id] {String} Optional ID, unique among all components in the parent {{#crossLink "Scene"}}Scene{{/crossLink}}, generated automatically when omitted.
 @param [cfg.entityType] {String} Optional entity classification when using within a semantic data model. See the {{#crossLink "Object"}}{{/crossLink}} documentation for usage.
 @param [cfg.meta] {String:Object} Optional map of user-defined metadata to attach to this BuildableModel.
 @param [cfg.position=[0,0,0]] {Float32Array} The BuildableModel's local 3D position.
 @param [cfg.scale=[1,1,1]] {Float32Array} The BuildableModel's local scale.
 @param [cfg.rotation=[0,0,0]] {Float32Array} The BuildableModel's local rotation, as Euler angles given in degrees.
 @param [cfg.matrix=[1,0,0,0,0,1,0,0,0,0,1,0,0,0,0,1] {Float32Array} The BuildableModel's local transform matrix. Overrides the position, scale and rotation parameters.
 @extends Model
 */
{

    xeogl.BuildableModel = class xeoglBuildableModel extends xeogl.Model {

        init(cfg) {
            super.init(cfg);
            this._initState();
        }

        _initState() {
            this._state = {
                material: null,
                geometry: null,
                pos: xeogl.math.vec3([0, 0, 0]),
                scale: xeogl.math.vec3([1, 1, 1]),
                angles: xeogl.math.vec3([0, 0, 0]),
                axis: [0, 1, 2],
                colorize: xeogl.math.vec3([1, 1, 1]),
                assetCfgs: {},
                assets: {}
            };
        }

        /**
         * Adds an asset to this BuildableModel.
         *
         * The asset is given as a configuration object, to be lazy-instantiated as soon as an mesh is built from
         * it with {{#crossLink "BuildableModel/mesh:method"}}mesh(){{/crossLink}}.
         *
         * #### Usage
         *
         * Adding a {{#crossLink "PhongMaterial"}}{{/crossLink}} asset with ID "gridMaterial":
         *
         * ````javascript
         * buildableModel.createAsset("gridMaterial", {
         *     type: "xeogl.PhongMaterial",
         *     ambient: [0.9, 0.3, 0.9],
         *     shininess: 30,
         *     diffuseMap: {
         *         src: "textures/diffuse/uvGrid2.jpg"
         *     }
         * });
         * ````
         *
         * Adding a {{#crossLink "BoxGeometry"}}{{/crossLink}} asset with ID "boxGeometry":
         *
         * ````javascript
         * buildableModel.createAsset("boxGeometry", {
         *     type: "xeogl.BoxGeometry",
         *     xSize: 1.0,
         *     ySize: 1.0,
         *     zSize: 1.0
         * });
         * ````
         *
         * @method createAsset
         * @param {String|Number} assetId A unique ID for the asset.
         * @param {*} cfg Configuration object for the asset.
         */
        createAsset(assetId, cfg) {
            this._state.assetCfgs[assetId] = cfg;
            delete this._state.assets[assetId];
        }

        /**
         * Selects the {{#crossLink "Geometry"}}{{/crossLink}} asset that will be added to
         * each {{#crossLink "Mesh"}}{{/crossLink}} subsequently created with
         * {{#crossLink "BuildableModel/mesh:method"}}mesh(){{/crossLink}}.
         *
         * The given ID must belong to a {{#crossLink "Geometry"}}{{/crossLink}} asset that was added previously with
         * {{#crossLink "BuildableModel/asset:method"}}asset(){{/crossLink}}.
         *
         * @method geometry
         * @param {String|Number} assetId The asset ID.
         */
        setGeometry(assetId) {
            this._state.geometry = assetId;
        }

        /**
         * Selects the {{#crossLink "Material"}}{{/crossLink}} asset that will be added to
         * each {{#crossLink "Mesh"}}{{/crossLink}} subsequently created with
         * {{#crossLink "BuildableModel/mesh:method"}}mesh(){{/crossLink}}.
         *
         * The given ID must belong to a {{#crossLink "Material"}}{{/crossLink}} asset that was added previously with
         * {{#crossLink "BuildableModel/asset:method"}}asset(){{/crossLink}}.
         *
         * @method setMaterial
         * @param {String|Number} assetId The asset ID.
         */
        setMaterial(assetId) {
            this._state.material = assetId;
        }

        /**
         * Sets the 3D position of each {{#crossLink "Mesh"}}{{/crossLink}} subsequently created with
         * {{#crossLink "BuildableModel/mesh:method"}}mesh(){{/crossLink}}.
         *
         * @method setPosition
         * @param {Number} x Position on X-axis.
         * @param {Number} y Position on Y-axis.
         * @param {Number} z Position on Z-axis.
         */
        setPosition(x, y, z) {
            this._state.pos[0] = x;
            this._state.pos[1] = y;
            this._state.pos[2] = z;
        }

        /**
         * Sets the 3D scale of each {{#crossLink "Mesh"}}{{/crossLink}} subsequently created with
         * {{#crossLink "BuildableModel/mesh:method"}}mesh(){{/crossLink}}.
         *
         * @method setScale
         * @param {Number} x Scale on X-axis.
         * @param {Number} y Scale on Y-axis.
         * @param {Number} z Scale on Z-axis.
         */
        setScale(x, y, z) {
            this._state.scale[0] = x;
            this._state.scale[1] = y;
            this._state.scale[2] = z;
        }

        /**
         * Sets the 3D Euler rotation angles for each {{#crossLink "Mesh"}}{{/crossLink}} subsequently created
         * with {{#crossLink "BuildableModel/mesh:method"}}mesh(){{/crossLink}}.
         *
         * @method setRotation
         * @param {Number} x Angle on X-axis in degrees.
         * @param {Number} y Angle on Y-axis in degrees.
         * @param {Number} z Angle on Z-axis in degrees.
         */
        setRotation(x, y, z) {
            this._state.angles[0] = x;
            this._state.angles[1] = y;
            this._state.angles[2] = z;
        }

        /**
         * Sets the order of 3D rotations for each {{#crossLink "Mesh"}}{{/crossLink}} subsequently created
         * with {{#crossLink "BuildableModel/mesh:method"}}mesh(){{/crossLink}}.
         *
         * #### Usage
         *
         * The X, Y and Z axis are identified as ````0, 1, 2```` respectively.
         *
         * ````Javascript
         * buildableModel.setRotationAxis(0,1,2); // X, Y, Z
         * buildableModel.setRotationAxis(2,0,1); // Z, X, Y
         * buildableModel.setRotationAxis(1,2,0); // Y, Z, X
         * ````
         *
         * @method setRotationAaxis
         * @param {Number} a Indicates the first rotation axis.
         * @param {Number} b Indicates the second rotation axis.
         * @param {Number} c Indicates the third rotation axis.
         */
        setRotationAxis(a, b, c) {
            this._state.axis[0] = a;
            this._state.axis[1] = b;
            this._state.axis[2] = c;
        }

        /**
         * Sets the RGBA colorize factors each {{#crossLink "Mesh"}}{{/crossLink}} subsequently created
         * with {{#crossLink "BuildableModel/mesh:method"}}mesh(){{/crossLink}}.
         *
         * #### Usage
         *
         * ````Javascript
         * buildableModel.setColorize(0.4, 0.4, 0.4, 1.0);
         * ````
         *
         * @method setColorize
         * @param {Number} r Indicates the amount of red.
         * @param {Number} g Indicates the amount of green.
         * @param {Number} b Indicates the amount of blue.
         * @param {Number} z Indicates the alpha.
         */
        setColorize(r, g, b, a) {
            this._state.colorize[0] = r;
            this._state.colorize[1] = g;
            this._state.colorize[2] = b;
            this._state.colorize[3] = a;
        }

        /**
         * Creates an {{#crossLink "Mesh"}}{{/crossLink}} with whatever assets and states are currently
         * set on this BuildableModel.
         *
         * @method createMesh
         * @param {String|Number} [id] A unique ID for the new {{#crossLink "Mesh"}}{{/crossLink}}.
         */
        createMesh(id) {
            var mesh = new xeogl.Mesh({
                id: id,
                material: this._getAsset(this._state.material),
                geometry: this._getAsset(this._state.geometry),
                scale: this._state.scale,
                position: this._state.pos,
                colorize: this._state.colorize
            });
            this._addComponent(mesh);
            this.addChild(mesh, false); // Don't inherit state from this Model
        }

        _getAsset(assetId) {
            if (assetId === null) {
                return;
            }
            var asset = this._state.assets[assetId];
            if (!asset) {
                var assetCfg = this._state.assetCfgs[assetId];
                if (!assetCfg) {
                    this.error("Unknown asset: " + assetId);
                    return;
                }
                asset = this.create(assetCfg);
                this._state.assets[assetId] = asset;
                this._addComponent(asset);
            }
            return asset;
        }

        /**
         * Removes all assets and {{#crossLink "Mesh"}}Meshes{{/crossLink}} from this BuildableModel.
         * @method clear
         */
        clear() {
            super.clear();
            this._initState();
        }

        /**
         * Resets the state of this BuildableModel to defaults.
         * @method reset
         */
        reset() {
            this.setPosition(0, 0, 0);
            this.setScale(1, 1, 1);
            this.setRotation(0, 0, 0);
            this.setRotationAxis(0, 1, 2);
            this.setColorize(1, 1, 1, 1);
            this.setMaterial(null);
            this.setGeometry(null);
        }
    };
}