o: ActiveSupport::Cache::Entry	:@compressedF:@expires_in0:@created_atf1380709795.047065:@value"d�{I"
class:ETI"BundledAsset;�FI"logical_path;�TI"Scene/Material.js;�TI"
pathname;�TI"O/Users/bogumil/www/engines/cesium/app/assets/javascripts/Scene/Material.js;�FI"content_type;�TI"application/javascript;�TI"
mtime;�TI"2013-10-02T12:16:28+02:00;�TI"length;�Ti��I"digest;�TI"%ca55bbbf57fee502fbcfaeb6d608b6c8;�FI"source;�TI"��/*global define*/

define(['ThirdParty/when', 'Core/loadImage', 'Core/DeveloperError', 'Core/createGuid', 'Core/clone', 'Core/Color', 'Core/combine', 'Core/defaultValue', 'Core/defined', 'Core/defineProperties', 'Core/destroyObject', 'Core/Cartesian2', 'Core/Matrix2', 'Core/Matrix3', 'Core/Matrix4', 'Renderer/Texture', 'Renderer/CubeMap', 'Shaders/Materials/AsphaltMaterial', 'Shaders/Materials/BlobMaterial', 'Shaders/Materials/BrickMaterial', 'Shaders/Materials/BumpMapMaterial', 'Shaders/Materials/CementMaterial', 'Shaders/Materials/CheckerboardMaterial', 'Shaders/Materials/DotMaterial', 'Shaders/Materials/FacetMaterial', 'Shaders/Materials/FresnelMaterial', 'Shaders/Materials/GrassMaterial', 'Shaders/Materials/GridMaterial', 'Shaders/Materials/NormalMapMaterial', 'Shaders/Materials/ReflectionMaterial', 'Shaders/Materials/RefractionMaterial', 'Shaders/Materials/StripeMaterial', 'Shaders/Materials/TieDyeMaterial', 'Shaders/Materials/Water', 'Shaders/Materials/WoodMaterial', 'Shaders/Materials/RimLightingMaterial', 'Shaders/Materials/ErosionMaterial', 'Shaders/Materials/FadeMaterial', 'Shaders/Materials/PolylineArrowMaterial', 'Shaders/Materials/PolylineGlowMaterial', 'Shaders/Materials/PolylineOutlineMaterial'], function(
        when,
        loadImage,
        DeveloperError,
        createGuid,
        clone,
        Color,
        combine,
        defaultValue,
        defined,
        defineProperties,
        destroyObject,
        Cartesian2,
        Matrix2,
        Matrix3,
        Matrix4,
        Texture,
        CubeMap,
        AsphaltMaterial,
        BlobMaterial,
        BrickMaterial,
        BumpMapMaterial,
        CementMaterial,
        CheckerboardMaterial,
        DotMaterial,
        FacetMaterial,
        FresnelMaterial,
        GrassMaterial,
        GridMaterial,
        NormalMapMaterial,
        ReflectionMaterial,
        RefractionMaterial,
        StripeMaterial,
        TieDyeMaterial,
        WaterMaterial,
        WoodMaterial,
        RimLightingMaterial,
        ErosionMaterial,
        FadeMaterial,
        PolylineArrowMaterial,
        PolylineGlowMaterial,
        PolylineOutlineMaterial) {
    "use strict";

    /**
     * A Material defines surface appearance through a combination of diffuse, specular,
     * normal, emission, and alpha components. These values are specified using a
     * JSON schema called Fabric which gets parsed and assembled into glsl shader code
     * behind-the-scenes. Check out the <a href='https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric'>wiki page</a>
     * for more details on Fabric.
     * <br /><br />
     * <style type="text/css">
     *  #materialDescriptions code {
     *      font-weight: normal;
     *      font-family: Consolas, 'Lucida Console', Monaco, monospace;
     *      color: #A35A00;
     *  }
     *  #materialDescriptions ul, #materialDescriptions ul ul {
     *      list-style-type: none;
     *  }
     *  #materialDescriptions ul ul {
     *      margin-bottom: 10px;
     *  }
     *  #materialDescriptions ul ul li {
     *      font-weight: normal;
     *      color: #000000;
     *      text-indent: -2em;
     *      margin-left: 2em;
     *  }
     *  #materialDescriptions ul li {
     *      font-weight: bold;
     *      color: #0053CF;
     *  }
     * </style>
     *
     * Base material types and their uniforms:
     * <div id='materialDescriptions'>
     * <ul>
     *  <li>Color</li>
     *  <ul>
     *      <li><code>color</code>:  rgba color object.</li>
     *  </ul>
     *  <li>Image</li>
     *  <ul>
     *      <li><code>image</code>:  path to image.</li>
     *      <li><code>repeat</code>:  Object with x and y values specifying the number of times to repeat the image.</li>
     *  </ul>
     *  <li>DiffuseMap</li>
     *  <ul>
     *      <li><code>image</code>:  path to image.</li>
     *      <li><code>channels</code>:  Three character string containing any combination of r, g, b, and a for selecting the desired image channels.</li>
     *      <li><code>repeat</code>:  Object with x and y values specifying the number of times to repeat the image.</li>
     *  </ul>
     *  <li>AlphaMap</li>
     *  <ul>
     *      <li><code>image</code>:  path to image.</li>
     *      <li><code>channel</code>:  One character string containing r, g, b, or a for selecting the desired image channel. </li>
     *      <li><code>repeat</code>:  Object with x and y values specifying the number of times to repeat the image.</li>
     *  </ul>
     *  <li>SpecularMap</li>
     *  <ul>
     *      <li><code>image</code>: path to image.</li>
     *      <li><code>channel</code>: One character string containing r, g, b, or a for selecting the desired image channel. </li>
     *      <li><code>repeat</code>: Object with x and y values specifying the number of times to repeat the image.</li>
     *  </ul>
     *  <li>EmissionMap</li>
     *  <ul>
     *      <li><code>image</code>:  path to image.</li>
     *      <li><code>channels</code>:  Three character string containing any combination of r, g, b, and a for selecting the desired image channels. </li>
     *      <li><code>repeat</code>:  Object with x and y values specifying the number of times to repeat the image.</li>
     *  </ul>
     *  <li>BumpMap</li>
     *  <ul>
     *      <li><code>image</code>:  path to image.</li>
     *      <li><code>channel</code>:  One character string containing r, g, b, or a for selecting the desired image channel. </li>
     *      <li><code>repeat</code>:  Object with x and y values specifying the number of times to repeat the image.</li>
     *      <li><code>strength</code>:  Bump strength value between 0.0 and 1.0 where 0.0 is small bumps and 1.0 is large bumps.</li>
     *  </ul>
     *  <li>NormalMap</li>
     *  <ul>
     *      <li><code>image</code>:  path to image.</li>
     *      <li><code>channels</code>:  Three character string containing any combination of r, g, b, and a for selecting the desired image channels. </li>
     *      <li><code>repeat</code>:  Object with x and y values specifying the number of times to repeat the image.</li>
     *      <li><code>strength</code>:  Bump strength value between 0.0 and 1.0 where 0.0 is small bumps and 1.0 is large bumps.</li>
     *  </ul>
     *  <li>Reflection</li>
     *  <ul>
     *      <li><code>cubeMap</code>:  Object with positiveX, negativeX, positiveY, negativeY, positiveZ, and negativeZ image paths. </li>
     *      <li><code>channels</code>:  Three character string containing any combination of r, g, b, and a for selecting the desired image channels.</li>
     *  </ul>
     *  <li>Refraction</li>
     *  <ul>
     *      <li><code>cubeMap</code>:  Object with positiveX, negativeX, positiveY, negativeY, positiveZ, and negativeZ image paths. </li>
     *      <li><code>channels</code>:  Three character string containing any combination of r, g, b, and a for selecting the desired image channels.</li>
     *      <li><code>indexOfRefractionRatio</code>:  Number representing the refraction strength where 1.0 is the lowest and 0.0 is the highest.</li>
     *  </ul>
     *  <li>Fresnel</li>
     *  <ul>
     *      <li><code>reflection</code>:  Reflection Material.</li>
     *      <li><code>refraction</code>:  Refraction Material.</li>
     *  </ul>
     *  <li>Brick</li>
     *  <ul>
     *      <li><code>brickColor</code>:  rgba color object for the brick color.</li>
     *      <li><code>mortarColor</code>:  rgba color object for the mortar color.</li>
     *      <li><code>brickSize</code>:  Number between 0.0 and 1.0 where 0.0 is many small bricks and 1.0 is one large brick.</li>
     *      <li><code>brickPct</code>:  Number for the ratio of brick to mortar where 0.0 is all mortar and 1.0 is all brick.</li>
     *      <li><code>brickRoughness</code>:  Number between 0.0 and 1.0 representing how rough the brick looks.</li>
     *      <li><code>mortarRoughness</code>:  Number between 0.0 and 1.0 representing how rough the mortar looks.</li>
     *  </ul>
     *  <li>Wood</li>
     *  <ul>
     *      <li><code>lightWoodColor</code>:  rgba color object for the wood's base color.</li>
     *      <li><code>darkWoodColor</code>:  rgba color object for the color of rings in the wood.</li>
     *      <li><code>ringFrequency</code>:  Number for the frequency of rings in the wood.</li>
     *      <li><code>noiseScale</code>:  Object with x and y values specifying the noisiness of the ring patterns in both directions.</li>
     *  </ul>
     *  <li>Asphalt</li>
     *  <ul>
     *      <li><code>asphaltColor</code>:  rgba color object for the asphalt's color.</li>
     *      <li><code>bumpSize</code>:  Number for the size of the asphalt's bumps.</li>
     *      <li><code>roughness</code>:  Number that controls how rough the asphalt looks.</li>
     *  </ul>
     *  <li>Cement</li>
     *  <ul>
     *  <li><code>cementColor</code>:  rgba color object for the cement's color. </li>
     *  <li><code>grainScale</code>:  Number for the size of rock grains in the cement. </li>
     *  <li><code>roughness</code>:  Number that controls how rough the cement looks.</li>
     *  </ul>
     *  <li>Grass</li>
     *  <ul>
     *      <li><code>grassColor</code>:  rgba color object for the grass' color. </li>
     *      <li><code>dirtColor</code>:  rgba color object for the dirt's color. </li>
     *      <li><code>patchiness</code>:  Number that controls the size of the color patches in the grass.</li>
     *  </ul>
     *  <li>Grid</li>
     *  <ul>
     *      <li><code>color</code>:  rgba color object for the whole material.</li>
     *      <li><code>cellAlpha</code>: Alpha value for the cells between grid lines.  This will be combined with color.alpha.</li>
     *      <li><code>lineCount</code>:  Object with x and y values specifying the number of columns and rows respectively.</li>
     *      <li><code>lineThickness</code>:  Object with x and y values specifying the thickness of grid lines (in pixels where available).</li>
     *  </ul>
     *  <li>Stripe</li>
     *  <ul>
     *      <li><code>horizontal</code>:  Boolean that determines if the stripes are horizontal or vertical.</li>
     *      <li><code>lightColor</code>:  rgba color object for the stripe's light alternating color.</li>
     *      <li><code>darkColor</code>:  rgba color object for the stripe's dark alternating color.</li>
     *      <li><code>offset</code>:  Number that controls the stripe offset from the edge.</li>
     *      <li><code>repeat</code>:  Number that controls the total number of stripes, half light and half dark.</li>
     *  </ul>
     *  <li>Checkerboard</li>
     *  <ul>
     *      <li><code>lightColor</code>:  rgba color object for the checkerboard's light alternating color.</li>
     *      <li><code>darkColor</code>: rgba color object for the checkerboard's dark alternating color.</li>
     *      <li><code>repeat</code>:  Object with x and y values specifying the number of columns and rows respectively.</li>
     *  </ul>
     *  <li>Dot</li>
     *  <ul>
     *      <li><code>lightColor</code>:  rgba color object for the dot color.</li>
     *      <li><code>darkColor</code>:  rgba color object for the background color.</li>
     *      <li><code>repeat</code>:  Object with x and y values specifying the number of columns and rows of dots respectively.</li>
     *  </ul>
     *  <li>TieDye</li>
     *  <ul>
     *      <li><code>lightColor</code>:  rgba color object for the light color.</li>
     *      <li><code>darkColor</code>:  rgba color object for the dark color.</li>
     *      <li><code>frequency</code>:  Number that controls the frequency of the pattern.</li>
     *  </ul>
     *  <li>Facet</li>
     *  <ul>
     *      <li><code>lightColor</code>:  rgba color object for the light color.</li>
     *      <li><code>darkColor</code>:  rgba color object for the dark color.</li>
     *      <li><code>frequency</code>:  Number that controls the frequency of the pattern.</li>
     *  </ul>
     *  <li>Blob</li>
     *  <ul>
     *      <li><code>lightColor</code>:  rgba color object for the light color.</li>
     *      <li><code>darkColor</code>:  rgba color object for the dark color.</li>
     *      <li><code>frequency</code>:  Number that controls the frequency of the pattern.</li>
     *  </ul>
     *  <li>Water</li>
     *  <ul>
     *      <li><code>baseWaterColor</code>:  rgba color object base color of the water.</li>
     *      <li><code>blendColor</code>:  rgba color object used when blending from water to non-water areas.</li>
     *      <li><code>specularMap</code>:  Single channel texture used to indicate areas of water.</li>
     *      <li><code>normalMap</code>:  Normal map for water normal perturbation.</li>
     *      <li><code>frequency</code>:  Number that controls the number of waves.</li>
     *      <li><code>normalMap</code>:  Normal map for water normal perturbation.</li>
     *      <li><code>animationSpeed</code>:  Number that controls the animations speed of the water.</li>
     *      <li><code>amplitude</code>:  Number that controls the amplitude of water waves.</li>
     *      <li><code>specularIntensity</code>:  Number that controls the intensity of specular reflections.</li>
     *  </ul>
     *  <li>RimLighting</li>
     *  <ul>
     *      <li><code>color</code>:  diffuse color and alpha.</li>
     *      <li><code>rimColor</code>:  diffuse color and alpha of the rim.</li>
     *      <li><code>width</code>:  Number that determines the rim's width.</li>
     *  </ul>
     *  <li>Erosion</li>
     *  <ul>
     *      <li><code>color</code>:  diffuse color and alpha.</li>
     *      <li><code>time</code>:  Time of erosion.  1.0 is no erosion; 0.0 is fully eroded.</li>
     *  </ul>
     *  <li>Fade</li>
     *  <ul>
     *      <li><code>fadeInColor</code>: diffuse color and alpha at <code>time</code></li>
     *      <li><code>fadeOutColor</code>: diffuse color and alpha at <code>maximumDistance<code> from <code>time</code></li>
     *      <li><code>maximumDistance</code>: Number between 0.0 and 1.0 where the <code>fadeInColor</code> becomes the <code>fadeOutColor</code>. A value of 0.0 gives the entire material a color of <code>fadeOutColor</code> and a value of 1.0 gives the the entire material a color of <code>fadeInColor</code></li>
     *      <li><code>repeat</code>: true if the fade should wrap around the texture coodinates.</li>
     *      <li><code>fadeDirection</code>: Object with x and y values specifying if the fade should be in the x and y directions.</li>
     *      <li><code>time</code>: Object with x and y values between 0.0 and 1.0 of the <code>fadeInColor</code> position</li>
     *  </ul>
     *  <li>PolylineArrow</li>
     *  <ul>
     *      <li><code>color</code>: diffuse color and alpha.</li>
     *  </ul>
     *  <li>PolylineGlow</li>
     *  <ul>
     *      <li><code>color</code>: color and maximum alpha for the glow on the line.</li>
     *      <li><code>glowPower</code>: strength of the glow, as a percentage of the total line width (less than 1.0).</li>
     *  </ul>
     *  <li>PolylineOutline</li>
     *  <ul>
     *      <li><code>color</code>: diffuse color and alpha for the interior of the line.</li>
     *      <li><code>outlineColor</code>: diffuse color and alpha for the outline.</li>
     *      <li><code>outlineWidth</code>: width of the outline in pixels.</li>
     *  </ul>
     * </ul>
     * </div>
     *
     * @alias Material
     *
     * @param {Boolean} [description.strict = false] Throws errors for issues that would normally be ignored, including unused uniforms or materials.
     * @param {Object} description.fabric The fabric JSON used to generate the material.
     *
     * @constructor
     *
     * @exception {DeveloperError} fabric: uniform has invalid type.
     * @exception {DeveloperError} fabric: uniforms and materials cannot share the same property.
     * @exception {DeveloperError} fabric: cannot have source and components in the same section.
     * @exception {DeveloperError} fabric: property name is not valid. It should be 'type', 'materials', 'uniforms', 'components', or 'source'.
     * @exception {DeveloperError} fabric: property name is not valid. It should be 'diffuse', 'specular', 'shininess', 'normal', 'emission', or 'alpha'.
     * @exception {DeveloperError} strict: shader source does not use string.
     * @exception {DeveloperError} strict: shader source does not use uniform.
     * @exception {DeveloperError} strict: shader source does not use material.
     *
     * @example
     * // Create a color material with fromType:
     * polygon.material = Material.fromType('Color');
     * polygon.material.uniforms.color = {
     *     red : 1.0,
     *     green : 1.0,
     *     blue : 0.0
     *     alpha : 1.0
     * };
     *
     * // Create the default material:
     * polygon.material = new Material();
     *
     * // Create a color material with full Fabric notation:
     * polygon.material = new Material({
     *     fabric : {
     *         type : 'Color',
     *         uniforms : {
     *             color : {
     *                 red : 1.0,
     *                 green : 1.0,
     *                 blue : 0.0,
     *                 alpha : 1.0
     *             }
     *         }
     *     }
     * });
     *
     * @see <a href='https://github.com/AnalyticalGraphicsInc/cesium/wiki/Fabric'>Fabric wiki page</a> for a more detailed description of Fabric.
     *
     * @demo <a href="http://cesium.agi.com/Cesium/Apps/Sandcastle/index.html?src=Materials.html">Cesium Sandcastle Materials Demo</a>
     */
    var Material = function(description) {
        /**
         * The material type. Can be an existing type or a new type. If no type is specified in fabric, type is a GUID.
         * @type {String}
         * @default undefined
         */
        this.type = undefined;

        /**
         * The glsl shader source for this material.
         * @type {String}
         * @default undefined
         */
        this.shaderSource = undefined;

        /**
         * Maps sub-material names to Material objects.
         * @type {Object}
         * @default undefined
         */
        this.materials = undefined;

        /**
         * Maps uniform names to their values.
         * @type {Object}
         * @default undefined
         */
        this.uniforms = undefined;
        this._uniforms = undefined;

        this._strict = undefined;
        this._template = undefined;
        this._count = undefined;

        this._texturePaths = {};
        this._loadedImages = [];
        this._loadedCubeMaps = [];

        this._textures = {};

        this._updateFunctions = [];

        initializeMaterial(description, this);
        defineProperties(this, {
            type : {
                value : this.type,
                writable : false
            }
        });

        if (!defined(Material._uniformList[this.type])) {
            Material._uniformList[this.type] = Object.keys(this._uniforms);
        }
    };

    // Cached list of combined uniform names indexed by type.
    // Used to get the list of uniforms in the same order.
    Material._uniformList = {};

    /**
     * Creates a new material using an existing material type.
     * <br /><br />
     * Shorthand for: new Material({fabric : {type : type}});
     *
     * @param {String} type The base material type.
     *
     * @returns {Material} New material object.
     *
     * @exception {DeveloperError} material with that type does not exist.
     *
     * @example
     * var material = Material.fromType('Color');
     * material.uniforms.color = vec4(1.0, 0.0, 0.0, 1.0);
     */
    Material.fromType = function(type) {
        if (!defined(Material._materialCache.getMaterial(type))) {
            throw new DeveloperError('material with type \'' + type + '\' does not exist.');
        }
        return new Material({
            fabric : {
                type : type
            }
        });
    };

    /**
     * @private
     */
    Material.prototype.update = function(context) {
        var i;
        var uniformId;

        var loadedImages = this._loadedImages;
        var length = loadedImages.length;

        for (i = 0; i < length; ++i) {
            var loadedImage = loadedImages[i];
            uniformId = loadedImage.id;
            var image = loadedImage.image;

            var texture = Material._textureCache.getTexture(this._texturePaths[uniformId]);
            if (!defined(texture)) {
                texture = context.createTexture2D({
                    source : image
                });
                Material._textureCache.addTexture(this._texturePaths[uniformId], texture);
            }

            this._textures[uniformId] = texture;

            var uniformDimensionsName = uniformId + 'Dimensions';
            if (this.uniforms.hasOwnProperty(uniformDimensionsName)) {
                var uniformDimensions = this.uniforms[uniformDimensionsName];
                uniformDimensions.x = texture._width;
                uniformDimensions.y = texture._height;
            }
        }

        loadedImages.length = 0;

        var loadedCubeMaps = this._loadedCubeMaps;
        length = loadedCubeMaps.length;

        for (i = 0; i < length; ++i) {
            var loadedCubeMap = loadedCubeMaps[i];
            uniformId = loadedCubeMap.id;
            var images = loadedCubeMap.images;

            var cubeMap = Material._textureCache.getTexture(this._texturePaths[uniformId]);
            if (!defined(cubeMap)) {
                cubeMap = context.createCubeMap({
                    source : {
                        positiveX : images[0],
                        negativeX : images[1],
                        positiveY : images[2],
                        negativeY : images[3],
                        positiveZ : images[4],
                        negativeZ : images[5]
                    }
                });
                Material._textureCache.addTexture(this._texturePaths[uniformId], cubeMap);
            }

            this._textures[uniformId] = cubeMap;
        }

        loadedCubeMaps.length = 0;

        var updateFunctions = this._updateFunctions;
        length = updateFunctions.length;
        for (i = 0; i < length; ++i) {
            updateFunctions[i](this, context);
        }

        var subMaterials = this.materials;
        for (var name in subMaterials) {
            if (subMaterials.hasOwnProperty(name)) {
                subMaterials[name].update(context);
            }
        }
    };

    /**
    * Returns true if this object was destroyed; otherwise, false.
    * <br /><br />
    * If this object was destroyed, it should not be used; calling any function other than
    * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.
    *
    * @memberof Material
    *
    * @returns {Boolean} True if this object was destroyed; otherwise, false.
    *
    * @see Material#destroy
    */
    Material.prototype.isDestroyed = function() {
        return false;
    };

    /**
     * Destroys the WebGL resources held by this object.  Destroying an object allows for deterministic
     * release of WebGL resources, instead of relying on the garbage collector to destroy this object.
     * <br /><br />
     * Once an object is destroyed, it should not be used; calling any function other than
     * <code>isDestroyed</code> will result in a {@link DeveloperError} exception.  Therefore,
     * assign the return value (<code>undefined</code>) to the object as done in the example.
     *
     * @memberof Material
     *
     * @returns {undefined}
     *
     * @exception {DeveloperError} This object was destroyed, i.e., destroy() was called.
     *
     * @see Material#isDestroyed
     *
     * @example
     * material = material && material.destroy();
     */
    Material.prototype.destroy = function() {
        var materials = this.materials;
        var uniforms = this.uniforms;
        for ( var uniformId in uniforms) {
            if (uniforms.hasOwnProperty(uniformId)) {
                var path = this._texturePaths[uniformId];
                Material._textureCache.releaseTexture(path);
            }
        }
        for ( var material in materials) {
            if (materials.hasOwnProperty(material)) {
                materials[material].destroy();
            }
        }
        return destroyObject(this);
    };

    function initializeMaterial(description, result) {
        description = defaultValue(description, defaultValue.EMPTY_OBJECT);
        result._strict = defaultValue(description.strict, false);
        result._count = defaultValue(description.count, 0);
        result._template = clone(defaultValue(description.fabric, defaultValue.EMPTY_OBJECT));
        result._template.uniforms = clone(defaultValue(result._template.uniforms, defaultValue.EMPTY_OBJECT));
        result._template.materials = clone(defaultValue(result._template.materials, defaultValue.EMPTY_OBJECT));

        result.type = defined(result._template.type) ? result._template.type : createGuid();

        result.shaderSource = '';
        result.materials = {};
        result.uniforms = {};
        result._uniforms = {};

        // If the cache contains this material type, build the material template off of the stored template.
        var cachedTemplate = Material._materialCache.getMaterial(result.type);
        if (defined(cachedTemplate)) {
            var template = clone(cachedTemplate, true);
            result._template = combine([result._template, template]);
        }

        // Make sure the template has no obvious errors. More error checking happens later.
        checkForTemplateErrors(result);

        // If the material has a new type, add it to the cache.
        if (!defined(cachedTemplate)) {
            Material._materialCache.addMaterial(result.type, result._template);
        }

        createMethodDefinition(result);
        createUniforms(result);
        createSubMaterials(result);
    }

    function checkForValidProperties(object, properties, result, throwNotFound) {
        if (defined(object)) {
            for ( var property in object) {
                if (object.hasOwnProperty(property)) {
                    var hasProperty = properties.indexOf(property) !== -1;
                    if ((throwNotFound && !hasProperty) || (!throwNotFound && hasProperty)) {
                        result(property, properties);
                    }
                }
            }
        }
    }

    function invalidNameError(property, properties) {
        var errorString = 'fabric: property name \'' + property + '\' is not valid. It should be ';
        for ( var i = 0; i < properties.length; i++) {
            var propertyName = '\'' + properties[i] + '\'';
            errorString += (i === properties.length - 1) ? ('or ' + propertyName + '.') : (propertyName + ', ');
        }
        throw new DeveloperError(errorString);
    }

    function duplicateNameError(property, properties) {
        var errorString = 'fabric: uniforms and materials cannot share the same property \'' + property + '\'';
        throw new DeveloperError(errorString);
    }

    var templateProperties = ['type', 'materials', 'uniforms', 'components', 'source'];
    var componentProperties = ['diffuse', 'specular', 'shininess', 'normal', 'emission', 'alpha'];

    function checkForTemplateErrors(material) {
        var template = material._template;
        var uniforms = template.uniforms;
        var materials = template.materials;
        var components = template.components;

        // Make sure source and components do not exist in the same template.
        if (defined(components) && defined(template.source)) {
            throw new DeveloperError('fabric: cannot have source and components in the same template.');
        }

        // Make sure all template and components properties are valid.
        checkForValidProperties(template, templateProperties, invalidNameError, true);
        checkForValidProperties(components, componentProperties, invalidNameError, true);

        // Make sure uniforms and materials do not share any of the same names.
        var materialNames = [];
        for ( var property in materials) {
            if (materials.hasOwnProperty(property)) {
                materialNames.push(property);
            }
        }
        checkForValidProperties(uniforms, materialNames, duplicateNameError, false);
    }

    // Create the czm_getMaterial method body using source or components.
    function createMethodDefinition(material) {
        var components = material._template.components;
        var source = material._template.source;
        if (defined(source)) {
            material.shaderSource += source + '\n';
        } else {
            material.shaderSource += 'czm_material czm_getMaterial(czm_materialInput materialInput)\n{\n';
            material.shaderSource += 'czm_material material = czm_getDefaultMaterial(materialInput);\n';
            if (defined(components)) {
                for ( var component in components) {
                    if (components.hasOwnProperty(component)) {
                        material.shaderSource += 'material.' + component + ' = ' + components[component] + ';\n';
                    }
                }
            }
            material.shaderSource += 'return material;\n}\n';
        }
    }

    var matrixMap = {
        'mat2' : Matrix2,
        'mat3' : Matrix3,
        'mat4' : Matrix4
    };

    function createTexture2DUpdateFunction(uniformId) {
        return function(material, context) {
            var uniforms = material.uniforms;
            var uniformValue = uniforms[uniformId];
            var texture = material._textures[uniformId];

            var uniformDimensionsName;
            var uniformDimensions;

            if (uniformValue instanceof Texture && uniformValue !== texture) {
                Material._textureCache.releaseTexture(material._texturePaths[uniformId]);
                material._texturePaths[uniformId] = undefined;
                material._textures[uniformId] = uniformValue;

                uniformDimensionsName = uniformId + 'Dimensions';
                if (uniforms.hasOwnProperty(uniformDimensionsName)) {
                    uniformDimensions = uniforms[uniformDimensionsName];
                    uniformDimensions.x = uniformValue._width;
                    uniformDimensions.y = uniformValue._height;
                }

                return;
            }

            if (!defined(texture)) {
                material._texturePaths[uniformId] = undefined;
                texture = material._textures[uniformId] = context.getDefaultTexture();

                uniformDimensionsName = uniformId + 'Dimensions';
                if (uniforms.hasOwnProperty(uniformDimensionsName)) {
                    uniformDimensions = uniforms[uniformDimensionsName];
                    uniformDimensions.x = texture._width;
                    uniformDimensions.y = texture._height;
                }
            }

            if (uniformValue === Material.DefaultImageId) {
                return;
            }

            if (uniformValue !== material._texturePaths[uniformId]) {
                var newTexture = Material._textureCache.getTexture(uniformValue);
                if (defined(newTexture)) {
                    Material._textureCache.releaseTexture(material._texturePaths[uniformId]);
                    material._textures[uniformId] = newTexture;
                } else {
                    when(loadImage(uniformValue), function(image) {
                        material._loadedImages.push({
                            id : uniformId,
                            image : image
                        });
                    });
                }

                material._texturePaths[uniformId] = uniformValue;
            }
        };
    }

    function createCubeMapUpdateFunction(uniformId) {
        return function(material, context) {
            var uniformValue = material.uniforms[uniformId];

            if (uniformValue instanceof CubeMap) {
                Material._textureCache.releaseTexture(material._texturePaths[uniformId]);
                material._texturePaths[uniformId] = undefined;
                material._textures[uniformId] = uniformValue;
                return;
            }

            if (!defined(material._textures[uniformId])) {
                material._texturePaths[uniformId] = undefined;
                material._textures[uniformId] = context.getDefaultCubeMap();
            }

            if (uniformValue === Material.DefaultCubeMapId) {
                return;
            }

            var path =
                uniformValue.positiveX + uniformValue.negativeX +
                uniformValue.positiveY + uniformValue.negativeY +
                uniformValue.positiveZ + uniformValue.negativeZ;

            if (path !== material._texturePaths[uniformId]) {
                var newTexture = Material._textureCache.getTexture(path);
                if (defined(newTexture)) {
                    Material._textureCache.releaseTexture(material._texturePaths[uniformId]);
                    material._textures[uniformId] = newTexture;
                } else {
                    var promises = [
                        loadImage(uniformValue.positiveX),
                        loadImage(uniformValue.negativeX),
                        loadImage(uniformValue.positiveY),
                        loadImage(uniformValue.negativeY),
                        loadImage(uniformValue.positiveZ),
                        loadImage(uniformValue.negativeZ)
                    ];

                    when.all(promises).then(function(images) {
                        material._loadedCubeMaps.push({
                            id : uniformId,
                            images : images
                        });
                    });
                }

                material._texturePaths[uniformId] = path;
            }
        };
    }

    function createUniforms(material) {
        var uniforms = material._template.uniforms;
        for ( var uniformId in uniforms) {
            if (uniforms.hasOwnProperty(uniformId)) {
                createUniform(material, uniformId);
            }
        }
    }

    // Writes uniform declarations to the shader file and connects uniform values with
    // corresponding material properties through the returnUniforms function.
    function createUniform(material, uniformId) {
        var strict = material._strict;
        var materialUniforms = material._template.uniforms;
        var uniformValue = materialUniforms[uniformId];
        var uniformType = getUniformType(uniformValue);

        if (!defined(uniformType)) {
            throw new DeveloperError('fabric: uniform \'' + uniformId + '\' has invalid type.');
        } else if (uniformType === 'channels') {
            if (replaceToken(material, uniformId, uniformValue, false) === 0 && strict) {
                throw new DeveloperError('strict: shader source does not use channels \'' + uniformId + '\'.');
            }
        } else {
            // Since webgl doesn't allow texture dimension queries in glsl, create a uniform to do it.
            // Check if the shader source actually uses texture dimensions before creating the uniform.
            if (uniformType === 'sampler2D') {
                var imageDimensionsUniformName = uniformId + 'Dimensions';
                if (getNumberOfTokens(material, imageDimensionsUniformName) > 0) {
                    materialUniforms[imageDimensionsUniformName] = {
                        type : 'ivec3',
                        x : 1,
                        y : 1
                    };
                    createUniform(material, imageDimensionsUniformName);
                }
            }
            // Add uniform declaration to source code.
            var uniformPhrase = 'uniform ' + uniformType + ' ' + uniformId + ';\n';
            if (material.shaderSource.indexOf(uniformPhrase) === -1) {
                material.shaderSource = uniformPhrase + material.shaderSource;
            }

            var newUniformId = uniformId + '_' + material._count++;
            if (replaceToken(material, uniformId, newUniformId) === 1 && strict) {
                throw new DeveloperError('strict: shader source does not use uniform \'' + uniformId + '\'.');
            }
            // Set uniform value
            material.uniforms[uniformId] = uniformValue;

            if (uniformType === 'sampler2D') {
                material._uniforms[newUniformId] = function() {
                    return material._textures[uniformId];
                };
                material._updateFunctions.push(createTexture2DUpdateFunction(uniformId));
            } else if (uniformType === 'samplerCube') {
                material._uniforms[newUniformId] = function() {
                    return material._textures[uniformId];
                };
                material._updateFunctions.push(createCubeMapUpdateFunction(uniformId));
            } else if (uniformType.indexOf('mat') !== -1) {
                var scratchMatrix = new matrixMap[uniformType]();
                material._uniforms[newUniformId] = function() {
                    return matrixMap[uniformType].fromColumnMajorArray(material.uniforms[uniformId], scratchMatrix);
                };
            } else {
                material._uniforms[newUniformId] = function() {
                    return material.uniforms[uniformId];
                };
            }
        }
    }

    // Determines the uniform type based on the uniform in the template.
    function getUniformType(uniformValue) {
        var uniformType = uniformValue.type;
        if (!defined(uniformType)) {
            var type = typeof uniformValue;
            if (type === 'number') {
                uniformType = 'float';
            } else if (type === 'boolean') {
                uniformType = 'bool';
            } else if (type === 'string') {
                if (/^([rgba]){1,4}$/i.test(uniformValue)) {
                    uniformType = 'channels';
                } else if (uniformValue === Material.DefaultCubeMapId) {
                    uniformType = 'samplerCube';
                } else {
                    uniformType = 'sampler2D';
                }
            } else if (type === 'object') {
                if (Array.isArray(uniformValue)) {
                    if (uniformValue.length === 4 || uniformValue.length === 9 || uniformValue.length === 16) {
                        uniformType = 'mat' + Math.sqrt(uniformValue.length);
                    }
                } else {
                    var numAttributes = 0;
                    for ( var attribute in uniformValue) {
                        if (uniformValue.hasOwnProperty(attribute)) {
                            numAttributes += 1;
                        }
                    }
                    if (numAttributes >= 2 && numAttributes <= 4) {
                        uniformType = 'vec' + numAttributes;
                    } else if (numAttributes === 6) {
                        uniformType = 'samplerCube';
                    }
                }
            }
        }
        return uniformType;
    }

    // Create all sub-materials by combining source and uniforms together.
    function createSubMaterials(material) {
        var strict = material._strict;
        var subMaterialTemplates = material._template.materials;
        for ( var subMaterialId in subMaterialTemplates) {
            if (subMaterialTemplates.hasOwnProperty(subMaterialId)) {
                // Construct the sub-material.
                var subMaterial = new Material({
                    strict : strict,
                    fabric : subMaterialTemplates[subMaterialId],
                    count : material._count
                });

                material._count = subMaterial._count;
                material._uniforms = combine([material._uniforms, subMaterial._uniforms]);
                material.materials[subMaterialId] = subMaterial;

                // Make the material's czm_getMaterial unique by appending the sub-material type.
                var originalMethodName = 'czm_getMaterial';
                var newMethodName = originalMethodName + '_' + material._count++;
                replaceToken(subMaterial, originalMethodName, newMethodName);
                material.shaderSource = subMaterial.shaderSource + material.shaderSource;

                // Replace each material id with an czm_getMaterial method call.
                var materialMethodCall = newMethodName + '(materialInput)';
                if (replaceToken(material, subMaterialId, materialMethodCall) === 0 && strict) {
                    throw new DeveloperError('strict: shader source does not use material \'' + subMaterialId + '\'.');
                }
            }
        }
    }

    // Used for searching or replacing a token in a material's shader source with something else.
    // If excludePeriod is true, do not accept tokens that are preceded by periods.
    // http://stackoverflow.com/questions/641407/javascript-negative-lookbehind-equivalent
    function replaceToken(material, token, newToken, excludePeriod) {
        excludePeriod = defaultValue(excludePeriod, true);
        var count = 0;
        var invalidCharacters = 'a-zA-Z0-9_';
        var suffixChars = '([' + invalidCharacters + '])?';
        var prefixChars = '([' + invalidCharacters + (excludePeriod ? '.' : '') + '])?';
        var regExp = new RegExp(prefixChars + token + suffixChars, 'g');
        material.shaderSource = material.shaderSource.replace(regExp, function($0, $1, $2) {
            if ($1 || $2) {
                return $0;
            }
            count += 1;
            return newToken;
        });
        return count;
    }

    function getNumberOfTokens(material, token, excludePeriod) {
        return replaceToken(material, token, token, excludePeriod);
    }

    Material._textureCache = {
        _textures : {},

        addTexture : function(path, texture) {
            this._textures[path] = {
                texture : texture,
                count : 1
            };
        },

        getTexture : function(path) {
            var entry = this._textures[path];

            if (defined(entry)) {
                entry.count++;
                return entry.texture;
            }

            return undefined;
        },

        releaseTexture : function(path) {
            var entry = this._textures[path];
            if (defined(entry) && --entry.count === 0) {
                entry.texture = entry.texture && entry.texture.destroy();
                this._textures[path] = undefined;
            }
        }
    };

    Material._materialCache = {
        _materials : {},
        addMaterial : function(type, materialTemplate) {
            this._materials[type] = materialTemplate;
        },
        getMaterial : function(type) {
            return this._materials[type];
        }
    };

    Material.DefaultImageId = 'czm_defaultImage';
    Material.DefaultCubeMapId = 'czm_defaultCubeMap';

    Material.ColorType = 'Color';
    Material._materialCache.addMaterial(Material.ColorType, {
        type : Material.ColorType,
        uniforms : {
            color : new Color(1.0, 0.0, 0.0, 0.5)
        },
        components : {
            diffuse : 'color.rgb',
            alpha : 'color.a'
        }
    });

    Material.ImageType = 'Image';
    Material._materialCache.addMaterial(Material.ImageType, {
        type : Material.ImageType,
        uniforms : {
            image : Material.DefaultImageId,
            repeat : new Cartesian2(1.0, 1.0)
        },
        components : {
            diffuse : 'texture2D(image, fract(repeat * materialInput.st)).rgb',
            alpha : 'texture2D(image, fract(repeat * materialInput.st)).a'
        }
    });

    Material.DiffuseMapType = 'DiffuseMap';
    Material._materialCache.addMaterial(Material.DiffuseMapType, {
        type : Material.DiffuseMapType,
        uniforms : {
            image : Material.DefaultImageId,
            channels : 'rgb',
            repeat : new Cartesian2(1.0, 1.0)
        },
        components : {
            diffuse : 'texture2D(image, fract(repeat * materialInput.st)).channels'
        }
    });

    Material.AlphaMapType = 'AlphaMap';
    Material._materialCache.addMaterial(Material.AlphaMapType, {
        type : Material.AlphaMapType,
        uniforms : {
            image : Material.DefaultImageId,
            channel : 'a',
            repeat : new Cartesian2(1.0, 1.0)
        },
        components : {
            alpha : 'texture2D(image, fract(repeat * materialInput.st)).channel'
        }
    });

    Material.SpecularMapType = 'SpecularMap';
    Material._materialCache.addMaterial(Material.SpecularMapType, {
        type : Material.SpecularMapType,
        uniforms : {
            image : Material.DefaultImageId,
            channel : 'r',
            repeat : new Cartesian2(1.0, 1.0)
        },
        components : {
            specular : 'texture2D(image, fract(repeat * materialInput.st)).channel'
        }
    });

    Material.EmissionMapType = 'EmissionMap';
    Material._materialCache.addMaterial(Material.EmissionMapType, {
        type : Material.EmissionMapType,
        uniforms : {
            image : Material.DefaultImageId,
            channels : 'rgb',
            repeat : new Cartesian2(1.0, 1.0)
        },
        components : {
            emission : 'texture2D(image, fract(repeat * materialInput.st)).channels'
        }
    });

    Material.BumpMapType = 'BumpMap';
    Material._materialCache.addMaterial(Material.BumpMapType, {
        type : Material.BumpMapType,
        uniforms : {
            image : Material.DefaultImageId,
            channel : 'r',
            strength : 0.8,
            repeat : new Cartesian2(1.0, 1.0)
        },
        source : BumpMapMaterial
    });

    Material.NormalMapType = 'NormalMap';
    Material._materialCache.addMaterial(Material.NormalMapType, {
        type : Material.NormalMapType,
        uniforms : {
            image : Material.DefaultImageId,
            channels : 'rgb',
            strength : 0.8,
            repeat : new Cartesian2(1.0, 1.0)
        },
        source : NormalMapMaterial
    });

    Material.ReflectionType = 'Reflection';
    Material._materialCache.addMaterial(Material.ReflectionType, {
        type : Material.ReflectionType,
        uniforms : {
            cubeMap : Material.DefaultCubeMapId,
            channels : 'rgb'
        },
        source : ReflectionMaterial
    });

    Material.RefractionType = 'Refraction';
    Material._materialCache.addMaterial(Material.RefractionType, {
        type : Material.RefractionType,
        uniforms : {
            cubeMap : Material.DefaultCubeMapId,
            channels : 'rgb',
            indexOfRefractionRatio : 0.9
        },
        source : RefractionMaterial
    });

    Material.FresnelType = 'Fresnel';
    Material._materialCache.addMaterial(Material.FresnelType, {
        type : Material.FresnelType,
        materials : {
            reflection : {
                type : Material.ReflectionType
            },
            refraction : {
                type : Material.RefractionType
            }
        },
        source : FresnelMaterial
    });

    Material.BrickType = 'Brick';
    Material._materialCache.addMaterial(Material.BrickType, {
        type : Material.BrickType,
        uniforms : {
            brickColor : new Color(0.6, 0.3, 0.1, 1.0),
            mortarColor : new Color(0.8, 0.8, 0.7, 1.0),
            brickSize : new Cartesian2(0.3, 0.15),
            brickPct : new Cartesian2(0.9, 0.85),
            brickRoughness : 0.2,
            mortarRoughness : 0.1
        },
        source : BrickMaterial
    });

    Material.WoodType = 'Wood';
    Material._materialCache.addMaterial(Material.WoodType, {
        type : Material.WoodType,
        uniforms : {
            lightWoodColor : new Color(0.6, 0.3, 0.1, 1.0),
            darkWoodColor : new Color(0.4, 0.2, 0.07, 1.0),
            ringFrequency : 3.0,
            noiseScale : new Cartesian2(0.7, 0.5),
            grainFrequency : 27.0
        },
        source : WoodMaterial
    });

    Material.AsphaltType = 'Asphalt';
    Material._materialCache.addMaterial(Material.AsphaltType, {
        type : Material.AsphaltType,
        uniforms : {
            asphaltColor : new Color(0.15, 0.15, 0.15, 1.0),
            bumpSize : 0.02,
            roughness : 0.2
        },
        source : AsphaltMaterial
    });

    Material.CementType = 'Cement';
    Material._materialCache.addMaterial(Material.CementType, {
        type : Material.CementType,
        uniforms : {
            cementColor : new Color(0.95, 0.95, 0.85, 1.0),
            grainScale : 0.01,
            roughness : 0.3
        },
        source : CementMaterial
    });

    Material.GrassType = 'Grass';
    Material._materialCache.addMaterial(Material.GrassType, {
        type : Material.GrassType,
        uniforms : {
            grassColor : new Color(0.25, 0.4, 0.1, 1.0),
            dirtColor : new Color(0.1, 0.1, 0.1, 1.0),
            patchiness : 1.5
        },
        source : GrassMaterial
    });

    Material.GridType = 'Grid';
    Material._materialCache.addMaterial(Material.GridType, {
        type : Material.GridType,
        uniforms : {
            color : new Color(0.0, 1.0, 0.0, 1.0),
            cellAlpha : 0.1,
            lineCount : new Cartesian2(8.0, 8.0),
            lineThickness : new Cartesian2(1.0, 1.0)
        },
        source : GridMaterial
    });

    Material.StripeType = 'Stripe';
    Material._materialCache.addMaterial(Material.StripeType, {
        type : Material.StripeType,
        uniforms : {
            horizontal : true,
            lightColor : new Color(1.0, 1.0, 1.0, 0.5),
            darkColor : new Color(0.0, 0.0, 1.0, 0.5),
            offset : 0.0,
            repeat : 5.0
        },
        source : StripeMaterial
    });

    Material.CheckerboardType = 'Checkerboard';
    Material._materialCache.addMaterial(Material.CheckerboardType, {
        type : Material.CheckerboardType,
        uniforms : {
            lightColor : new Color(1.0, 1.0, 1.0, 0.5),
            darkColor : new Color(0.0, 0.0, 0.0, 0.5),
            repeat : new Cartesian2(5.0, 5.0)
        },
        source : CheckerboardMaterial
    });

    Material.DotType = 'Dot';
    Material._materialCache.addMaterial(Material.DotType, {
        type : Material.DotType,
        uniforms : {
            lightColor : new Color(1.0, 1.0, 0.0, 0.75),
            darkColor : new Color(0.0, 1.0, 1.0, 0.75),
            repeat : new Cartesian2(5.0, 5.0)
        },
        source : DotMaterial
    });

    Material.TyeDyeType = 'TieDye';
    Material._materialCache.addMaterial(Material.TyeDyeType, {
        type : Material.TyeDyeType,
        uniforms : {
            lightColor : new Color(1.0, 1.0, 0.0, 0.75),
            darkColor : new Color(1.0, 0.0, 0.0, 0.75),
            frequency : 5.0
        },
        source : TieDyeMaterial
    });

    Material.FacetType = 'Facet';
    Material._materialCache.addMaterial(Material.FacetType, {
        type : Material.FacetType,
        uniforms : {
            lightColor : new Color(0.25, 0.25, 0.25, 0.75),
            darkColor : new Color(0.75, 0.75, 0.75, 0.75),
            frequency : 10.0
        },
        source : FacetMaterial
    });

    Material.BlobType = 'Blob';
    Material._materialCache.addMaterial(Material.BlobType, {
        type : Material.BlobType,
        uniforms : {
            lightColor : new Color(1.0, 1.0, 1.0, 0.5),
            darkColor : new Color(0.0, 0.0, 1.0, 0.5),
            frequency : 10.0
        },
        source : BlobMaterial
    });

    Material.WaterType = 'Water';
    Material._materialCache.addMaterial(Material.WaterType, {
        type : Material.WaterType,
        uniforms : {
            baseWaterColor : {
                red : 0.2,
                green : 0.3,
                blue : 0.6,
                alpha : 1.0
            },
            blendColor : {
                red : 0.0,
                green : 1.0,
                blue : 0.699,
                alpha : 1.0
            },
            specularMap : Material.DefaultImageId,
            normalMap : Material.DefaultImageId,
            frequency : 10.0,
            animationSpeed : 0.01,
            amplitude : 1.0,
            specularIntensity : 0.5,
            fadeFactor : 1.0
        },
        source : WaterMaterial
    });

    Material.RimLightingType = 'RimLighting';
    Material._materialCache.addMaterial(Material.RimLightingType, {
        type : Material.RimLightingType,
        uniforms : {
            color : new Color(1.0, 0.0, 0.0, 0.7),
            rimColor : new Color(1.0, 1.0, 1.0, 0.4),
            width : 0.3
        },
        source : RimLightingMaterial
    });

    Material.ErosionType = 'Erosion';
    Material._materialCache.addMaterial(Material.ErosionType, {
        type : Material.ErosionType,
        uniforms : {
            color : new Color(1.0, 0.0, 0.0, 0.5),
            time : 1.0
        },
        source : ErosionMaterial
    });

    Material.FadeType = 'Fade';
    Material._materialCache.addMaterial(Material.FadeType, {
        type : Material.FadeType,
        uniforms : {
            fadeInColor : new Color(1.0, 0.0, 0.0, 1.0),
            fadeOutColor : new Color(0.0, 0.0, 0.0, 0.0),
            maximumDistance : 0.5,
            repeat : true,
            fadeDirection : {
                x : true,
                y : true
            },
            time : new Cartesian2(0.5, 0.5)
        },
        source : FadeMaterial
    });

    Material.PolylineArrowType = 'PolylineArrow';
    Material._materialCache.addMaterial(Material.PolylineArrowType, {
        type : Material.PolylineArrowType,
        uniforms : {
            color : new Color(1.0, 1.0, 1.0, 1.0)
        },
        source : PolylineArrowMaterial
    });

    Material.PolylineGlowType = 'PolylineGlow';
    Material._materialCache.addMaterial(Material.PolylineGlowType, {
        type : Material.PolylineGlowType,
        uniforms : {
            color : new Color(0.0, 0.5, 1.0, 1.0),
            glowPower : 0.1
        },
        source : PolylineGlowMaterial
    });

    Material.PolylineOutlineType = 'PolylineOutline';
    Material._materialCache.addMaterial(Material.PolylineOutlineType, {
        type : Material.PolylineOutlineType,
        uniforms : {
            color : new Color(1.0, 1.0, 1.0, 1.0),
            outlineColor : new Color(1.0, 0.0, 0.0, 1.0),
            outlineWidth : 1.0
        },
        source : PolylineOutlineMaterial
    });

    return Material;
});
;�TI"required_assets_digest;�TI"%cdd391ee69e8ce6a296c46b26d31dc1b;�FI"
_version;�TI"%6776f581a4329e299531e1d52aa59832;�F