text: var Three = (function (exports) {
  	'use strict';
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	// WARNING: This file was auto-generated, any change will be overridden in next release. Please use configs/es6.conf.js then run "npm run convert". //
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	var _Math = {
  
  		DEG2RAD: Math.PI / 180,
  		RAD2DEG: 180 / Math.PI,
  
  		generateUUID: ( function () {
  
  			// http://stackoverflow.com/questions/105034/how-to-create-a-guid-uuid-in-javascript/21963136#21963136
  
  			var lut = [];
  
  			for ( var i = 0; i < 256; i ++ ) {
  
  				lut[ i ] = ( i < 16 ? '0' : '' ) + ( i ).toString( 16 );
  
  			}
  
  			return function generateUUID() {
  
  				var d0 = Math.random() * 0xffffffff | 0;
  				var d1 = Math.random() * 0xffffffff | 0;
  				var d2 = Math.random() * 0xffffffff | 0;
  				var d3 = Math.random() * 0xffffffff | 0;
  				var uuid = lut[ d0 & 0xff ] + lut[ d0 >> 8 & 0xff ] + lut[ d0 >> 16 & 0xff ] + lut[ d0 >> 24 & 0xff ] + '-' +
  					lut[ d1 & 0xff ] + lut[ d1 >> 8 & 0xff ] + '-' + lut[ d1 >> 16 & 0x0f | 0x40 ] + lut[ d1 >> 24 & 0xff ] + '-' +
  					lut[ d2 & 0x3f | 0x80 ] + lut[ d2 >> 8 & 0xff ] + '-' + lut[ d2 >> 16 & 0xff ] + lut[ d2 >> 24 & 0xff ] +
  					lut[ d3 & 0xff ] + lut[ d3 >> 8 & 0xff ] + lut[ d3 >> 16 & 0xff ] + lut[ d3 >> 24 & 0xff ];
  
  				// .toUpperCase() here flattens concatenated strings to save heap memory space.
  				return uuid.toUpperCase();
  
  			};
  
  		} )(),
  
  		clamp: function ( value, min, max ) {
  
  			return Math.max( min, Math.min( max, value ) );
  
  		},
  
  		// compute euclidian modulo of m % n
  		// https://en.wikipedia.org/wiki/Modulo_operation
  
  		euclideanModulo: function ( n, m ) {
  
  			return ( ( n % m ) + m ) % m;
  
  		},
  
  		// Linear mapping from range <a1, a2> to range <b1, b2>
  
  		mapLinear: function ( x, a1, a2, b1, b2 ) {
  
  			return b1 + ( x - a1 ) * ( b2 - b1 ) / ( a2 - a1 );
  
  		},
  
  		// https://en.wikipedia.org/wiki/Linear_interpolation
  
  		lerp: function ( x, y, t ) {
  
  			return ( 1 - t ) * x + t * y;
  
  		},
  
  		// http://en.wikipedia.org/wiki/Smoothstep
  
  		smoothstep: function ( x, min, max ) {
  
  			if ( x <= min ) return 0;
  			if ( x >= max ) return 1;
  
  			x = ( x - min ) / ( max - min );
  
  			return x * x * ( 3 - 2 * x );
  
  		},
  
  		smootherstep: function ( x, min, max ) {
  
  			if ( x <= min ) return 0;
  			if ( x >= max ) return 1;
  
  			x = ( x - min ) / ( max - min );
  
  			return x * x * x * ( x * ( x * 6 - 15 ) + 10 );
  
  		},
  
  		// Random integer from <low, high> interval
  
  		randInt: function ( low, high ) {
  
  			return low + Math.floor( Math.random() * ( high - low + 1 ) );
  
  		},
  
  		// Random float from <low, high> interval
  
  		randFloat: function ( low, high ) {
  
  			return low + Math.random() * ( high - low );
  
  		},
  
  		// Random float from <-range/2, range/2> interval
  
  		randFloatSpread: function ( range ) {
  
  			return range * ( 0.5 - Math.random() );
  
  		},
  
  		degToRad: function ( degrees ) {
  
  			return degrees * _Math.DEG2RAD;
  
  		},
  
  		radToDeg: function ( radians ) {
  
  			return radians * _Math.RAD2DEG;
  
  		},
  
  		isPowerOfTwo: function ( value ) {
  
  			return ( value & ( value - 1 ) ) === 0 && value !== 0;
  
  		},
  
  		ceilPowerOfTwo: function ( value ) {
  
  			return Math.pow( 2, Math.ceil( Math.log( value ) / Math.LN2 ) );
  
  		},
  
  		floorPowerOfTwo: function ( value ) {
  
  			return Math.pow( 2, Math.floor( Math.log( value ) / Math.LN2 ) );
  
  		}
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	function Node( type ) {
  
  		this.uuid = _Math.generateUUID();
  
  		this.name = "";
  
  		this.type = type;
  
  		this.userData = {};
  
  	}
  
  	Node.prototype = {
  
  		constructor: Node,
  
  		isNode: true,
  
  		parse: function ( builder, settings ) {
  
  			settings = settings || {};
  
  			builder.parsing = true;
  
  			this.build( builder.addFlow( settings.slot, settings.cache, settings.context ), 'v4' );
  
  			builder.clearVertexNodeCode();
  			builder.clearFragmentNodeCode();
  
  			builder.removeFlow();
  
  			builder.parsing = false;
  
  		},
  
  		parseAndBuildCode: function ( builder, output, settings ) {
  
  			settings = settings || {};
  
  			this.parse( builder, settings );
  
  			return this.buildCode( builder, output, settings );
  
  		},
  
  		buildCode: function ( builder, output, settings ) {
  
  			settings = settings || {};
  
  			var data = { result: this.build( builder.addFlow( settings.slot, settings.cache, settings.context ), output ) };
  
  			data.code = builder.clearNodeCode();
  
  			builder.removeFlow();
  
  			return data;
  
  		},
  
  		build: function ( builder, output, uuid ) {
  
  			output = output || this.getType( builder, output );
  
  			var data = builder.getNodeData( uuid || this );
  
  			if ( builder.parsing ) {
  
  				this.appendDepsNode( builder, data, output );
  
  			}
  
  			if ( builder.nodes.indexOf( this ) === - 1 ) {
  
  				builder.nodes.push( this );
  
  			}
  
  			if ( this.updateFrame !== undefined && builder.updaters.indexOf( this ) === - 1 ) {
  
  				builder.updaters.push( this );
  
  			}
  
  			return this.generate( builder, output, uuid );
  
  		},
  
  		appendDepsNode: function ( builder, data, output ) {
  
  			data.deps = ( data.deps || 0 ) + 1;
  
  			var outputLen = builder.getTypeLength( output );
  
  			if ( outputLen > ( data.outputMax || 0 ) || this.getType( builder, output ) ) {
  
  				data.outputMax = outputLen;
  				data.output = output;
  
  			}
  
  		},
  
  		setName: function ( name ) {
  
  			this.name = name;
  
  			return this;
  
  		},
  
  		getName: function ( builder ) {
  
  			return this.name;
  
  		},
  
  		getType: function ( builder, output ) {
  
  			return output === 'sampler2D' || output === 'samplerCube' ? output : this.type;
  
  		},
  
  		getJSONNode: function ( meta ) {
  
  			var isRootObject = ( meta === undefined || typeof meta === 'string' );
  
  			if ( ! isRootObject && meta.nodes[ this.uuid ] !== undefined ) {
  
  				return meta.nodes[ this.uuid ];
  
  			}
  
  		},
  
  		copy: function ( source ) {
  
  			if ( source.name !== undefined ) this.name = source.name;
  
  			if ( source.userData !== undefined ) this.userData = JSON.parse( JSON.stringify( source.userData ) );
  
  		},
  
  		createJSONNode: function ( meta ) {
  
  			var isRootObject = ( meta === undefined || typeof meta === 'string' );
  
  			var data = {};
  
  			if ( typeof this.nodeType !== "string" ) throw new Error( "Node does not allow serialization." );
  
  			data.uuid = this.uuid;
  			data.nodeType = this.nodeType;
  
  			if ( this.name !== "" ) data.name = this.name;
  
  			if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
  
  			if ( ! isRootObject ) {
  
  				meta.nodes[ this.uuid ] = data;
  
  			}
  
  			return data;
  
  		},
  
  		toJSON: function ( meta ) {
  
  			return this.getJSONNode( meta ) || this.createJSONNode( meta );
  
  		}
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	function TempNode( type, params ) {
  
  		Node.call( this, type );
  
  		params = params || {};
  
  		this.shared = params.shared !== undefined ? params.shared : true;
  		this.unique = params.unique !== undefined ? params.unique : false;
  
  	}
  
  	TempNode.prototype = Object.create( Node.prototype );
  	TempNode.prototype.constructor = TempNode;
  
  	TempNode.prototype.build = function ( builder, output, uuid, ns ) {
  
  		output = output || this.getType( builder );
  
  		if ( this.isShared( builder, output ) ) {
  
  			var isUnique = this.isUnique( builder, output );
  
  			if ( isUnique && this.constructor.uuid === undefined ) {
  
  				this.constructor.uuid = _Math.generateUUID();
  
  			}
  
  			uuid = builder.getUuid( uuid || this.getUuid(), ! isUnique );
  
  			var data = builder.getNodeData( uuid ),
  				type = data.output || this.getType( builder );
  
  			if ( builder.parsing ) {
  
  				if ( ( data.deps || 0 ) > 0 ) {
  
  					this.appendDepsNode( builder, data, output );
  
  					return this.generate( builder, output, uuid );
  
  				}
  
  				return Node.prototype.build.call( this, builder, output, uuid );
  
  			} else if ( isUnique ) {
  
  				data.name = data.name || Node.prototype.build.call( this, builder, output, uuid );
  
  				return data.name;
  
  			} else if ( ! this.isShared( builder, type ) || ( ! builder.optimize || data.deps == 1 ) ) {
  
  				return Node.prototype.build.call( this, builder, output, uuid );
  
  			}
  
  			uuid = this.getUuid( false );
  
  			var name = this.getTemp( builder, uuid );
  
  			if ( name ) {
  
  				return builder.format( name, type, output );
  
  			} else {
  
  				name = TempNode.prototype.generate.call( this, builder, output, uuid, data.output, ns );
  
  				var code = this.generate( builder, type, uuid );
  
  				builder.addNodeCode( name + ' = ' + code + ';' );
  
  				return builder.format( name, type, output );
  
  			}
  
  		}
  
  		return Node.prototype.build.call( this, builder, output, uuid );
  
  	};
  
  	TempNode.prototype.isShared = function ( builder, output ) {
  
  		return output !== 'sampler2D' && output !== 'samplerCube' && this.shared;
  
  	};
  
  	TempNode.prototype.isUnique = function ( builder, output ) {
  
  		return this.unique;
  
  	};
  
  	TempNode.prototype.getUuid = function ( unique ) {
  
  		var uuid = unique || unique == undefined ? this.constructor.uuid || this.uuid : this.uuid;
  
  		if ( typeof this.scope == "string" ) uuid = this.scope + '-' + uuid;
  
  		return uuid;
  
  	};
  
  	TempNode.prototype.getTemp = function ( builder, uuid ) {
  
  		uuid = uuid || this.uuid;
  
  		var tempVar = builder.getVars()[ uuid ];
  
  		return tempVar ? tempVar.name : undefined;
  
  	};
  
  	TempNode.prototype.generate = function ( builder, output, uuid, type, ns ) {
  
  		if ( ! this.isShared( builder, output ) ) console.error( "TempNode is not shared!" );
  
  		uuid = uuid || this.uuid;
  
  		return builder.getTempVar( uuid, type || this.getType( builder ), ns ).name;
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	var declarationRegexp = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=?\s?(.*?)(\;|$)/i;
  
  	function ConstNode( src, useDefine ) {
  
  		TempNode.call( this );
  
  		this.eval( src || ConstNode.PI, useDefine );
  
  	}
  
  	ConstNode.PI = 'PI';
  	ConstNode.PI2 = 'PI2';
  	ConstNode.RECIPROCAL_PI = 'RECIPROCAL_PI';
  	ConstNode.RECIPROCAL_PI2 = 'RECIPROCAL_PI2';
  	ConstNode.LOG2 = 'LOG2';
  	ConstNode.EPSILON = 'EPSILON';
  
  	ConstNode.prototype = Object.create( TempNode.prototype );
  	ConstNode.prototype.constructor = ConstNode;
  	ConstNode.prototype.nodeType = "Const";
  
  	ConstNode.prototype.getType = function ( builder ) {
  
  		return builder.getTypeByFormat( this.type );
  
  	};
  
  	ConstNode.prototype.eval = function ( src, useDefine ) {
  
  		this.src = src || '';
  
  		var name, type, value = "";
  
  		var match = this.src.match( declarationRegexp );
  
  		this.useDefine = useDefine || this.src.charAt( 0 ) === '#';
  
  		if ( match && match.length > 1 ) {
  
  			type = match[ 1 ];
  			name = match[ 2 ];
  			value = match[ 3 ];
  
  		} else {
  
  			name = this.src;
  			type = 'f';
  
  		}
  
  		this.name = name;
  		this.type = type;
  		this.value = value;
  
  	};
  
  	ConstNode.prototype.build = function ( builder, output ) {
  
  		if ( output === 'source' ) {
  
  			if ( this.value ) {
  
  				if ( this.useDefine ) {
  
  					return '#define ' + this.name + ' ' + this.value;
  
  				}
  
  				return 'const ' + this.type + ' ' + this.name + ' = ' + this.value + ';';
  
  			} else if ( this.useDefine ) {
  
  				return this.src;
  
  			}
  
  		} else {
  
  			builder.include( this );
  
  			return builder.format( this.name, this.getType( builder ), output );
  
  		}
  
  	};
  
  	ConstNode.prototype.generate = function ( builder, output ) {
  
  		return builder.format( this.name, this.getType( builder ), output );
  
  	};
  
  	ConstNode.prototype.copy = function ( source ) {
  
  		TempNode.prototype.copy.call( this, source );
  
  		this.eval( source.src, source.useDefine );
  
  	};
  
  	ConstNode.prototype.toJSON = function ( meta ) {
  
  		var data = this.getJSONNode( meta );
  
  		if ( ! data ) {
  
  			data = this.createJSONNode( meta );
  
  			data.src = this.src;
  
  			if ( data.useDefine === true ) data.useDefine = true;
  
  		}
  
  		return data;
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	var FunctionNode = function( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions ) {
  
  		src = src || '';
  
  		this.isMethod = typeof includesOrType !== "string";
  		this.useKeywords = true;
  
  		TempNode.call( this, this.isMethod ? null : includesOrType );
  
  		if ( this.isMethod ) this.eval( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions );
  		else this.eval( src, extensionsOrIncludes, keywordsOrExtensions );
  
  	};
  
  	FunctionNode.rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
  	FunctionNode.rProperties = /[a-z_0-9]+/ig;
  
  	FunctionNode.prototype = Object.create( TempNode.prototype );
  	FunctionNode.prototype.constructor = FunctionNode;
  	FunctionNode.prototype.nodeType = "Function";
  
  	FunctionNode.prototype.eval = function( src, includes, extensions, keywords ) {
  
  		src = ( src || '' ).trim();
  
  		this.includes = includes || [];
  		this.extensions = extensions || {};
  		this.keywords = keywords || {};
  
  		if ( this.isMethod ) {
  
  			var match = src.match( FunctionNode.rDeclaration );
  
  			this.inputs = [];
  
  			if ( match && match.length == 4 ) {
  
  				this.type = match[ 1 ];
  				this.name = match[ 2 ];
  
  				var inputs = match[ 3 ].match( FunctionNode.rProperties );
  
  				if ( inputs ) {
  
  					var i = 0;
  
  					while ( i < inputs.length ) {
  
  						var qualifier = inputs[ i ++ ];
  						var type, name;
  
  						if ( qualifier == 'in' || qualifier == 'out' || qualifier == 'inout' ) {
  
  							type = inputs[ i ++ ];
  
  						} else {
  
  							type = qualifier;
  							qualifier = '';
  
  						}
  
  						name = inputs[ i ++ ];
  
  						this.inputs.push( {
  							name : name,
  							type : type,
  							qualifier : qualifier
  						} );
  
  					}
  
  				}
  
  			} else {
  
  				this.type = '';
  				this.name = '';
  
  			}
  
  		}
  
  		this.value = src;
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  	// WARNING: This file was auto-generated, any change will be overridden in next release. Please use configs/es6.conf.js then run "npm run convert". //
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	var NodeLib = {
  
  		nodes: {},
  		keywords: {},
  
  		add: function( node ) {
  
  			this.nodes[ node.name ] = node;
  
  		},
  
  		addKeyword: function( name, callback, cache ) {
  
  			cache = cache !== undefined ? cache : true;
  
  			this.keywords[ name ] = { callback : callback, cache : cache };
  
  		},
  
  		remove: function( node ) {
  
  			delete this.nodes[ node.name ];
  
  		},
  
  		removeKeyword: function( name ) {
  
  			delete this.keywords[ name ];
  
  		},
  
  		get: function( name ) {
  
  			return this.nodes[ name ];
  
  		},
  
  		getKeyword: function( name, material ) {
  
  			return this.keywords[ name ].callback.call( this, material );
  
  		},
  
  		getKeywordData: function( name ) {
  
  			return this.keywords[ name ];
  
  		},
  
  		contains: function( name ) {
  
  			return this.nodes[ name ] != undefined;
  
  		},
  
  		containsKeyword: function( name ) {
  
  			return this.keywords[ name ] != undefined;
  
  		}
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	var vertexDict = [ 'uv', 'uv2' ],
  		fragmentDict = [ 'vUv', 'vUv2' ];
  
  	function UVNode( index ) {
  
  		TempNode.call( this, 'v2', { shared: false } );
  
  		this.index = index || 0;
  
  	}
  
  	UVNode.prototype = Object.create( TempNode.prototype );
  	UVNode.prototype.constructor = UVNode;
  	UVNode.prototype.nodeType = "UV";
  
  	UVNode.prototype.generate = function ( builder, output ) {
  
  		builder.requires.uv[ this.index ] = true;
  
  		var result = builder.isShader( 'vertex' ) ? vertexDict[ this.index ] : fragmentDict[ this.index ];
  
  		return builder.format( result, this.getType( builder ), output );
  
  	};
  
  	UVNode.prototype.copy = function ( source ) {
  
  		TempNode.prototype.copy.call( this, source );
  
  		this.index = source.index;
  
  	};
  
  	UVNode.prototype.toJSON = function ( meta ) {
  
  		var data = this.getJSONNode( meta );
  
  		if ( ! data ) {
  
  			data = this.createJSONNode( meta );
  
  			data.index = this.index;
  
  		}
  
  		return data;
  
  	};
  
  	NodeLib.addKeyword( 'uv', function () {
  
  		return new UVNode();
  
  	} );
  
  	NodeLib.addKeyword( 'uv2', function () {
  
  		return new UVNode( 1 );
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	function PositionNode( scope ) {
  
  		TempNode.call( this, 'v3' );
  
  		this.scope = scope || PositionNode.LOCAL;
  
  	}
  
  	PositionNode.LOCAL = 'local';
  	PositionNode.WORLD = 'world';
  	PositionNode.VIEW = 'view';
  	PositionNode.PROJECTION = 'projection';
  
  	PositionNode.prototype = Object.create( TempNode.prototype );
  	PositionNode.prototype.constructor = PositionNode;
  	PositionNode.prototype.nodeType = "Position";
  
  	PositionNode.prototype.getType = function ( ) {
  
  		switch ( this.scope ) {
  
  			case PositionNode.PROJECTION:
  
  				return 'v4';
  
  		}
  
  		return this.type;
  
  	};
  
  	PositionNode.prototype.isShared = function ( builder ) {
  
  		switch ( this.scope ) {
  
  			case PositionNode.LOCAL:
  			case PositionNode.WORLD:
  
  				return false;
  
  		}
  
  		return true;
  
  	};
  
  	PositionNode.prototype.generate = function ( builder, output ) {
  
  		var result;
  
  		switch ( this.scope ) {
  
  			case PositionNode.LOCAL:
  
  				builder.requires.position = true;
  
  				result = builder.isShader( 'vertex' ) ? 'transformed' : 'vPosition';
  
  				break;
  
  			case PositionNode.WORLD:
  
  				builder.requires.worldPosition = true;
  
  				result = 'vWPosition';
  
  				break;
  
  			case PositionNode.VIEW:
  
  				result = builder.isShader( 'vertex' ) ? '-mvPosition.xyz' : 'vViewPosition';
  
  				break;
  
  			case PositionNode.PROJECTION:
  
  				result = builder.isShader( 'vertex' ) ? '( projectionMatrix * modelViewMatrix * vec4( position, 1.0 ) )' : 'vec4( 0.0 )';
  
  				break;
  
  		}
  
  		return builder.format( result, this.getType( builder ), output );
  
  	};
  
  	PositionNode.prototype.copy = function ( source ) {
  
  		TempNode.prototype.copy.call( this, source );
  
  		this.scope = source.scope;
  
  	};
  
  	PositionNode.prototype.toJSON = function ( meta ) {
  
  		var data = this.getJSONNode( meta );
  
  		if ( ! data ) {
  
  			data = this.createJSONNode( meta );
  
  			data.scope = this.scope;
  
  		}
  
  		return data;
  
  	};
  
  	NodeLib.addKeyword( 'position', function () {
  
  		return new PositionNode();
  
  	} );
  
  	NodeLib.addKeyword( 'worldPosition', function () {
  
  		return new PositionNode( PositionNode.WORLD );
  
  	} );
  
  	NodeLib.addKeyword( 'viewPosition', function () {
  
  		return new PositionNode( NormalNode.VIEW );
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	function NormalNode$1( scope ) {
  
  		TempNode.call( this, 'v3' );
  
  		this.scope = scope || NormalNode$1.LOCAL;
  
  	}
  
  	NormalNode$1.LOCAL = 'local';
  	NormalNode$1.WORLD = 'world';
  	NormalNode$1.VIEW = 'view';
  
  	NormalNode$1.prototype = Object.create( TempNode.prototype );
  	NormalNode$1.prototype.constructor = NormalNode$1;
  	NormalNode$1.prototype.nodeType = "Normal";
  
  	NormalNode$1.prototype.isShared = function ( builder ) {
  
  		switch ( this.scope ) {
  
  			case NormalNode$1.WORLD:
  
  				return true;
  
  		}
  
  		return false;
  
  	};
  
  	NormalNode$1.prototype.generate = function ( builder, output ) {
  
  		var result;
  
  		switch ( this.scope ) {
  
  			case NormalNode$1.LOCAL:
  
  				builder.requires.normal = true;
  
  				result = 'normal';
  
  				break;
  
  			case NormalNode$1.WORLD:
  
  				builder.requires.worldNormal = true;
  
  				result = builder.isShader( 'vertex' ) ? '( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz' : 'vWNormal';
  
  				break;
  
  			case NormalNode$1.VIEW:
  
  				result = 'vNormal';
  
  				break;
  
  		}
  
  		return builder.format( result, this.getType( builder ), output );
  
  	};
  
  	NormalNode$1.prototype.copy = function ( source ) {
  
  		TempNode.prototype.copy.call( this, source );
  
  		this.scope = source.scope;
  
  	};
  
  	NormalNode$1.prototype.toJSON = function ( meta ) {
  
  		var data = this.getJSONNode( meta );
  
  		if ( ! data ) {
  
  			data = this.createJSONNode( meta );
  
  			data.scope = this.scope;
  
  		}
  
  		return data;
  
  	};
  
  	NodeLib.addKeyword( 'normal', function () {
  
  		return new NormalNode$1();
  
  	} );
  
  	NodeLib.addKeyword( 'worldNormal', function () {
  
  		return new NormalNode$1( NormalNode$1.WORLD );
  
  	} );
  
  	NodeLib.addKeyword( 'viewNormal', function () {
  
  		return new NormalNode$1( NormalNode$1.VIEW );
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	function InputNode( type, params ) {
  
  		params = params || {};
  		params.shared = params.shared !== undefined ? params.shared : false;
  
  		TempNode.call( this, type, params );
  
  		this.readonly = false;
  
  	}
  
  	InputNode.prototype = Object.create( TempNode.prototype );
  	InputNode.prototype.constructor = InputNode;
  
  	InputNode.prototype.isReadonly = function ( builder ) {
  
  		return this.readonly;
  
  	};
  
  	InputNode.prototype.copy = function ( source ) {
  
  		TempNode.prototype.copy.call( this, source );
  
  		if ( source.readonly !== undefined ) this.readonly = source.readonly;
  
  	};
  
  	InputNode.prototype.createJSONNode = function ( meta ) {
  
  		var data = TempNode.prototype.createJSONNode.call( this, meta );
  
  		if ( this.readonly === true ) data.readonly = this.readonly;
  
  		return data;
  
  	};
  
  	InputNode.prototype.generate = function ( builder, output, uuid, type, ns, needsUpdate ) {
  
  		uuid = builder.getUuid( uuid || this.getUuid() );
  		type = type || this.getType( builder );
  
  		var data = builder.getNodeData( uuid ),
  			readonly = this.isReadonly( builder ) && this.generateReadonly !== undefined;
  
  		if ( readonly ) {
  
  			return this.generateReadonly( builder, output, uuid, type, ns, needsUpdate );
  
  		} else {
  
  			if ( builder.isShader( 'vertex' ) ) {
  
  				if ( ! data.vertex ) {
  
  					data.vertex = builder.createVertexUniform( type, this, ns, needsUpdate );
  
  				}
  
  				return builder.format( data.vertex.name, type, output );
  
  			} else {
  
  				if ( ! data.fragment ) {
  
  					data.fragment = builder.createFragmentUniform( type, this, ns, needsUpdate );
  
  				}
  
  				return builder.format( data.fragment.name, type, output );
  
  			}
  
  		}
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	function FloatNode( value ) {
  
  		InputNode.call( this, 'f' );
  
  		this.value = value || 0;
  
  	}
  
  	FloatNode.prototype = Object.create( InputNode.prototype );
  	FloatNode.prototype.constructor = FloatNode;
  	FloatNode.prototype.nodeType = "Float";
  
  	FloatNode.prototype.generateReadonly = function ( builder, output, uuid, type, ns, needsUpdate ) {
  
  		return builder.format( this.value + ( this.value % 1 ? '' : '.0' ), type, output );
  
  	};
  
  	FloatNode.prototype.copy = function ( source ) {
  
  		InputNode.prototype.copy.call( this, source );
  
  		this.value = source.value;
  
  	};
  
  	FloatNode.prototype.toJSON = function ( meta ) {
  
  		var data = this.getJSONNode( meta );
  
  		if ( ! data ) {
  
  			data = this.createJSONNode( meta );
  
  			data.value = this.value;
  
  			if ( this.readonly === true ) data.readonly = true;
  
  		}
  
  		return data;
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	function TimerNode( scale, scope, timeScale ) {
  
  		FloatNode.call( this );
  
  		this.scale = scale !== undefined ? scale : 1;
  		this.scope = scope || TimerNode.GLOBAL;
  
  		this.timeScale = timeScale !== undefined ? timeScale : this.scale !== 1;
  
  	}
  
  	TimerNode.GLOBAL = 'global';
  	TimerNode.LOCAL = 'local';
  	TimerNode.DELTA = 'delta';
  
  	TimerNode.prototype = Object.create( FloatNode.prototype );
  	TimerNode.prototype.constructor = TimerNode;
  	TimerNode.prototype.nodeType = "Timer";
  
  	TimerNode.prototype.isReadonly = function () {
  
  		// never use TimerNode as readonly but aways as "uniform"
  
  		return false;
  
  	};
  
  	TimerNode.prototype.isUnique = function () {
  
  		// share TimerNode "uniform" input if is used on more time with others TimerNode
  
  		return this.timeScale && ( this.scope === TimerNode.GLOBAL || this.scope === TimerNode.DELTA );
  
  	};
  
  	TimerNode.prototype.updateFrame = function ( frame ) {
  
  		var scale = this.timeScale ? this.scale : 1;
  
  		switch ( this.scope ) {
  
  			case TimerNode.LOCAL:
  
  				this.value += frame.delta * scale;
  
  				break;
  
  			case TimerNode.DELTA:
  
  				this.value = frame.delta * scale;
  
  				break;
  
  			default:
  
  				this.value = frame.time * scale;
  
  		}
  
  	};
  
  	TimerNode.prototype.copy = function ( source ) {
  
  		FloatNode.prototype.copy.call( this, source );
  
  		this.scope = source.scope;
  		this.scale = source.scale;
  
  		this.timeScale = source.timeScale;
  
  	};
  
  	TimerNode.prototype.toJSON = function ( meta ) {
  
  		var data = this.getJSONNode( meta );
  
  		if ( ! data ) {
  
  			data = this.createJSONNode( meta );
  
  			data.scope = this.scope;
  			data.scale = this.scale;
  
  			data.timeScale = this.timeScale;
  
  		}
  
  		return data;
  
  	};
  
  	NodeLib.addKeyword( 'time', function () {
  
  		return new TimerNode();
  
  	} );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	// Fix circular dependency, see #2
  
  	//
  	//	Keywords
  	//
  
  	NodeLib.addKeyword( 'uv', function() {
  
  		return new UVNode();
  
  	} );
  
  	NodeLib.addKeyword( 'uv2', function() {
  
  		return new UVNode( 1 );
  
  	} );
  
  	NodeLib.addKeyword( 'position', function() {
  
  		return new PositionNode();
  
  	} );
  
  	NodeLib.addKeyword( 'worldPosition', function() {
  
  		return new PositionNode( PositionNode.WORLD );
  
  	} );
  
  	NodeLib.addKeyword( 'normal', function() {
  
  		return new NormalNode$1();
  
  	} );
  
  	NodeLib.addKeyword( 'worldNormal', function() {
  
  		return new NormalNode$1( NormalNode$1.WORLD );
  
  	} );
  
  	NodeLib.addKeyword( 'viewPosition', function() {
  
  		return new PositionNode( NormalNode$1.VIEW );
  
  	} );
  
  	NodeLib.addKeyword( 'viewNormal', function() {
  
  		return new NormalNode$1( NormalNode$1.VIEW );
  
  	} );
  
  	NodeLib.addKeyword( 'time', function() {
  
  		return new TimerNode();
  
  	} );
  
  	//
  	//	Luma
  	//
  
  	NodeLib.add( new ConstNode( "vec3 LUMA vec3(0.2125, 0.7154, 0.0721)" ) );
  
  	//
  	//	NormalMap
  	//
  
  	NodeLib.add( new FunctionNode( [
  		// Per-Pixel Tangent Space Normal Mapping
  		// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
  		"vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 map, vec2 mUv, vec2 scale ) {",
  		"	vec3 q0 = dFdx( eye_pos );",
  		"	vec3 q1 = dFdy( eye_pos );",
  		"	vec2 st0 = dFdx( mUv.st );",
  		"	vec2 st1 = dFdy( mUv.st );",
  		"	vec3 S = normalize( q0 * st1.t - q1 * st0.t );",
  		"	vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
  		"	vec3 N = normalize( surf_norm );",
  		"	vec3 mapN = map * 2.0 - 1.0;",
  		"	mapN.xy = scale * mapN.xy;",
  		"	mat3 tsn = mat3( S, T, N );",
  		"	return normalize( tsn * mapN );",
  		"}"
  	].join( "\n" ), null, { derivatives: true } ) );
  
  	//
  	//	Noise
  	//
  
  	NodeLib.add( new FunctionNode( [
  		"float snoise(vec2 co) {",
  		"	return fract( sin( dot(co.xy, vec2(12.9898,78.233) ) ) * 43758.5453 );",
  		"}"
  	].join( "\n" ) ) );
  
  	//
  	//	Hue
  	//
  
  	NodeLib.add( new FunctionNode( [
  		"vec3 hue_rgb(vec3 rgb, float adjustment) {",
  		"	const mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);",
  		"	const mat3 YIQtoRGB = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.107, 1.7046);",
  		"	vec3 yiq = RGBtoYIQ * rgb;",
  		"	float hue = atan(yiq.z, yiq.y) + adjustment;",
  		"	float chroma = sqrt(yiq.z * yiq.z + yiq.y * yiq.y);",
  		"	return YIQtoRGB * vec3(yiq.x, chroma * cos(hue), chroma * sin(hue));",
  		"}"
  	].join( "\n" ) ) );
  
  	//
  	//	Saturation
  	//
  
  	NodeLib.add( new FunctionNode( [
  		// Algorithm from Chapter 16 of OpenGL Shading Language
  		"vec3 saturation_rgb(vec3 rgb, float adjustment) {",
  		"	vec3 intensity = vec3(dot(rgb, LUMA));",
  		"	return mix(intensity, rgb, adjustment);",
  		"}"
  	].join( "\n" ) ) );
  
  	//
  	//	Luminance
  	//
  
  	NodeLib.add( new FunctionNode( [
  		// Algorithm from Chapter 10 of Graphics Shaders
  		"float luminance_rgb(vec3 rgb) {",
  		"	return dot(rgb, LUMA);",
  		"}"
  	].join( "\n" ) ) );
  
  	//
  	//	Vibrance
  	//
  
  	NodeLib.add( new FunctionNode( [
  		// Shader by Evan Wallace adapted by @lo-th
  		"vec3 vibrance_rgb(vec3 rgb, float adjustment) {",
  		"	float average = (rgb.r + rgb.g + rgb.b) / 3.0;",
  		"	float mx = max(rgb.r, max(rgb.g, rgb.b));",
  		"	float amt = (mx - average) * (-3.0 * adjustment);",
  		"	return mix(rgb.rgb, vec3(mx), amt);",
  		"}"
  	].join( "\n" ) ) );
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	// Fix circular dependency, see #2
  
  	FunctionNode.prototype.isShared = function( builder, output ) {
  
  		return ! this.isMethod;
  
  	};
  
  	FunctionNode.prototype.getType = function( builder ) {
  
  		return builder.getTypeByFormat( this.type );
  
  	};
  
  	FunctionNode.prototype.getInputByName = function( name ) {
  
  		var i = this.inputs.length;
  
  		while ( i -- ) {
  
  			if ( this.inputs[ i ].name === name )
  				return this.inputs[ i ];
  
  		}
  
  	};
  
  	FunctionNode.prototype.getIncludeByName = function( name ) {
  
  		var i = this.includes.length;
  
  		while ( i -- ) {
  
  			if ( this.includes[ i ].name === name )
  				return this.includes[ i ];
  
  		}
  
  	};
  
  	FunctionNode.prototype.generate = function( builder, output ) {
  
  		var match, offset = 0, src = this.value;
  
  		for ( var i = 0; i < this.includes.length; i ++ ) {
  
  			builder.include( this.includes[ i ], this );
  
  		}
  
  		for ( var ext in this.extensions ) {
  
  			builder.material.extensions[ ext ] = true;
  
  		}
  
  		while ( match = FunctionNode.rProperties.exec( this.value ) ) {
  
  			var prop = match[ 0 ], isGlobal = this.isMethod ? ! this.getInputByName( prop ) : true;
  			var reference = prop;
  
  			if ( this.keywords[ prop ] || ( this.useKeywords && isGlobal && NodeLib.containsKeyword( prop ) ) ) {
  
  				var node = this.keywords[ prop ];
  
  				if ( ! node ) {
  
  					var keyword = NodeLib.getKeywordData( prop );
  
  					if ( keyword.cache ) node = builder.keywords[ prop ];
  
  					node = node || NodeLib.getKeyword( prop, builder );
  
  					if ( keyword.cache ) builder.keywords[ prop ] = node;
  
  				}
  
  				reference = node.build( builder );
  
  			}
  
  			if ( prop != reference ) {
  
  				src = src.substring( 0, match.index + offset ) + reference + src.substring( match.index + prop.length + offset );
  
  				offset += reference.length - prop.length;
  
  			}
  
  			if ( this.getIncludeByName( reference ) === undefined && NodeLib.contains( reference ) ) {
  
  				builder.include( NodeLib.get( reference ) );
  
  			}
  
  		}
  
  		if ( output === 'source' ) {
  
  			return src;
  
  		} else if ( this.isMethod ) {
  
  			builder.include( this, false, src );
  
  			return this.name;
  
  		} else {
  
  			return builder.format( "(" + src + ")", this.getType( builder ), output );
  
  		}
  
  	};
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  
  	function LuminanceNode( rgb ) {
  
  		TempNode.call( this, 'f' );
  
  		this.rgb = rgb;
  
  	}
  
  	LuminanceNode.Nodes = ( function () {
  
  		var LUMA = new ConstNode( "vec3 LUMA vec3( 0.2125, 0.7154, 0.0721 )" );
  
  		var luminance = new FunctionNode( [
  			// Algorithm from Chapter 10 of Graphics Shaders
  			"float luminance( vec3 rgb ) {",
  
  			"	return dot( rgb, LUMA );",
  
  			"}"
  		].join( "\n" ), [ LUMA ] );
  
  		return {
  			LUMA: LUMA,
  			luminance: luminance
  		};
  
  	} )();
  
  	LuminanceNode.prototype = Object.create( TempNode.prototype );
  	LuminanceNode.prototype.constructor = LuminanceNode;
  	LuminanceNode.prototype.nodeType = "Luminance";
  
  	LuminanceNode.prototype.generate = function ( builder, output ) {
  
  		var luminance = builder.include( LuminanceNode.Nodes.luminance );
  
  		return builder.format( luminance + '( ' + this.rgb.build( builder, 'v3' ) + ' )', this.getType( builder ), output );
  
  	};
  
  	LuminanceNode.prototype.copy = function ( source ) {
  
  		TempNode.prototype.copy.call( this, source );
  
  		this.rgb = source.rgb;
  
  	};
  
  	LuminanceNode.prototype.toJSON = function ( meta ) {
  
  		var data = this.getJSONNode( meta );
  
  		if ( ! data ) {
  
  			data = this.createJSONNode( meta );
  
  			data.rgb = this.rgb.toJSON( meta ).uuid;
  
  		}
  
  		return data;
  
  	};
  
  	exports.LuminanceNode = LuminanceNode;
  
  	return exports;
  
  }({}));
  