Browse Source

nodematerial r6 (#9636)

* add standard define

* fix auto set transparent

* add indirectSpecular and fix premultiplied_alpha, tonemapping, encodings, fog

* fix premultiplied_alpha, tonemapping, encodings, fog

* fix spherical reflection

* fix spherical reflection

* nodematerial r6

* added triangle-blur and custom-attribute examples

* fix reflection using in color slot

* support to expression and reserved-keywords

* int <-> float support

* fix possible initial space in eval

* description how to add global const or function

* bump map support

* sampleCube and slotId

* gammaInput

* add bump and spherical-reflection

* reduces and optimize NodeLib

* blur example, revision and cleanup

* fix normal-map offset

* cleanup

* up comments

* fix cache texture

* fix cache
sunag 9 years ago
parent
commit
7d8278e032
46 changed files with 1366 additions and 571 deletions
  1. 38 0
      examples/js/nodes/AttributeNode.js
  2. 0 208
      examples/js/nodes/BuilderNode.js
  3. 43 17
      examples/js/nodes/ConstNode.js
  4. 6 8
      examples/js/nodes/FunctionCallNode.js
  5. 118 74
      examples/js/nodes/FunctionNode.js
  6. 24 18
      examples/js/nodes/GLNode.js
  7. 3 0
      examples/js/nodes/InputNode.js
  8. 258 0
      examples/js/nodes/NodeBuilder.js
  9. 120 40
      examples/js/nodes/NodeLib.js
  10. 73 38
      examples/js/nodes/NodeMaterial.js
  11. 1 2
      examples/js/nodes/RawNode.js
  12. 11 14
      examples/js/nodes/TempNode.js
  13. 16 3
      examples/js/nodes/accessors/CameraNode.js
  14. 2 2
      examples/js/nodes/accessors/ColorsNode.js
  15. 1 2
      examples/js/nodes/accessors/LightNode.js
  16. 2 2
      examples/js/nodes/accessors/NormalNode.js
  17. 2 2
      examples/js/nodes/accessors/PositionNode.js
  18. 3 3
      examples/js/nodes/accessors/ReflectNode.js
  19. 1 2
      examples/js/nodes/accessors/ScreenUVNode.js
  20. 1 1
      examples/js/nodes/accessors/UVNode.js
  21. 1 1
      examples/js/nodes/inputs/ColorNode.js
  22. 21 1
      examples/js/nodes/inputs/CubeTextureNode.js
  23. 1 1
      examples/js/nodes/inputs/FloatNode.js
  24. 1 1
      examples/js/nodes/inputs/IntNode.js
  25. 1 1
      examples/js/nodes/inputs/Matrix4Node.js
  26. 7 4
      examples/js/nodes/inputs/MirrorNode.js
  27. 21 1
      examples/js/nodes/inputs/TextureNode.js
  28. 1 1
      examples/js/nodes/inputs/Vector2Node.js
  29. 1 1
      examples/js/nodes/inputs/Vector3Node.js
  30. 1 1
      examples/js/nodes/inputs/Vector4Node.js
  31. 19 17
      examples/js/nodes/materials/PhongNode.js
  32. 21 17
      examples/js/nodes/materials/StandardNode.js
  33. 1 1
      examples/js/nodes/math/Math3Node.js
  34. 8 8
      examples/js/nodes/math/OperatorNode.js
  35. 114 0
      examples/js/nodes/utils/BlurNode.js
  36. 49 0
      examples/js/nodes/utils/BumpNode.js
  37. 3 3
      examples/js/nodes/utils/JoinNode.js
  38. 1 2
      examples/js/nodes/utils/NormalMapNode.js
  39. 1 1
      examples/js/nodes/utils/ResolutionNode.js
  40. 2 4
      examples/js/nodes/utils/RoughnessToBlinnExponentNode.js
  41. 1 1
      examples/js/nodes/utils/SwitchNode.js
  42. 1 1
      examples/js/nodes/utils/TimerNode.js
  43. 1 1
      examples/js/nodes/utils/VelocityNode.js
  44. 307 62
      examples/webgl_materials_nodes.html
  45. 22 2
      examples/webgl_mirror_nodes.html
  46. 36 2
      examples/webgl_postprocessing_nodes.html

+ 38 - 0
examples/js/nodes/AttributeNode.js

@@ -0,0 +1,38 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.AttributeNode = function( name, type ) {
+
+	THREE.InputNode.call( this, type, { shared: false } );
+
+	this.name = name;
+
+};
+
+THREE.AttributeNode.prototype = Object.create( THREE.InputNode.prototype );
+THREE.AttributeNode.prototype.constructor = THREE.AttributeNode;
+
+THREE.AttributeNode.prototype.getAttributeType = function( builder ) {
+
+	return typeof this.type === 'number' ? builder.getConstructorFromLength( this.type ) : this.type;
+
+};
+
+THREE.AttributeNode.prototype.getType = function( builder ) {
+
+	var type = this.getAttributeType( builder );
+
+	return builder.getTypeByFormat( type );
+
+};
+
+THREE.AttributeNode.prototype.generate = function( builder, output ) {
+
+	var type = this.getAttributeType( builder );
+
+	var attribute = builder.material.getAttribute( this.name, type );
+
+	return builder.format( attribute.varName, this.getType( builder ), output );
+
+};

+ 0 - 208
examples/js/nodes/BuilderNode.js

@@ -1,208 +0,0 @@
-/**
- * @author sunag / http://www.sunag.com.br/
- */
-
-THREE.BuilderNode = function( material ) {
-
-	this.material = material;
-
-	this.caches = [];
-
-	this.parsing = false;
-	this.optimize = true;
-
-	this.addCache();
-
-};
-
-THREE.BuilderNode.prototype = {
-	constructor: THREE.BuilderNode,
-
-	addCache : function( name, requires ) {
-
-		this.caches.push( {
-			name : name || '',
-			requires : requires || {}
-		} );
-
-		return this.updateCache();
-
-	},
-
-	removeCache : function() {
-
-		this.caches.pop();
-
-		return this.updateCache();
-
-	},
-
-	isCache : function( name ) {
-
-		var i = this.caches.length;
-
-		while ( i -- ) {
-
-			if ( this.caches[ i ].name == name ) return true;
-
-		}
-
-		return false;
-
-	},
-
-	updateCache : function() {
-
-		var cache = this.caches[ this.caches.length - 1 ];
-
-		this.cache = cache.name;
-		this.requires = cache.requires;
-
-		return this;
-
-	},
-
-	require : function( name, node ) {
-
-		this.requires[ name ] = node;
-
-		return this;
-
-	},
-
-	include : function( func ) {
-
-		this.material.include( this.shader, func );
-
-		return this;
-
-	},
-
-	colorToVector : function( color ) {
-
-		return color.replace( 'r', 'x' ).replace( 'g', 'y' ).replace( 'b', 'z' ).replace( 'a', 'w' );
-
-	},
-
-	getFormatConstructor : function( len ) {
-
-		return THREE.BuilderNode.constructors[ len - 1 ];
-
-	},
-
-	getFormatName : function( format ) {
-
-		return format.replace( /c/g, 'v3' ).replace( /fv1|iv1/g, 'v1' );
-
-	},
-
-	isFormatMatrix : function( format ) {
-
-		return /^m/.test( format );
-
-	},
-
-	getFormatLength : function( format ) {
-
-		return parseInt( this.getFormatName( format ).substr( 1 ) );
-
-	},
-
-	getFormatByLength : function( len ) {
-
-		if ( len == 1 ) return 'fv1';
-
-		return 'v' + len;
-
-	},
-
-	format : function( code, from, to ) {
-
-		var format = this.getFormatName( from + '=' + to );
-
-		switch ( format ) {
-			case 'v1=v2': return 'vec2(' + code + ')';
-			case 'v1=v3': return 'vec3(' + code + ')';
-			case 'v1=v4': return 'vec4(' + code + ')';
-
-			case 'v2=v1': return code + '.x';
-			case 'v2=v3': return 'vec3(' + code + ',0.0)';
-			case 'v2=v4': return 'vec4(' + code + ',0.0,1.0)';
-
-			case 'v3=v1': return code + '.x';
-			case 'v3=v2': return code + '.xy';
-			case 'v3=v4': return 'vec4(' + code + ',1.0)';
-
-			case 'v4=v1': return code + '.x';
-			case 'v4=v2': return code + '.xy';
-			case 'v4=v3': return code + '.xyz';
-		}
-
-		return code;
-
-	},
-
-	getTypeByFormat : function( format ) {
-
-		return THREE.BuilderNode.type[ format ];
-
-	},
-
-	getUuid : function( uuid, useCache ) {
-
-		useCache = useCache !== undefined ? useCache : true;
-
-		if ( useCache && this.cache ) uuid = this.cache + '-' + uuid;
-
-		return uuid;
-
-	},
-
-	getElementByIndex : function( index ) {
-
-		return THREE.BuilderNode.elements[ index ];
-
-	},
-
-	getIndexByElement : function( elm ) {
-
-		return THREE.BuilderNode.elements.indexOf( elm );
-
-	},
-
-	isShader : function( shader ) {
-
-		return this.shader == shader;
-
-	},
-
-	setShader : function( shader ) {
-
-		this.shader = shader;
-
-		return this;
-
-	}
-};
-
-THREE.BuilderNode.type = {
-	'float' : 'fv1',
-	vec2 : 'v2',
-	vec3 : 'v3',
-	vec4 : 'v4',
-	mat4 : 'v4'
-};
-
-THREE.BuilderNode.constructors = [
-	'',
-	'vec2',
-	'vec3',
-	'vec4'
-];
-
-THREE.BuilderNode.elements = [
-	'x',
-	'y',
-	'z',
-	'w'
-];

+ 43 - 17
examples/js/nodes/ConstNode.js

@@ -2,11 +2,11 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ConstNode = function( name, useDefine ) {
+THREE.ConstNode = function( src, useDefine ) {
 
 	THREE.TempNode.call( this );
 
-	this.parse( name || THREE.ConstNode.PI, useDefine );
+	this.eval( src || THREE.ConstNode.PI, useDefine );
 
 };
 
@@ -20,31 +20,30 @@ THREE.ConstNode.EPSILON = 'EPSILON';
 THREE.ConstNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.ConstNode.prototype.constructor = THREE.ConstNode;
 
-THREE.ConstNode.prototype.parse = function( src, useDefine ) {
+THREE.ConstNode.prototype.getType = function( builder ) {
 
-	var name, type;
+	return builder.getTypeByFormat( this.type );
 
-	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=(.*?)\;/i;
-	var match = src.match( rDeclaration );
+};
 
-	if ( match && match.length > 1 ) {
+THREE.ConstNode.prototype.eval = function( src, useDefine ) {
 
-		type = match[ 1 ];
-		name = match[ 2 ];
+	src = ( src || '' ).trim();
 
-		if ( useDefine ) {
+	var name, type, value;
 
-			this.src = '#define ' + name + ' ' + match[ 3 ];
+	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=?\s?(.*?)(\;|$)/i;
+	var match = src.match( rDeclaration );
 
-		}
-		else {
+	this.useDefine = useDefine;
 
-			this.src = 'const ' + type + ' ' + name + ' = ' + match[ 3 ] + ';';
+	if ( match && match.length > 1 ) {
 
-		}
+		type = match[ 1 ];
+		name = match[ 2 ];
+		value = match[ 3 ];
 
-	}
-	else {
+	} else {
 
 		name = src;
 		type = 'fv1';
@@ -53,6 +52,33 @@ THREE.ConstNode.prototype.parse = function( src, useDefine ) {
 
 	this.name = name;
 	this.type = type;
+	this.value = value;
+
+};
+
+THREE.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 {
+
+		builder.include( this );
+
+		return builder.format( this.name, this.getType( builder ), output );
+
+	}
 
 };
 

+ 6 - 8
examples/js/nodes/FunctionCallNode.js

@@ -2,21 +2,21 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.FunctionCallNode = function( value ) {
+THREE.FunctionCallNode = function( func, inputs ) {
 
 	THREE.TempNode.call( this );
 
-	this.setFunction( value );
+	this.setFunction( func, inputs );
 
 };
 
 THREE.FunctionCallNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.FunctionCallNode.prototype.constructor = THREE.FunctionCallNode;
 
-THREE.FunctionCallNode.prototype.setFunction = function( val ) {
+THREE.FunctionCallNode.prototype.setFunction = function( func, inputs ) {
 
-	this.inputs = [];
-	this.value = val;
+	this.value = func;
+	this.inputs = inputs || [];
 
 };
 
@@ -39,9 +39,7 @@ THREE.FunctionCallNode.prototype.generate = function( builder, output ) {
 	var type = this.getType( builder );
 	var func = this.value;
 
-	builder.include( func );
-
-	var code = func.name + '(';
+	var code = func.build( builder, output ) + '(';
 	var params = [];
 
 	for ( var i = 0; i < func.inputs.length; i ++ ) {

+ 118 - 74
examples/js/nodes/FunctionNode.js

@@ -3,37 +3,35 @@
  * @thanks bhouston / https://clara.io/
  */
 
-THREE.FunctionNode = function( src, includes, extensions ) {
+THREE.FunctionNode = function( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions ) {
 
-	THREE.GLNode.call( this );
+	src = src || '';
 
-	this.parse( src || '', includes, extensions );
+	this.isMethod = typeof includesOrType !== "string";
+	this.useKeywords = true;
+
+	THREE.TempNode.call( this, this.isMethod ? null : includesOrType );
+
+	if ( this.isMethod ) this.eval( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions );
+	else this.eval( src, extensionsOrIncludes, keywordsOrExtensions );
 
 };
 
-THREE.FunctionNode.prototype = Object.create( THREE.GLNode.prototype );
+THREE.FunctionNode.rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
+THREE.FunctionNode.rProperties = /[a-z_0-9]+/ig;
+
+THREE.FunctionNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.FunctionNode.prototype.constructor = THREE.FunctionNode;
 
-THREE.FunctionNode.prototype.parseReference = function( name ) {
-
-	switch ( name ) {
-		case 'uv': return new THREE.UVNode().name;
-		case 'uv2': return new THREE.UVNode( 1 ).name;
-		case 'position': return new THREE.PositionNode().name;
-		case 'worldPosition': return new THREE.PositionNode( THREE.PositionNode.WORLD ).name;
-		case 'normal': return new THREE.NormalNode().name;
-		case 'normalPosition': return new THREE.NormalNode( THREE.NormalNode.WORLD ).name;
-		case 'viewPosition': return new THREE.PositionNode( THREE.NormalNode.VIEW ).name;
-		case 'viewNormal': return new THREE.NormalNode( THREE.NormalNode.VIEW ).name;
-	}
+THREE.FunctionNode.prototype.isShared = function( builder, output ) {
 
-	return name;
+	return ! this.isMethod;
 
 };
 
-THREE.FunctionNode.prototype.getTypeNode = function( builder, type ) {
+THREE.FunctionNode.prototype.getType = function( builder ) {
 
-	return builder.getTypeByFormat( type ) || type;
+	return builder.getTypeByFormat( this.type );
 
 };
 
@@ -50,13 +48,7 @@ THREE.FunctionNode.prototype.getInputByName = function( name ) {
 
 };
 
-THREE.FunctionNode.prototype.getType = function( builder ) {
-
-	return this.getTypeNode( builder, this.type );
-
-};
-
-THREE.FunctionNode.prototype.getInclude = function( name ) {
+THREE.FunctionNode.prototype.getIncludeByName = function( name ) {
 
 	var i = this.includes.length;
 
@@ -67,93 +59,145 @@ THREE.FunctionNode.prototype.getInclude = function( name ) {
 
 	}
 
-	return undefined;
-
 };
 
-THREE.FunctionNode.prototype.parse = function( src, includes, extensions ) {
+THREE.FunctionNode.prototype.generate = function( builder, output ) {
 
-	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
-	var rProperties = /[a-z_0-9]+/ig;
+	var match, offset = 0, src = this.value;
 
-	this.includes = includes || [];
-	this.extensions = extensions || {};
+	while ( match = THREE.FunctionNode.rProperties.exec( this.value ) ) {
 
-	var match = src.match( rDeclaration );
+		var prop = match[ 0 ], isGlobal = this.isMethod ? ! this.getInputByName( prop ) : true;
+		var reference = prop;
 
-	this.inputs = [];
+		if ( this.keywords[ prop ] || ( this.useKeywords && isGlobal && THREE.NodeLib.containsKeyword( prop ) ) ) {
 
-	if ( match && match.length == 4 ) {
+			var node = this.keywords[ prop ];
 
-		this.type = match[ 1 ];
-		this.name = match[ 2 ];
+			if ( ! node ) {
 
-		var inputs = match[ 3 ].match( rProperties );
+				var keyword = THREE.NodeLib.getKeywordData( prop );
 
-		if ( inputs ) {
+				if ( keyword.cache ) node = builder.keywords[ prop ];
 
-			var i = 0;
+				node = node || THREE.NodeLib.getKeyword( prop, builder );
 
-			while ( i < inputs.length ) {
+				if ( keyword.cache ) builder.keywords[ prop ] = node;
 
-				var qualifier = inputs[ i ++ ];
-				var type, name;
+			}
 
-				if ( qualifier == 'in' || qualifier == 'out' || qualifier == 'inout' ) {
+			reference = node.build( builder );
 
-					type = inputs[ i ++ ];
+		}
 
-				}
-				else {
+		if ( prop != reference ) {
 
-					type = qualifier;
-					qualifier = '';
+			src = src.substring( 0, match.index + offset ) + reference + src.substring( match.index + prop.length + offset );
 
-				}
+			offset += reference.length - prop.length;
 
-				name = inputs[ i ++ ];
+		}
 
-				this.inputs.push( {
-					name : name,
-					type : type,
-					qualifier : qualifier
-				} );
+		if ( this.getIncludeByName( reference ) === undefined && THREE.NodeLib.contains( reference ) ) {
 
-			}
+			builder.include( THREE.NodeLib.get( reference ) );
 
 		}
 
-		var match, offset = 0;
+	}
 
-		while ( match = rProperties.exec( src ) ) {
+	if ( output === 'source' ) {
 
-			var prop = match[ 0 ];
-			var reference = this.parseReference( prop );
+		for ( var i = 0; i < this.includes.length; i ++ ) {
 
-			if ( prop != reference ) {
+			builder.include( this.includes[ i ], this );
 
-				src = src.substring( 0, match.index + offset ) + reference + src.substring( match.index + prop.length + offset );
+		}
 
-				offset += reference.length - prop.length;
+		for ( var ext in this.extensions ) {
 
-			}
+			builder.material.extensions[ ext ] = true;
 
-			if ( this.getInclude( reference ) === undefined && THREE.NodeLib.contains( reference ) ) {
+		}
 
-				this.includes.push( THREE.NodeLib.get( reference ) );
+		return src;
 
-			}
+	} else if ( this.isMethod ) {
 
-		}
+		builder.include( this, false, src );
+
+		return this.name;
+
+	} else {
 
-		this.src = src;
+		return builder.format( "(" + src + ")", this.getType( builder ), output );
 
 	}
-	else {
 
-		this.type = '';
-		this.name = '';
+};
+
+THREE.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( THREE.FunctionNode.rDeclaration );
+
+		this.inputs = [];
+
+		if ( match && match.length == 4 ) {
+
+			this.type = match[ 1 ];
+			this.name = match[ 2 ];
+
+			var inputs = match[ 3 ].match( THREE.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;
+
 };

+ 24 - 18
examples/js/nodes/GLNode.js

@@ -6,48 +6,54 @@ THREE.GLNode = function( type ) {
 
 	this.uuid = THREE.Math.generateUUID();
 
-	this.allow = {};
+	this.allows = {};
 	this.requestUpdate = false;
 
 	this.type = type;
 
 };
 
-THREE.GLNode.prototype.parse = function( builder, cache, requires ) {
+THREE.GLNode.prototype.parse = function( builder, context ) {
+
+	context = context || {};
 
 	builder.parsing = true;
 
 	var material = builder.material;
 
-	this.build( builder.addCache( cache, requires ), 'v4' );
+	this.build( builder.addCache( context.cache, context.requires ).addSlot( context.slot ), 'v4' );
 
 	material.clearVertexNode();
 	material.clearFragmentNode();
 
-	builder.removeCache();
+	builder.removeCache().removeSlot();
 
 	builder.parsing = false;
 
 };
 
-THREE.GLNode.prototype.parseAndBuildCode = function( builder, output, cache, requires ) {
+THREE.GLNode.prototype.parseAndBuildCode = function( builder, output, context ) {
+
+	context = context || {};
 
-	this.parse( builder, cache, requires );
+	this.parse( builder, context );
 
-	return this.buildCode( builder, output, cache, requires );
+	return this.buildCode( builder, output, context );
 
 };
 
-THREE.GLNode.prototype.buildCode = function( builder, output, cache, requires ) {
+THREE.GLNode.prototype.buildCode = function( builder, output, context ) {
+
+	context = context || {};
 
 	var material = builder.material;
 
-	var data = { result : this.build( builder.addCache( cache, requires ), output ) };
+	var data = { result : this.build( builder.addCache( context.cache, context.requires ).addSlot( context.slot ), output ) };
 
 	if ( builder.isShader( 'vertex' ) ) data.code = material.clearVertexNode();
 	else data.code = material.clearFragmentNode();
 
-	builder.removeCache();
+	builder.removeCache().removeSlot();
 
 	return data;
 
@@ -55,21 +61,21 @@ THREE.GLNode.prototype.buildCode = function( builder, output, cache, requires )
 
 THREE.GLNode.prototype.build = function( builder, output, uuid ) {
 
-	var material = builder.material;
-	var data = material.getDataNode( uuid || this.uuid );
+	output = output || this.getType( builder, output );
+
+	var material = builder.material, data = material.getDataNode( uuid || this.uuid );
 
 	if ( builder.parsing ) this.appendDepsNode( builder, data, output );
 
-	if ( this.allow[ builder.shader ] === false ) {
+	if ( this.allows[ builder.shader ] === false ) {
 
 		throw new Error( 'Shader ' + shader + ' is not compatible with this node.' );
 
 	}
 
-	if ( this.requestUpdate && ! data.requestUpdate ) {
+	if ( this.requestUpdate && material.requestUpdate.indexOf( this ) === - 1 ) {
 
 		material.requestUpdate.push( this );
-		data.requestUpdate = true;
 
 	}
 
@@ -83,7 +89,7 @@ THREE.GLNode.prototype.appendDepsNode = function( builder, data, output ) {
 
 	var outputLen = builder.getFormatLength( output );
 
-	if ( outputLen > ( data.outputMax || 0 ) || this.getType( builder ) ) {
+	if ( outputLen > ( data.outputMax || 0 ) || this.getType( builder, output ) ) {
 
 		data.outputMax = outputLen;
 		data.output = output;
@@ -92,8 +98,8 @@ THREE.GLNode.prototype.appendDepsNode = function( builder, data, output ) {
 
 };
 
-THREE.GLNode.prototype.getType = function( builder ) {
+THREE.GLNode.prototype.getType = function( builder, output ) {
 
-	return this.type;
+	return output === 'sampler2D' || output === 'samplerCube' ? output : this.type;
 
 };

+ 3 - 0
examples/js/nodes/InputNode.js

@@ -4,6 +4,9 @@
 
 THREE.InputNode = function( type, params ) {
 
+	params = params || {};
+	params.shared = params.shared !== undefined ? params.shared : false;
+
 	THREE.TempNode.call( this, type, params );
 
 };

+ 258 - 0
examples/js/nodes/NodeBuilder.js

@@ -0,0 +1,258 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeBuilder = function( material ) {
+
+	this.material = material;
+
+	this.caches = [];
+	this.slots = [];
+
+	this.keywords = {};
+
+	this.parsing = false;
+	this.optimize = true;
+
+	this.update();
+
+};
+
+THREE.NodeBuilder.type = {
+	float : 'fv1',
+	vec2 : 'v2',
+	vec3 : 'v3',
+	vec4 : 'v4',
+	mat4 : 'v4',
+	int : 'iv1'
+};
+
+THREE.NodeBuilder.constructors = [
+	'float',
+	'vec2',
+	'vec3',
+	'vec4'
+];
+
+THREE.NodeBuilder.elements = [
+	'x',
+	'y',
+	'z',
+	'w'
+];
+
+THREE.NodeBuilder.prototype = {
+
+	constructor: THREE.NodeBuilder,
+
+	addCache : function( name, requires ) {
+
+		this.caches.push( {
+			name : name || '',
+			requires : requires || {}
+		} );
+
+		return this.update();
+
+	},
+
+	removeCache : function() {
+
+		this.caches.pop();
+
+		return this.update();
+
+	},
+
+	addSlot : function( name ) {
+
+		this.slots.push( {
+			name : name || '',
+		} );
+
+		return this.update();
+
+	},
+
+	removeSlot : function() {
+
+		this.slots.pop();
+
+		return this.update();
+
+	},
+
+	isCache : function( name ) {
+
+		var i = this.caches.length;
+
+		while ( i -- ) {
+
+			if ( this.caches[ i ].name == name ) return true;
+
+		}
+
+		return false;
+
+	},
+
+	isSlot : function( name ) {
+
+		var i = this.slots.length;
+
+		while ( i -- ) {
+
+			if ( this.slots[ i ].name == name ) return true;
+
+		}
+
+		return false;
+
+	},
+
+	update : function() {
+
+		var cache = this.caches[ this.caches.length - 1 ];
+		var slot = this.slots[ this.slots.length - 1 ];
+
+		this.slot = slot ? slot.name : '';
+		this.cache = cache ? cache.name : '';
+		this.requires = cache ? cache.requires : {};
+
+		return this;
+
+	},
+
+	require : function( name, node ) {
+
+		this.requires[ name ] = node;
+
+		return this;
+
+	},
+
+	include : function( node, parent, source ) {
+
+		this.material.include( this, node, parent, source );
+
+		return this;
+
+	},
+
+	colorToVector : function( color ) {
+
+		return color.replace( 'r', 'x' ).replace( 'g', 'y' ).replace( 'b', 'z' ).replace( 'a', 'w' );
+
+	},
+
+	getConstructorFromLength : function( len ) {
+
+		return THREE.NodeBuilder.constructors[ len - 1 ];
+
+	},
+
+	getFormatName : function( format ) {
+
+		return format.replace( /c/g, 'v3' ).replace( /fv1/g, 'v1' ).replace( /iv1/g, 'i' );
+
+	},
+
+	isFormatMatrix : function( format ) {
+
+		return /^m/.test( format );
+
+	},
+
+	getFormatLength : function( format ) {
+
+		return parseInt( this.getFormatName( format ).substr( 1 ) );
+
+	},
+
+	getFormatFromLength : function( len ) {
+
+		if ( len == 1 ) return 'fv1';
+
+		return 'v' + len;
+
+	},
+
+	format : function( code, from, to ) {
+
+		var format = this.getFormatName( to + '=' + from );
+
+		switch ( format ) {
+
+			case 'v1=v2': return code + '.x';
+			case 'v1=v3': return code + '.x';
+			case 'v1=v4': return code + '.x';
+			case 'v1=i': return 'float(' + code + ')';
+
+			case 'v2=v1': return 'vec2(' + code + ')';
+			case 'v2=v3': return code + '.xy';
+			case 'v2=v4': return code + '.xy';
+			case 'v2=i': return 'vec2(float(' + code + '))';
+
+			case 'v3=v1': return 'vec3(' + code + ')';
+			case 'v3=v2': return 'vec3(' + code + ',0.0)';
+			case 'v3=v4': return code + '.xyz';
+			case 'v3=i': return 'vec2(float(' + code + '))';
+
+			case 'v4=v1': return 'vec4(' + code + ')';
+			case 'v4=v2': return 'vec4(' + code + ',0.0,1.0)';
+			case 'v4=v3': return 'vec4(' + code + ',1.0)';
+			case 'v4=i': return 'vec4(float(' + code + '))';
+
+			case 'i=v1': return 'int(' + code + ')';
+			case 'i=v2': return 'int(' + code + '.x)';
+			case 'i=v3': return 'int(' + code + '.x)';
+			case 'i=v4': return 'int(' + code + '.x)';
+
+		}
+
+		return code;
+
+	},
+
+	getTypeByFormat : function( format ) {
+
+		return THREE.NodeBuilder.type[ format ] || format;
+
+	},
+
+	getUuid : function( uuid, useCache ) {
+
+		useCache = useCache !== undefined ? useCache : true;
+
+		if ( useCache && this.cache ) uuid = this.cache + '-' + uuid;
+
+		return uuid;
+
+	},
+
+	getElementByIndex : function( index ) {
+
+		return THREE.NodeBuilder.elements[ index ];
+
+	},
+
+	getIndexByElement : function( elm ) {
+
+		return THREE.NodeBuilder.elements.indexOf( elm );
+
+	},
+
+	isShader : function( shader ) {
+
+		return this.shader == shader;
+
+	},
+
+	setShader : function( shader ) {
+
+		this.shader = shader;
+
+		return this;
+
+	}
+};

+ 120 - 40
examples/js/nodes/NodeLib.js

@@ -3,51 +3,131 @@
  */
 
 THREE.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[ node ];
+
+	},
+
 	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;
+
 	}
+
 };
 
 //
-//	Luma
+//	Keywords
 //
 
-THREE.NodeLib.add( new THREE.ConstNode( "vec3 LUMA = vec3(0.2125, 0.7154, 0.0721);" ) );
+THREE.NodeLib.addKeyword( 'uv', function() {
 
-//
-//	DepthColor
-//
+	return new THREE.UVNode();
 
-THREE.NodeLib.add( new THREE.FunctionNode( [
-"float depthcolor( float mNear, float mFar ) {",
+} );
 
-	"#ifdef USE_LOGDEPTHBUF_EXT",
-		"float depth = gl_FragDepthEXT / gl_FragCoord.w;",
-	"#else",
-		"float depth = gl_FragCoord.z / gl_FragCoord.w;",
-	"#endif",
+THREE.NodeLib.addKeyword( 'uv2', function() {
 
-	"return 1.0 - smoothstep( mNear, mFar, depth );",
-"}"
-].join( "\n" ) ) );
+	return new THREE.UVNode( 1 );
+
+} );
+
+THREE.NodeLib.addKeyword( 'position', function() {
+
+	return new THREE.PositionNode();
+
+} );
+
+THREE.NodeLib.addKeyword( 'worldPosition', function() {
+
+	return new THREE.PositionNode( THREE.PositionNode.WORLD );
+
+} );
+
+THREE.NodeLib.addKeyword( 'normal', function() {
+
+	return new THREE.NormalNode();
+
+} );
+
+THREE.NodeLib.addKeyword( 'worldNormal', function() {
+
+	return new THREE.NormalNode( THREE.NormalNode.WORLD );
+
+} );
+
+THREE.NodeLib.addKeyword( 'viewPosition', function() {
+
+	return new THREE.PositionNode( THREE.NormalNode.VIEW );
+
+} );
+
+THREE.NodeLib.addKeyword( 'viewNormal', function() {
+
+	return new THREE.NormalNode( THREE.NormalNode.VIEW );
+
+} );
+
+THREE.NodeLib.addKeyword( 'time', function() {
+
+	return new THREE.TimerNode();
+
+} );
+
+//
+//	Luma
+//
+
+THREE.NodeLib.add( new THREE.ConstNode( "vec3 LUMA vec3(0.2125, 0.7154, 0.0721)" ) );
 
 //
 //	NormalMap
@@ -57,17 +137,17 @@ THREE.NodeLib.add( new THREE.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 );",
+"	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 } ) );
 
@@ -77,7 +157,7 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
 
 THREE.NodeLib.add( new THREE.FunctionNode( [
 "float snoise(vec2 co) {",
-	"return fract( sin( dot(co.xy, vec2(12.9898,78.233) ) ) * 43758.5453 );",
+"	return fract( sin( dot(co.xy, vec2(12.9898,78.233) ) ) * 43758.5453 );",
 "}"
 ].join( "\n" ) ) );
 
@@ -87,12 +167,12 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
 
 THREE.NodeLib.add( new THREE.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));",
+"	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" ) ) );
 
@@ -103,8 +183,8 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
 THREE.NodeLib.add( new THREE.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);",
+"	vec3 intensity = vec3(dot(rgb, LUMA));",
+"	return mix(intensity, rgb, adjustment);",
 "}"
 ].join( "\n" ) ) );
 
@@ -115,7 +195,7 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
 THREE.NodeLib.add( new THREE.FunctionNode( [
 // Algorithm from Chapter 10 of Graphics Shaders
 "float luminance_rgb(vec3 rgb) {",
-	"return dot(rgb, LUMA);",
+"	return dot(rgb, LUMA);",
 "}"
 ].join( "\n" ) ) );
 
@@ -126,9 +206,9 @@ THREE.NodeLib.add( new THREE.FunctionNode( [
 THREE.NodeLib.add( new THREE.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);",
+"	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" ) ) );

+ 73 - 38
examples/js/nodes/NodeMaterial.js

@@ -64,11 +64,11 @@ THREE.NodeMaterial.addShortcuts = function( proto, prop, list ) {
 THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
 THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
 
-THREE.NodeMaterial.prototype.updateAnimation = function( delta ) {
+THREE.NodeMaterial.prototype.updateFrame = function( delta ) {
 
 	for ( var i = 0; i < this.requestUpdate.length; ++ i ) {
 
-		this.requestUpdate[ i ].updateAnimation( delta );
+		this.requestUpdate[ i ].updateFrame( delta );
 
 	}
 
@@ -80,6 +80,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	this.defines = {};
 	this.uniforms = {};
+	this.attributes = {};
 
 	this.nodeData = {};
 
@@ -96,7 +97,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	this.requestUpdate = [];
 
-	this.requestAttrib = {
+	this.requestAttribs = {
 		uv: [],
 		color: []
 	};
@@ -130,12 +131,12 @@ THREE.NodeMaterial.prototype.build = function() {
 	"#endif"
 	].join( "\n" );
 
-	var builder = new THREE.BuilderNode( this );
+	var builder = new THREE.NodeBuilder( this );
 
 	vertex = this.vertex.build( builder.setShader( 'vertex' ), 'v4' );
 	fragment = this.fragment.build( builder.setShader( 'fragment' ), 'v4' );
 
-	if ( this.requestAttrib.uv[ 0 ] ) {
+	if ( this.requestAttribs.uv[ 0 ] ) {
 
 		this.addVertexPars( 'varying vec2 vUv;' );
 		this.addFragmentPars( 'varying vec2 vUv;' );
@@ -144,7 +145,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttrib.uv[ 1 ] ) {
+	if ( this.requestAttribs.uv[ 1 ] ) {
 
 		this.addVertexPars( 'varying vec2 vUv2; attribute vec2 uv2;' );
 		this.addFragmentPars( 'varying vec2 vUv2;' );
@@ -153,7 +154,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttrib.color[ 0 ] ) {
+	if ( this.requestAttribs.color[ 0 ] ) {
 
 		this.addVertexPars( 'varying vec4 vColor; attribute vec4 color;' );
 		this.addFragmentPars( 'varying vec4 vColor;' );
@@ -162,7 +163,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttrib.color[ 1 ] ) {
+	if ( this.requestAttribs.color[ 1 ] ) {
 
 		this.addVertexPars( 'varying vec4 vColor2; attribute vec4 color2;' );
 		this.addFragmentPars( 'varying vec4 vColor2;' );
@@ -171,7 +172,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttrib.position ) {
+	if ( this.requestAttribs.position ) {
 
 		this.addVertexPars( 'varying vec3 vPosition;' );
 		this.addFragmentPars( 'varying vec3 vPosition;' );
@@ -180,7 +181,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttrib.worldPosition ) {
+	if ( this.requestAttribs.worldPosition ) {
 
 		// for future update replace from the native "varying vec3 vWorldPosition" for optimization
 
@@ -191,7 +192,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttrib.normal ) {
+	if ( this.requestAttribs.normal ) {
 
 		this.addVertexPars( 'varying vec3 vObjectNormal;' );
 		this.addFragmentPars( 'varying vec3 vObjectNormal;' );
@@ -200,7 +201,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttrib.worldNormal ) {
+	if ( this.requestAttribs.worldNormal ) {
 
 		this.addVertexPars( 'varying vec3 vWNormal;' );
 		this.addFragmentPars( 'varying vec3 vWNormal;' );
@@ -209,8 +210,8 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	this.lights = this.requestAttrib.light;
-	this.transparent = this.requestAttrib.transparent || (this.blendMode !== THREE.NormalBlending && this.blendMode !== THREE.NoBlending);
+	this.lights = this.requestAttribs.light;
+	this.transparent = this.requestAttribs.transparent || this.blendMode > THREE.NormalBlending;
 
 	this.vertexShader = [
 		this.prefixCode,
@@ -318,11 +319,32 @@ THREE.NodeMaterial.prototype.getFragmentTemp = function( uuid, type, ns ) {
 
 };
 
-THREE.NodeMaterial.prototype.getIncludes = function( incs ) {
+THREE.NodeMaterial.prototype.getAttribute = function( name, type ) {
+
+	if ( ! this.attributes[ name ] ) {
+
+		var varName = 'nV' + name;
+
+		this.addVertexPars( 'varying ' + type + ' ' + varName + ';' );
+		this.addFragmentPars( 'varying ' + type + ' ' + varName + ';' );
+
+		this.addVertexPars( 'attribute ' + type + ' ' + name + ';' );
+
+		this.addVertexCode( varName + ' = ' + name + ';' );
+
+		this.attributes[ name ] = { varName : varName, name : name, type : type };
+
+	}
+
+	return this.attributes[ name ];
+
+};
+
+THREE.NodeMaterial.prototype.getIncludes = function() {
 
 	function sortByPosition( a, b ) {
 
-		return b.deps - a.deps;
+		return a.deps.length - b.deps.length;
 
 	}
 
@@ -330,12 +352,11 @@ THREE.NodeMaterial.prototype.getIncludes = function( incs ) {
 
 		if ( ! incs ) return '';
 
-		var code = '';
-		var incs = incs.sort( sortByPosition );
+		var code = '', incs = incs.sort( sortByPosition );
 
 		for ( var i = 0; i < incs.length; i ++ ) {
 
-			code += incs[ i ].node.src + '\n';
+			if ( incs[ i ].src ) code += incs[ i ].src + '\n';
 
 		}
 
@@ -377,9 +398,9 @@ THREE.NodeMaterial.prototype.addVertexNode = function( code ) {
 
 THREE.NodeMaterial.prototype.clearVertexNode = function() {
 
-	var code = this.fragmentNode;
+	var code = this.vertexNode;
 
-	this.fragmentNode = '';
+	this.vertexNode = '';
 
 	return code;
 
@@ -459,7 +480,7 @@ THREE.NodeMaterial.prototype.getDataNode = function( uuid ) {
 
 };
 
-THREE.NodeMaterial.prototype.include = function( shader, node ) {
+THREE.NodeMaterial.prototype.include = function( builder, node, parent, source ) {
 
 	var includes;
 
@@ -467,37 +488,51 @@ THREE.NodeMaterial.prototype.include = function( shader, node ) {
 
 	if ( node instanceof THREE.FunctionNode ) {
 
-		for ( var i = 0; i < node.includes.length; i ++ ) {
+		includes = this.functions[ builder.shader ] = this.functions[ builder.shader ] || [];
 
-			this.include( shader, node.includes[ i ] );
+	} else if ( node instanceof THREE.ConstNode ) {
 
-		}
-
-		includes = this.functions[ shader ] = this.functions[ shader ] || [];
+		includes = this.consts[ builder.shader ] = this.consts[ builder.shader ] || [];
 
 	}
-	else if ( node instanceof THREE.ConstNode ) {
 
-		includes = this.consts[ shader ] = this.consts[ shader ] || [];
+	var included = includes[ node.name ];
+
+	if ( ! included ) {
+
+		included = includes[ node.name ] = {
+			node : node,
+			deps : []
+		};
+
+		includes.push( included );
+
+		included.src = node.build( builder, 'source' );
 
 	}
 
-	if ( includes[ node.name ] === undefined ) {
+	if ( node instanceof THREE.FunctionNode && parent && includes[ parent.name ].deps.indexOf( node ) == - 1 ) {
+
+		includes[ parent.name ].deps.push( node );
 
-		for ( var ext in node.extensions ) {
+		if ( node.includes && node.includes.length ) {
 
-			this.extensions[ ext ] = true;
+			var i = 0;
+
+			do {
+
+				this.include( builder, node.includes[ i ++ ], parent );
+
+			} while ( i < node.includes.length );
 
 		}
 
-		includes[ node.name ] = {
-			node : node,
-			deps : 1
-		};
+	}
+
+	if ( source ) {
 
-		includes.push( includes[ node.name ] );
+		included.src = source;
 
 	}
-	else ++ includes[ node.name ].deps;
 
 };

+ 1 - 2
examples/js/nodes/RawNode.js

@@ -25,8 +25,7 @@ THREE.GLNode.prototype.generate = function( builder ) {
 
 		code += 'gl_Position = ' + data.result + ';';
 
-	}
-	else {
+	} else {
 
 		code += 'gl_FragColor = ' + data.result + ';';
 

+ 11 - 14
examples/js/nodes/TempNode.js

@@ -19,11 +19,13 @@ THREE.TempNode.prototype.constructor = THREE.TempNode;
 
 THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
 
+	output = output || this.getType( builder );
+
 	var material = builder.material;
 
-	if ( this.isShared() ) {
+	if ( this.isShared( builder, output ) ) {
 
-		var isUnique = this.isUnique();
+		var isUnique = this.isUnique( builder, output );
 
 		if ( isUnique && this.constructor.uuid === undefined ) {
 
@@ -47,8 +49,7 @@ THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
 
 			return THREE.GLNode.prototype.build.call( this, builder, output, uuid );
 
-		}
-		else if ( ! builder.optimize || data.deps == 1 ) {
+		} else if ( ! builder.optimize || data.deps == 1 ) {
 
 			return THREE.GLNode.prototype.build.call( this, builder, output, uuid );
 
@@ -63,8 +64,7 @@ THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
 
 			return builder.format( name, type, output );
 
-		}
-		else {
+		} else {
 
 			name = THREE.TempNode.prototype.generate.call( this, builder, output, uuid, data.output, ns );
 
@@ -78,21 +78,18 @@ THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
 		}
 
 	}
-	else {
 
-		return builder.format( this.generate( builder, this.getType( builder ), uuid ), this.getType( builder ), output );
-
-	}
+	return THREE.GLNode.prototype.build.call( this, builder, output, uuid );
 
 };
 
-THREE.TempNode.prototype.isShared = function() {
+THREE.TempNode.prototype.isShared = function( builder, output ) {
 
-	return this.shared;
+	return output !== 'sampler2D' && output !== 'samplerCube' && this.shared;
 
 };
 
-THREE.TempNode.prototype.isUnique = function() {
+THREE.TempNode.prototype.isUnique = function( builder, output ) {
 
 	return this.unique;
 
@@ -121,7 +118,7 @@ THREE.TempNode.prototype.getTemp = function( builder, uuid ) {
 
 THREE.TempNode.prototype.generate = function( builder, output, uuid, type, ns ) {
 
-	if ( ! this.isShared() ) console.error( "THREE.TempNode is not shared!" );
+	if ( ! this.isShared( builder, output ) ) console.error( "THREE.TempNode is not shared!" );
 
 	uuid = uuid || this.uuid;
 

+ 16 - 3
examples/js/nodes/accessors/CameraNode.js

@@ -11,6 +11,17 @@ THREE.CameraNode = function( scope, camera ) {
 
 };
 
+THREE.CameraNode.fDepthColor = new THREE.FunctionNode( [
+"float depthColor( float mNear, float mFar ) {",
+"	#ifdef USE_LOGDEPTHBUF_EXT",
+"		float depth = gl_FragDepthEXT / gl_FragCoord.w;",
+"	#else",
+"		float depth = gl_FragCoord.z / gl_FragCoord.w;",
+"	#endif",
+"	return 1.0 - smoothstep( mNear, mFar, depth );",
+"}"
+].join( "\n" ) );
+
 THREE.CameraNode.POSITION = 'position';
 THREE.CameraNode.DEPTH = 'depth';
 THREE.CameraNode.TO_VERTEX = 'toVertex';
@@ -102,9 +113,11 @@ THREE.CameraNode.prototype.generate = function( builder, output ) {
 
 		case THREE.CameraNode.DEPTH:
 
-			builder.include( 'depthcolor' );
+			var func = THREE.CameraNode.fDepthColor;
+
+			builder.include( func );
 
-			result = 'depthcolor(' + this.near.build( builder, 'fv1' ) + ',' + this.far.build( builder, 'fv1' ) + ')';
+			result = func.name + '(' + this.near.build( builder, 'fv1' ) + ',' + this.far.build( builder, 'fv1' ) + ')';
 
 			break;
 
@@ -120,7 +133,7 @@ THREE.CameraNode.prototype.generate = function( builder, output ) {
 
 };
 
-THREE.CameraNode.prototype.updateAnimation = function( delta ) {
+THREE.CameraNode.prototype.updateFrame = function( delta ) {
 
 	switch ( this.scope ) {
 

+ 2 - 2
examples/js/nodes/accessors/ColorsNode.js

@@ -4,7 +4,7 @@
 
 THREE.ColorsNode = function( index ) {
 
-	THREE.TempNode.call( this, 'v4', { share: false } );
+	THREE.TempNode.call( this, 'v4', { shared: false } );
 
 	this.index = index || 0;
 
@@ -21,7 +21,7 @@ THREE.ColorsNode.prototype.generate = function( builder, output ) {
 	var material = builder.material;
 	var result;
 
-	material.requestAttrib.color[ this.index ] = true;
+	material.requestAttribs.color[ this.index ] = true;
 
 	if ( builder.isShader( 'vertex' ) ) result = THREE.ColorsNode.vertexDict[ this.index ];
 	else result = THREE.ColorsNode.fragmentDict[ this.index ];

+ 1 - 2
examples/js/nodes/accessors/LightNode.js

@@ -17,8 +17,7 @@ THREE.LightNode.prototype.generate = function( builder, output ) {
 
 		return builder.format( 'reflectedLight.directDiffuse', this.getType( builder ), output )
 
-	}
-	else {
+	} else {
 
 		console.warn( "THREE.LightNode is only compatible in \"light\" channel." );
 

+ 2 - 2
examples/js/nodes/accessors/NormalNode.js

@@ -37,7 +37,7 @@ THREE.NormalNode.prototype.generate = function( builder, output ) {
 
 		case THREE.NormalNode.LOCAL:
 
-			material.requestAttrib.normal = true;
+			material.requestAttribs.normal = true;
 
 			if ( builder.isShader( 'vertex' ) ) result = 'normal';
 			else result = 'vObjectNormal';
@@ -46,7 +46,7 @@ THREE.NormalNode.prototype.generate = function( builder, output ) {
 
 		case THREE.NormalNode.WORLD:
 
-			material.requestAttrib.worldNormal = true;
+			material.requestAttribs.worldNormal = true;
 
 			if ( builder.isShader( 'vertex' ) ) result = '( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz';
 			else result = 'vWNormal';

+ 2 - 2
examples/js/nodes/accessors/PositionNode.js

@@ -50,7 +50,7 @@ THREE.PositionNode.prototype.generate = function( builder, output ) {
 
 		case THREE.PositionNode.LOCAL:
 
-			material.requestAttrib.position = true;
+			material.requestAttribs.position = true;
 
 			if ( builder.isShader( 'vertex' ) ) result = 'transformed';
 			else result = 'vPosition';
@@ -59,7 +59,7 @@ THREE.PositionNode.prototype.generate = function( builder, output ) {
 
 		case THREE.PositionNode.WORLD:
 
-			material.requestAttrib.worldPosition = true;
+			material.requestAttribs.worldPosition = true;
 
 			if ( builder.isShader( 'vertex' ) ) result = 'vWPosition';
 			else result = 'vWPosition';

+ 3 - 3
examples/js/nodes/accessors/ReflectNode.js

@@ -20,7 +20,7 @@ THREE.ReflectNode.prototype.constructor = THREE.ReflectNode;
 THREE.ReflectNode.prototype.getType = function( builder ) {
 
 	switch ( this.scope ) {
-		case THREE.CameraNode.SPHERE:
+		case THREE.ReflectNode.SPHERE:
 			return 'v2';
 	}
 
@@ -36,7 +36,7 @@ THREE.ReflectNode.prototype.generate = function( builder, output ) {
 
 		case THREE.ReflectNode.VECTOR:
 
-			builder.material.addFragmentNode( 'vec3 reflectVec = inverseTransformDirection( reflect( -geometry.viewDir, geometry.normal ), viewMatrix );' );
+			builder.material.addFragmentNode( 'vec3 reflectVec = inverseTransformDirection( reflect( -normalize( vViewPosition ), normal ), viewMatrix );' );
 
 			result = 'reflectVec';
 
@@ -56,7 +56,7 @@ THREE.ReflectNode.prototype.generate = function( builder, output ) {
 
 			var reflectVec = new THREE.ReflectNode( THREE.ReflectNode.VECTOR ).build( builder, 'v3' );
 
-			builder.material.addFragmentNode( 'vec3 reflectSphereVec = normalize((viewMatrix * vec4(' + reflectVec + ', 0.0 )).xyz + vec3(0.0,0.0,1.0)).xy * 0.5 + 0.5;' );
+			builder.material.addFragmentNode( 'vec2 reflectSphereVec = normalize((viewMatrix * vec4(' + reflectVec + ', 0.0 )).xyz + vec3(0.0,0.0,1.0)).xy * 0.5 + 0.5;' );
 
 			result = 'reflectSphereVec';
 

+ 1 - 2
examples/js/nodes/accessors/ScreenUVNode.js

@@ -22,8 +22,7 @@ THREE.ScreenUVNode.prototype.generate = function( builder, output ) {
 
 		result = '(gl_FragCoord.xy/' + this.resolution.build( builder, 'v2' ) + ')';
 
-	}
-	else {
+	} else {
 
 		console.warn( "THREE.ScreenUVNode is not compatible with " + builder.shader + " shader." );
 

+ 1 - 1
examples/js/nodes/accessors/UVNode.js

@@ -21,7 +21,7 @@ THREE.UVNode.prototype.generate = function( builder, output ) {
 	var material = builder.material;
 	var result;
 
-	material.requestAttrib.uv[ this.index ] = true;
+	material.requestAttribs.uv[ this.index ] = true;
 
 	if ( builder.isShader( 'vertex' ) ) result = THREE.UVNode.vertexDict[ this.index ];
 	else result = THREE.UVNode.fragmentDict[ this.index ];

+ 1 - 1
examples/js/nodes/inputs/ColorNode.js

@@ -4,7 +4,7 @@
 
 THREE.ColorNode = function( color ) {
 
-	THREE.InputNode.call( this, 'c', { share: false } );
+	THREE.InputNode.call( this, 'c', { shared: false } );
 
 	this.value = new THREE.Color( color || 0 );
 

+ 21 - 1
examples/js/nodes/inputs/CubeTextureNode.js

@@ -4,7 +4,7 @@
 
 THREE.CubeTextureNode = function( value, coord, bias ) {
 
-	THREE.InputNode.call( this, 'v4' );
+	THREE.InputNode.call( this, 'v4', { shared : true } );
 
 	this.value = value;
 	this.coord = coord || new THREE.ReflectNode();
@@ -23,6 +23,12 @@ THREE.CubeTextureNode.prototype.getTexture = function( builder, output ) {
 
 THREE.CubeTextureNode.prototype.generate = function( builder, output ) {
 
+	if ( output === 'samplerCube' ) {
+
+		return this.getTexture( builder, output );
+
+	}
+
 	var cubetex = this.getTexture( builder, output );
 	var coord = this.coord.build( builder, 'v3' );
 	var bias = this.bias ? this.bias.build( builder, 'fv1' ) : undefined;
@@ -38,6 +44,20 @@ THREE.CubeTextureNode.prototype.generate = function( builder, output ) {
 	if ( bias ) code = 'texCubeBias(' + cubetex + ',' + coord + ',' + bias + ')';
 	else code = 'texCube(' + cubetex + ',' + coord + ')';
 
+	if ( builder.isSlot( 'color' ) ) {
+
+		code = 'mapTexelToLinear(' + code + ')';
+
+	} else if ( builder.isSlot( 'emissive' ) ) {
+
+		code = 'emissiveMapTexelToLinear(' + code + ')';
+
+	} else if ( builder.isSlot( 'environment' ) ) {
+
+		code = 'envMapTexelToLinear(' + code + ')';
+
+	}
+
 	return builder.format( code, this.type, output );
 
 };

+ 1 - 1
examples/js/nodes/inputs/FloatNode.js

@@ -4,7 +4,7 @@
 
 THREE.FloatNode = function( value ) {
 
-	THREE.InputNode.call( this, 'fv1', { share: false } );
+	THREE.InputNode.call( this, 'fv1', { shared: false } );
 
 	this.value = [ value || 0 ];
 

+ 1 - 1
examples/js/nodes/inputs/IntNode.js

@@ -4,7 +4,7 @@
 
 THREE.IntNode = function( value ) {
 
-	THREE.InputNode.call( this, 'fv1', { share: false } );
+	THREE.InputNode.call( this, 'iv1', { shared: false } );
 
 	this.value = [ Math.floor( value || 0 ) ];
 

+ 1 - 1
examples/js/nodes/inputs/Matrix4Node.js

@@ -4,7 +4,7 @@
 
 THREE.Matrix4Node = function( matrix ) {
 
-	THREE.InputNode.call( this, 'm4', { share: false } );
+	THREE.InputNode.call( this, 'm4', { shared: false } );
 
 	this.value = matrix || new THREE.Matrix4();
 

+ 7 - 4
examples/js/nodes/inputs/MirrorNode.js

@@ -27,12 +27,15 @@ THREE.MirrorNode.prototype.generate = function( builder, output ) {
 		this.coordResult.a = this.offset;
 		this.texture.coord = this.offset ? this.coordResult : this.coord;
 
-		var coord = this.texture.build( builder, this.type );
+		if ( output === 'sampler2D' ) {
 
-		return builder.format( coord, this.type, output );
+			return this.texture.build( builder, output );
 
-	}
-	else {
+		}
+
+		return builder.format( this.texture.build( builder, this.type ), this.type, output );
+
+	} else {
 
 		console.warn( "THREE.MirrorNode is not compatible with " + builder.shader + " shader." );
 

+ 21 - 1
examples/js/nodes/inputs/TextureNode.js

@@ -4,7 +4,7 @@
 
 THREE.TextureNode = function( value, coord, bias, project ) {
 
-	THREE.InputNode.call( this, 'v4' );
+	THREE.InputNode.call( this, 'v4', { shared : true } );
 
 	this.value = value;
 	this.coord = coord || new THREE.UVNode();
@@ -24,6 +24,12 @@ THREE.TextureNode.prototype.getTexture = function( builder, output ) {
 
 THREE.TextureNode.prototype.generate = function( builder, output ) {
 
+	if ( output === 'sampler2D' ) {
+
+		return this.getTexture( builder, output );
+
+	}
+
 	var tex = this.getTexture( builder, output );
 	var coord = this.coord.build( builder, this.project ? 'v4' : 'v2' );
 	var bias = this.bias ? this.bias.build( builder, 'fv1' ) : undefined;
@@ -42,6 +48,20 @@ THREE.TextureNode.prototype.generate = function( builder, output ) {
 	if ( bias ) code = method + '(' + tex + ',' + coord + ',' + bias + ')';
 	else code = method + '(' + tex + ',' + coord + ')';
 
+	if ( builder.isSlot( 'color' ) ) {
+
+		code = 'mapTexelToLinear(' + code + ')';
+
+	} else if ( builder.isSlot( 'emissive' ) ) {
+
+		code = 'emissiveMapTexelToLinear(' + code + ')';
+
+	} else if ( builder.isSlot( 'environment' ) ) {
+
+		code = 'envMapTexelToLinear(' + code + ')';
+
+	}
+
 	return builder.format( code, this.type, output );
 
 };

+ 1 - 1
examples/js/nodes/inputs/Vector2Node.js

@@ -4,7 +4,7 @@
 
 THREE.Vector2Node = function( x, y ) {
 
-	THREE.InputNode.call( this, 'v2', { share: false } );
+	THREE.InputNode.call( this, 'v2', { shared: false } );
 
 	this.value = new THREE.Vector2( x, y );
 

+ 1 - 1
examples/js/nodes/inputs/Vector3Node.js

@@ -4,7 +4,7 @@
 
 THREE.Vector3Node = function( x, y, z ) {
 
-	THREE.InputNode.call( this, 'v3', { share: false } );
+	THREE.InputNode.call( this, 'v3', { shared: false } );
 
 	this.type = 'v3';
 	this.value = new THREE.Vector3( x, y, z );

+ 1 - 1
examples/js/nodes/inputs/Vector4Node.js

@@ -4,7 +4,7 @@
 
 THREE.Vector4Node = function( x, y, z, w ) {
 
-	THREE.InputNode.call( this, 'v4', { share: false } );
+	THREE.InputNode.call( this, 'v4', { shared: false } );
 
 	this.value = new THREE.Vector4( x, y, z, w );
 

+ 19 - 17
examples/js/nodes/materials/PhongNode.js

@@ -23,11 +23,11 @@ THREE.PhongNode.prototype.build = function( builder ) {
 	material.define( 'PHONG' );
 	material.define( 'ALPHATEST', '0.0' );
 
-	material.requestAttrib.light = true;
+	material.requestAttribs.light = true;
 
 	if ( builder.isShader( 'vertex' ) ) {
 
-		var transform = this.transform ? this.transform.parseAndBuildCode( builder, 'v3', 'transform' ) : undefined;
+		var transform = this.transform ? this.transform.parseAndBuildCode( builder, 'v3', { cache : 'transform' } ) : undefined;
 
 		material.mergeUniform( THREE.UniformsUtils.merge( [
 
@@ -98,47 +98,47 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 		// parse all nodes to reuse generate codes
 
-		this.color.parse( builder );
+		this.color.parse( builder, { slot : 'color' } );
 		this.specular.parse( builder );
 		this.shininess.parse( builder );
 
 		if ( this.alpha ) this.alpha.parse( builder );
 
-		if ( this.light ) this.light.parse( builder, 'light' );
+		if ( this.light ) this.light.parse( builder, { cache : 'light' } );
 
 		if ( this.ao ) this.ao.parse( builder );
 		if ( this.ambient ) this.ambient.parse( builder );
 		if ( this.shadow ) this.shadow.parse( builder );
-		if ( this.emissive ) this.emissive.parse( builder );
+		if ( this.emissive ) this.emissive.parse( builder, { slot : 'emissive' } );
 
 		if ( this.normal ) this.normal.parse( builder );
 		if ( this.normalScale && this.normal ) this.normalScale.parse( builder );
 
-		if ( this.environment ) this.environment.parse( builder );
+		if ( this.environment ) this.environment.parse( builder, { slot : 'environment' } );
 		if ( this.environmentAlpha && this.environment ) this.environmentAlpha.parse( builder );
 
 		// build code
 
-		var color = this.color.buildCode( builder, 'c' );
+		var color = this.color.buildCode( builder, 'c', { slot : 'color' } );
 		var specular = this.specular.buildCode( builder, 'c' );
 		var shininess = this.shininess.buildCode( builder, 'fv1' );
 
 		var alpha = this.alpha ? this.alpha.buildCode( builder, 'fv1' ) : undefined;
 
-		var light = this.light ? this.light.buildCode( builder, 'v3', 'light' ) : undefined;
+		var light = this.light ? this.light.buildCode( builder, 'v3', { cache : 'light' } ) : undefined;
 
 		var ao = this.ao ? this.ao.buildCode( builder, 'fv1' ) : undefined;
 		var ambient = this.ambient ? this.ambient.buildCode( builder, 'c' ) : undefined;
 		var shadow = this.shadow ? this.shadow.buildCode( builder, 'c' ) : undefined;
-		var emissive = this.emissive ? this.emissive.buildCode( builder, 'c' ) : undefined;
+		var emissive = this.emissive ? this.emissive.buildCode( builder, 'c', { slot : 'emissive' } ) : undefined;
 
 		var normal = this.normal ? this.normal.buildCode( builder, 'v3' ) : undefined;
 		var normalScale = this.normalScale && this.normal ? this.normalScale.buildCode( builder, 'v2' ) : undefined;
 
-		var environment = this.environment ? this.environment.buildCode( builder, 'c' ) : undefined;
+		var environment = this.environment ? this.environment.buildCode( builder, 'c', { slot : 'environment' } ) : undefined;
 		var environmentAlpha = this.environmentAlpha && this.environment ? this.environmentAlpha.buildCode( builder, 'fv1' ) : undefined;
 
-		material.requestAttrib.transparent = alpha != undefined;
+		material.requestAttribs.transparent = alpha != undefined;
 
 		material.addFragmentPars( [
 			THREE.ShaderChunk[ "common" ],
@@ -275,7 +275,7 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 				output.push(
 					environmentAlpha.code,
-					"outgoingLight = mix(" + 'outgoingLight' + "," + environment.result + "," + environmentAlpha.result + ");"
+					"outgoingLight = mix( outgoingLight, " + environment.result + ", " + environmentAlpha.result + " );"
 				);
 
 			}
@@ -287,11 +287,6 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 		}
 
-		output.push(
-			THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
-			THREE.ShaderChunk[ "fog_fragment" ]
-		);
-
 		if ( alpha ) {
 
 			output.push( "gl_FragColor = vec4( outgoingLight, " + alpha.result + " );" );
@@ -303,6 +298,13 @@ THREE.PhongNode.prototype.build = function( builder ) {
 
 		}
 
+		output.push(
+			THREE.ShaderChunk[ "premultiplied_alpha_fragment" ],
+			THREE.ShaderChunk[ "tonemapping_fragment" ],
+			THREE.ShaderChunk[ "encodings_fragment" ],
+			THREE.ShaderChunk[ "fog_fragment" ]
+		);
+
 		code = output.join( "\n" );
 
 	}

+ 21 - 17
examples/js/nodes/materials/StandardNode.js

@@ -24,11 +24,11 @@ THREE.StandardNode.prototype.build = function( builder ) {
 	material.define( 'PHYSICAL' );
 	material.define( 'ALPHATEST', '0.0' );
 
-	material.requestAttrib.light = true;
+	material.requestAttribs.light = true;
 
 	if ( builder.isShader( 'vertex' ) ) {
 
-		var transform = this.transform ? this.transform.parseAndBuildCode( builder, 'v3', 'transform' ) : undefined;
+		var transform = this.transform ? this.transform.parseAndBuildCode( builder, 'v3', { cache : 'transform' } ) : undefined;
 
 		material.mergeUniform( THREE.UniformsUtils.merge( [
 
@@ -107,27 +107,27 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 		// parse all nodes to reuse generate codes
 
-		this.color.parse( builder );
+		this.color.parse( builder, { slot : 'color' } );
 		this.roughness.parse( builder );
 		this.metalness.parse( builder );
 
 		if ( this.alpha ) this.alpha.parse( builder );
 
-		if ( this.light ) this.light.parse( builder, 'light' );
+		if ( this.light ) this.light.parse( builder, { cache : 'light' } );
 
 		if ( this.ao ) this.ao.parse( builder );
 		if ( this.ambient ) this.ambient.parse( builder );
 		if ( this.shadow ) this.shadow.parse( builder );
-		if ( this.emissive ) this.emissive.parse( builder );
+		if ( this.emissive ) this.emissive.parse( builder, { slot : 'emissive' } );
 
 		if ( this.normal ) this.normal.parse( builder );
 		if ( this.normalScale && this.normal ) this.normalScale.parse( builder );
 
-		if ( this.environment ) this.environment.parse( builder, 'env', requires ); // isolate environment from others inputs ( see TextureNode, CubeTextureNode )
+		if ( this.environment ) this.environment.parse( builder, { cache : 'env', requires : requires, slot : 'environment' } ); // isolate environment from others inputs ( see TextureNode, CubeTextureNode )
 
 		// build code
 
-		var color = this.color.buildCode( builder, 'c' );
+		var color = this.color.buildCode( builder, 'c', { slot : 'color' } );
 		var roughness = this.roughness.buildCode( builder, 'fv1' );
 		var metalness = this.metalness.buildCode( builder, 'fv1' );
 
@@ -135,19 +135,19 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 		var alpha = this.alpha ? this.alpha.buildCode( builder, 'fv1' ) : undefined;
 
-		var light = this.light ? this.light.buildCode( builder, 'v3', 'light' ) : undefined;
+		var light = this.light ? this.light.buildCode( builder, 'v3', { cache : 'light' } ) : undefined;
 
 		var ao = this.ao ? this.ao.buildCode( builder, 'fv1' ) : undefined;
 		var ambient = this.ambient ? this.ambient.buildCode( builder, 'c' ) : undefined;
 		var shadow = this.shadow ? this.shadow.buildCode( builder, 'c' ) : undefined;
-		var emissive = this.emissive ? this.emissive.buildCode( builder, 'c' ) : undefined;
+		var emissive = this.emissive ? this.emissive.buildCode( builder, 'c', { slot : 'emissive' } ) : undefined;
 
 		var normal = this.normal ? this.normal.buildCode( builder, 'v3' ) : undefined;
 		var normalScale = this.normalScale && this.normal ? this.normalScale.buildCode( builder, 'v2' ) : undefined;
 
-		var environment = this.environment ? this.environment.buildCode( builder, 'c', 'env', requires ) : undefined;
+		var environment = this.environment ? this.environment.buildCode( builder, 'c', { cache : 'env', requires : requires, slot : 'environment' } ) : undefined;
 
-		material.requestAttrib.transparent = alpha != undefined;
+		material.requestAttribs.transparent = alpha != undefined;
 
 		material.addFragmentPars( [
 
@@ -266,7 +266,9 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 			output.push(
 				ao.code,
-				"reflectedLight.indirectDiffuse *= " + ao.result + ";"
+				"reflectedLight.indirectDiffuse *= " + ao.result + ";",
+				"float dotNV = saturate( dot( geometry.normal, geometry.viewDir ) );",
+				"reflectedLight.indirectSpecular *= computeSpecularOcclusion( dotNV, " + ao.result + ", material.specularRoughness );"
 			);
 
 		}
@@ -310,11 +312,6 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 		output.push( "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;" );
 
-		output.push(
-			THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
-			THREE.ShaderChunk[ "fog_fragment" ]
-		);
-
 		if ( alpha ) {
 
 			output.push( "gl_FragColor = vec4( outgoingLight, " + alpha.result + " );" );
@@ -326,6 +323,13 @@ THREE.StandardNode.prototype.build = function( builder ) {
 
 		}
 
+		output.push(
+			THREE.ShaderChunk[ "premultiplied_alpha_fragment" ],
+			THREE.ShaderChunk[ "tonemapping_fragment" ],
+			THREE.ShaderChunk[ "encodings_fragment" ],
+			THREE.ShaderChunk[ "fog_fragment" ]
+		);
+
 		code = output.join( "\n" );
 
 	}

+ 1 - 1
examples/js/nodes/math/Math3Node.js

@@ -30,7 +30,7 @@ THREE.Math3Node.prototype.getType = function( builder ) {
 
 	if ( a > b && a > c ) return this.a.getType( builder );
 	else if ( b > c ) return this.b.getType( builder );
-	
+
 	return this.c.getType( builder );
 
 };

+ 8 - 8
examples/js/nodes/math/OperatorNode.js

@@ -24,14 +24,14 @@ THREE.OperatorNode.prototype.getType = function( builder ) {
 
 	var a = this.a.getType( builder );
 	var b = this.b.getType( builder );
-	
+
 	if ( builder.isFormatMatrix( a ) ) {
-	
+
 		return 'v4';
-		
-	}
-	// use the greater length vector
-	else if ( builder.getFormatLength( b ) > builder.getFormatLength( a ) ) {
+
+	} else if ( builder.getFormatLength( b ) > builder.getFormatLength( a ) ) {
+
+		// use the greater length vector
 
 		return b;
 
@@ -43,11 +43,11 @@ THREE.OperatorNode.prototype.getType = function( builder ) {
 
 THREE.OperatorNode.prototype.generate = function( builder, output ) {
 
-	var material = builder.material, 
+	var material = builder.material,
 		data = material.getDataNode( this.uuid );
 
 	var type = this.getType( builder );
-	
+
 	var a = this.a.build( builder, type );
 	var b = this.b.build( builder, type );
 

+ 114 - 0
examples/js/nodes/utils/BlurNode.js

@@ -0,0 +1,114 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.BlurNode = function( value, coord, radius, size ) {
+
+	THREE.TempNode.call( this, 'v4' );
+
+	this.requestUpdate = true;
+
+	this.value = value;
+	this.coord = coord || new THREE.UVNode();
+	this.radius = new THREE.Vector2Node( 1, 1 );
+	this.size = size;
+
+	this.blurX = true;
+	this.blurY = true;
+
+	this.horizontal = new THREE.FloatNode( 1 / 64 );
+	this.vertical = new THREE.FloatNode( 1 / 64 );
+
+};
+
+THREE.BlurNode.fBlurX = new THREE.FunctionNode( [
+"vec4 blurX( sampler2D texture, vec2 uv, float s ) {",
+"	vec4 sum = vec4( 0.0 );",
+"	sum += texture2D( texture, vec2( uv.x - 4.0 * s, uv.y ) ) * 0.051;",
+"	sum += texture2D( texture, vec2( uv.x - 3.0 * s, uv.y ) ) * 0.0918;",
+"	sum += texture2D( texture, vec2( uv.x - 2.0 * s, uv.y ) ) * 0.12245;",
+"	sum += texture2D( texture, vec2( uv.x - 1.0 * s, uv.y ) ) * 0.1531;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y ) ) * 0.1633;",
+"	sum += texture2D( texture, vec2( uv.x + 1.0 * s, uv.y ) ) * 0.1531;",
+"	sum += texture2D( texture, vec2( uv.x + 2.0 * s, uv.y ) ) * 0.12245;",
+"	sum += texture2D( texture, vec2( uv.x + 3.0 * s, uv.y ) ) * 0.0918;",
+"	sum += texture2D( texture, vec2( uv.x + 4.0 * s, uv.y ) ) * 0.051;",
+"	return sum;",
+"}"
+].join( "\n" ) );
+
+THREE.BlurNode.fBlurY = new THREE.FunctionNode( [
+"vec4 blurY( sampler2D texture, vec2 uv, float s ) {",
+"	vec4 sum = vec4( 0.0 );",
+"	sum += texture2D( texture, vec2( uv.x, uv.y - 4.0 * s ) ) * 0.051;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y - 3.0 * s ) ) * 0.0918;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y - 2.0 * s ) ) * 0.12245;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y - 1.0 * s ) ) * 0.1531;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y ) ) * 0.1633;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y + 1.0 * s ) ) * 0.1531;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y + 2.0 * s ) ) * 0.12245;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y + 3.0 * s ) ) * 0.0918;",
+"	sum += texture2D( texture, vec2( uv.x, uv.y + 4.0 * s ) ) * 0.051;",
+"	return sum;",
+"}"
+].join( "\n" ) );
+
+THREE.BlurNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.BlurNode.prototype.constructor = THREE.BlurNode;
+
+THREE.BlurNode.prototype.updateFrame = function( delta ) {
+
+	if ( this.size ) {
+
+		this.horizontal.number = this.radius.x / this.size.x;
+		this.vertical.number = this.radius.y / this.size.y;
+
+	} else if ( this.value.value && this.value.value.image ) {
+
+		var image = this.value.value.image;
+
+		this.horizontal.number = this.radius.x / image.width;
+		this.vertical.number = this.radius.y / image.height;
+
+	}
+
+};
+
+THREE.BlurNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material, blurX = THREE.BlurNode.fBlurX, blurY = THREE.BlurNode.fBlurY;
+
+	builder.include( blurX );
+	builder.include( blurY );
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		var blurCode = [], code;
+
+		if ( this.blurX ) {
+
+			blurCode.push( blurX.name + '(' + this.value.build( builder, 'sampler2D' ) + ',' + this.coord.build( builder, 'v2' ) + ',' + this.horizontal.build( builder, 'fv1' ) + ')' );
+
+		}
+
+		if ( this.blurY ) {
+
+			blurCode.push( blurY.name + '(' + this.value.build( builder, 'sampler2D' ) + ',' + this.coord.build( builder, 'v2' ) + ',' + this.vertical.build( builder, 'fv1' ) + ')' );
+
+		}
+
+		if ( blurCode.length == 2 ) code = '(' + blurCode.join( '+' ) + '/2.0)';
+		else if ( blurCode.length ) code = '(' + blurCode[ 0 ] + ')';
+		else code = 'vec4( 0.0 )';
+
+		return builder.format( code, this.getType( builder ), output );
+
+	} else {
+
+		console.warn( "THREE.BlurNode is not compatible with " + builder.shader + " shader." );
+
+		return builder.format( 'vec4( 0.0 )', this.getType( builder ), output );
+
+	}
+
+};

+ 49 - 0
examples/js/nodes/utils/BumpNode.js

@@ -0,0 +1,49 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.BumpNode = function( value, coord, scale ) {
+
+	THREE.TempNode.call( this, 'v3' );
+
+	this.value = value;
+	this.coord = coord || new THREE.UVNode();
+	this.scale = scale || new THREE.Vector2Node( 1, 1 );
+
+};
+
+THREE.BumpNode.fBumpToNormal = new THREE.FunctionNode( [
+"vec3 bumpToNormal( sampler2D bumpMap, vec2 uv, vec2 scale ) {",
+"	vec2 dSTdx = dFdx( uv );",
+"	vec2 dSTdy = dFdy( uv );",
+"	float Hll = texture2D( bumpMap, uv ).x;",
+"	float dBx = texture2D( bumpMap, uv + dSTdx ).x - Hll;",
+"	float dBy = texture2D( bumpMap, uv + dSTdy ).x - Hll;",
+"	return vec3( .5 + ( dBx * scale.x ), .5 + ( dBy * scale.y ), 1.0 );",
+"}"
+].join( "\n" ), null, { derivatives: true } );
+
+THREE.BumpNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.BumpNode.prototype.constructor = THREE.BumpNode;
+
+THREE.BumpNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material, func = THREE.BumpNode.fBumpToNormal;
+
+	builder.include( func );
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		return builder.format( func.name + '(' + this.value.build( builder, 'sampler2D' ) + ',' +
+			this.coord.build( builder, 'v2' ) + ',' +
+			this.scale.build( builder, 'v2' ) + ')', this.getType( builder ), output );
+
+	} else {
+
+		console.warn( "THREE.BumpNode is not compatible with " + builder.shader + " shader." );
+
+		return builder.format( 'vec3( 0.0 )', this.getType( builder ), output );
+
+	}
+
+};

+ 3 - 3
examples/js/nodes/utils/JoinNode.js

@@ -40,7 +40,7 @@ THREE.JoinNode.prototype.getNumElements = function() {
 
 THREE.JoinNode.prototype.getType = function( builder ) {
 
-	return builder.getFormatByLength( this.getNumElements() );
+	return builder.getFormatFromLength( this.getNumElements() );
 
 };
 
@@ -56,13 +56,13 @@ THREE.JoinNode.prototype.generate = function( builder, output ) {
 
 	for ( var i = 0; i < length; i ++ ) {
 
-		var elm = this[ inputs[ i ]];
+		var elm = this[ inputs[ i ] ];
 
 		outputs.push( elm ? elm.build( builder, 'fv1' ) : '0.' );
 
 	}
 
-	var code = builder.getFormatConstructor( length ) + '(' + outputs.join( ',' ) + ')';
+	var code = ( length > 1 ? builder.getConstructorFromLength( length ) : '' ) + '(' + outputs.join( ',' ) + ')';
 
 	return builder.format( code, type, output );
 

+ 1 - 2
examples/js/nodes/utils/NormalMapNode.js

@@ -31,8 +31,7 @@ THREE.NormalMapNode.prototype.generate = function( builder, output ) {
 			this.value.coord.build( builder, 'v2' ) + ',' +
 			this.scale.build( builder, 'v2' ) + ')', this.getType( builder ), output );
 
-	}
-	else {
+	} else {
 
 		console.warn( "THREE.NormalMapNode is not compatible with " + builder.shader + " shader." );
 

+ 1 - 1
examples/js/nodes/utils/ResolutionNode.js

@@ -15,7 +15,7 @@ THREE.ResolutionNode = function( renderer ) {
 THREE.ResolutionNode.prototype = Object.create( THREE.Vector2Node.prototype );
 THREE.ResolutionNode.prototype.constructor = THREE.ResolutionNode;
 
-THREE.ResolutionNode.prototype.updateAnimation = function( delta ) {
+THREE.ResolutionNode.prototype.updateFrame = function( delta ) {
 
 	var size = this.renderer.getSize();
 

+ 2 - 4
examples/js/nodes/utils/RoughnessToBlinnExponentNode.js

@@ -37,8 +37,7 @@ THREE.RoughnessToBlinnExponentNode.prototype.generate = function( builder, outpu
 
 			return builder.format( 'getSpecularMIPLevel( Material_BlinnShininessExponent( material ), 8 )', this.type, output );
 
-		}
-		else {
+		} else {
 
 			console.warn( "THREE.RoughnessToBlinnExponentNode is only compatible with PhysicalMaterial." );
 
@@ -46,8 +45,7 @@ THREE.RoughnessToBlinnExponentNode.prototype.generate = function( builder, outpu
 
 		}
 
-	}
-	else {
+	} else {
 
 		console.warn( "THREE.RoughnessToBlinnExponentNode is not compatible with " + builder.shader + " shader." );
 

+ 1 - 1
examples/js/nodes/utils/SwitchNode.js

@@ -16,7 +16,7 @@ THREE.SwitchNode.prototype.constructor = THREE.SwitchNode;
 
 THREE.SwitchNode.prototype.getType = function( builder ) {
 
-	return builder.getFormatByLength( this.components.length );
+	return builder.getFormatFromLength( this.components.length );
 
 };
 

+ 1 - 1
examples/js/nodes/utils/TimerNode.js

@@ -15,7 +15,7 @@ THREE.TimerNode = function( value, scale ) {
 THREE.TimerNode.prototype = Object.create( THREE.FloatNode.prototype );
 THREE.TimerNode.prototype.constructor = THREE.TimerNode;
 
-THREE.TimerNode.prototype.updateAnimation = function( delta ) {
+THREE.TimerNode.prototype.updateFrame = function( delta ) {
 
 	this.number += delta * this.scale;
 

+ 1 - 1
examples/js/nodes/utils/VelocityNode.js

@@ -21,7 +21,7 @@ THREE.VelocityNode = function( target, params ) {
 THREE.VelocityNode.prototype = Object.create( THREE.Vector3Node.prototype );
 THREE.VelocityNode.prototype.constructor = THREE.VelocityNode;
 
-THREE.VelocityNode.prototype.updateAnimation = function( delta ) {
+THREE.VelocityNode.prototype.updateFrame = function( delta ) {
 
 	this.velocity.subVectors( this.target.position, this.position );
 	this.position.copy( this.target.position );

+ 307 - 62
examples/webgl_materials_nodes.html

@@ -47,7 +47,8 @@
 		<script src="js/nodes/ConstNode.js"></script>
 		<script src="js/nodes/FunctionNode.js"></script>
 		<script src="js/nodes/FunctionCallNode.js"></script>
-		<script src="js/nodes/BuilderNode.js"></script>
+		<script src="js/nodes/AttributeNode.js"></script>
+		<script src="js/nodes/NodeBuilder.js"></script>
 		<script src="js/nodes/NodeLib.js"></script>
 		<script src="js/nodes/NodeMaterial.js"></script>
 
@@ -87,6 +88,8 @@
 		<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
 		<script src="js/nodes/utils/NoiseNode.js"></script>
 		<script src="js/nodes/utils/ResolutionNode.js"></script>
+		<script src="js/nodes/utils/BumpNode.js"></script>
+		<script src="js/nodes/utils/BlurNode.js"></script>
 
 		<!-- Phong Material -->
 		<script src="js/nodes/materials/PhongNode.js"></script>
@@ -106,18 +109,31 @@
 		var move = false;
 		var rtTexture, rtMaterial;
 		var gui, guiElements = [];
+		var textures = {
+			brick : { url : 'textures/brick_diffuse.jpg' },
+			grass : { url : 'textures/terrain/grasslight-big.jpg' },
+			grassNormal : { url : 'textures/terrain/grasslight-big-nm.jpg' },
+			decalDiffuse : { url : 'textures/decal/decal-diffuse.png' },
+			cloud : { url : 'textures/lava/cloud.png' },
+			spherical : { url : 'textures/envmap.png' }
+		};
 
 		var param = { example: 'standard' };
 
-		var brick = new THREE.TextureLoader().load( 'textures/brick_diffuse.jpg' );
-		var grass = new THREE.TextureLoader().load( 'textures/terrain/grasslight-big.jpg' );
-		var grassNormal = new THREE.TextureLoader().load( 'textures/terrain/grasslight-big-nm.jpg' );
+		function getTexture( name ) {
 
-		var decalDiffuse = new THREE.TextureLoader().load( 'textures/decal/decal-diffuse.png' );
-		decalDiffuse.wrapS = decalDiffuse.wrapT = THREE.RepeatWrapping;
+			var texture = textures[ name ].texture;
 
-		var cloud = new THREE.TextureLoader().load( 'textures/lava/cloud.png' );
-		cloud.wrapS = cloud.wrapT = THREE.RepeatWrapping;
+			if ( ! texture ) {
+
+				texture = textures[ name ].texture = new THREE.TextureLoader().load( textures[ name ].url );
+				texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
+
+			}
+
+			return texture;
+
+		}
 
 		var cubemap = function() {
 
@@ -193,6 +209,9 @@
 				'basic / layers': 'layers',
 				'basic / rim': 'rim',
 				'basic / color-adjustment': 'color-adjustment',
+				'basic / bump': 'bump',
+				'basic / blur': 'blur',
+				'basic / spherical-reflection': 'spherical-reflection',
 				'adv / fresnel': 'fresnel',
 				'adv / saturation': 'saturation',
 				'adv / top-bottom': 'top-bottom',
@@ -205,10 +224,14 @@
 				'adv / camera-depth': 'camera-depth',
 				'adv / soft-body': 'soft-body',
 				'adv / wave': 'wave',
+				'adv / triangle-blur' : 'triangle-blur',
+				'adv / expression' : 'expression',
 				'adv / sss' : 'sss',
 				'adv / translucent' : 'translucent',
 				'misc / smoke' : 'smoke',
-				'misc / firefly' : 'firefly'
+				'misc / firefly' : 'firefly',
+				'misc / reserved-keywords' : 'reserved-keywords',
+				'misc / custom-attribute' : 'custom-attribute'
 			} ).onFinishChange( function() {
 
 				updateMaterial();
@@ -306,14 +329,14 @@
 					//mtl.environmentAlpha = // environment alpha (float)
 					//mtl.transform = // vertex transformation (vec3)
 
-					var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
+					var mask = new THREE.SwitchNode( new THREE.TextureNode( getTexture( "decalDiffuse" ) ), 'w' );
 
-					mtl.color = new THREE.TextureNode( grass );
+					mtl.color = new THREE.TextureNode( getTexture( "grass" ) );
 					mtl.specular = new THREE.FloatNode( .5 );
 					mtl.shininess = new THREE.FloatNode( 15 );
 					mtl.environment = new THREE.CubeTextureNode( cubemap );
 					mtl.environmentAlpha = mask;
-					mtl.normal = new THREE.TextureNode( grassNormal );
+					mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
 					mtl.normalScale = new THREE.Math1Node( mask, THREE.Math1Node.INVERT );
 
 					break;
@@ -338,7 +361,7 @@
 					//mtl.environment = // reflection/refraction (vec3)
 					//mtl.transform = // vertex transformation (vec3)
 
-					var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
+					var mask = new THREE.SwitchNode( new THREE.TextureNode( getTexture( "decalDiffuse" ) ), 'w' );
 
 					var normalScale = new THREE.FloatNode( .3 );
 
@@ -372,7 +395,7 @@
 					mtl.roughness = roughness;
 					mtl.metalness = metalness;
 					mtl.environment = new THREE.CubeTextureNode( cubemap );
-					mtl.normal = new THREE.TextureNode( grassNormal );
+					mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
 					mtl.normalScale = normalMask;
 
 					// GUI
@@ -619,7 +642,7 @@
 
 					mtl = new THREE.PhongNodeMaterial();
 
-					var texture = new THREE.TextureNode( brick );
+					var texture = new THREE.TextureNode( getTexture( "brick" ) );
 
 					var hue = new THREE.FloatNode();
 					var sataturation = new THREE.FloatNode( 1 );
@@ -669,6 +692,82 @@
 
 					break;
 
+				case 'bump':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var diffuse = new THREE.TextureNode( getTexture( "brick" ) );
+
+					var bump = new THREE.BumpNode( new THREE.TextureNode( getTexture( "brick" ) ) );
+					bump.scale = new THREE.Vector2Node( - 1.5, - 1.5 );
+
+					mtl.color = diffuse;
+					mtl.normal = bump;
+
+					// GUI
+
+					addGui( 'scaleX', bump.scale.x, function( val ) {
+
+						bump.scale.x = val;
+
+					}, false, - 2, 2 );
+
+					addGui( 'scaleY', bump.scale.y, function( val ) {
+
+						bump.scale.y = val;
+
+					}, false, - 2, 2 );
+
+					addGui( 'color', true, function( val ) {
+
+						mtl.color = val ? diffuse : new THREE.ColorNode( 0xEEEEEE );
+
+						mtl.build();
+
+					} );
+
+					break;
+
+				case 'blur':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var diffuse = new THREE.TextureNode( getTexture( "brick" ) );
+
+					var blur = new THREE.BlurNode( new THREE.TextureNode( getTexture( "brick" ) ) );
+
+					mtl.color = blur;
+
+					// GUI
+
+					addGui( 'radiusX', blur.radius.x, function( val ) {
+
+						blur.radius.x = val;
+
+					}, false, 0, 15 );
+
+					addGui( 'radiusY', blur.radius.y, function( val ) {
+
+						blur.radius.y = val;
+
+					}, false, 0, 15 );
+
+					break;
+
+				case 'spherical-reflection':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					mtl.environment = new THREE.TextureNode( getTexture( "spherical" ), new THREE.ReflectNode( THREE.ReflectNode.SPHERE ) );
+
+					break;
+
 				case 'fresnel':
 
 					// MATERIAL
@@ -729,8 +828,8 @@
 
 					mtl = new THREE.PhongNodeMaterial();
 
-					var tex1 = new THREE.TextureNode( grass );
-					var tex2 = new THREE.TextureNode( brick );
+					var tex1 = new THREE.TextureNode( getTexture( "grass" ) );
+					var tex2 = new THREE.TextureNode( getTexture( "brick" ) );
 
 					var offset = new THREE.FloatNode( 0 );
 					var scale = new THREE.FloatNode( 1 );
@@ -748,7 +847,7 @@
 						THREE.OperatorNode.MUL
 					);
 
-					var mask = new THREE.TextureNode( decalDiffuse, uvScale );
+					var mask = new THREE.TextureNode( getTexture( "decalDiffuse" ), uvScale );
 					var maskAlphaChannel = new THREE.SwitchNode( mask, 'w' );
 
 					var blend = new THREE.Math3Node(
@@ -782,14 +881,14 @@
 
 					mtl = new THREE.StandardNodeMaterial();
 
-					var tex = new THREE.TextureNode( brick );
+					var tex = new THREE.TextureNode( getTexture( "brick" ) );
 					var sat = new THREE.FloatNode( 0 );
 
 					var satrgb = new THREE.FunctionNode( [
 					"vec3 satrgb(vec3 rgb, float adjustment) {",
-						//"const vec3 W = vec3(0.2125, 0.7154, 0.0721);", // LUMA
-						"vec3 intensity = vec3(dot(rgb, LUMA));",
-						"return mix(intensity, rgb, adjustment);",
+					//"	const vec3 W = vec3(0.2125, 0.7154, 0.0721);", // LUMA
+					"	vec3 intensity = vec3(dot(rgb, LUMA));",
+					"	return mix(intensity, rgb, adjustment);",
 					"}"
 					].join( "\n" ) );
 
@@ -821,8 +920,8 @@
 
 					mtl = new THREE.PhongNodeMaterial();
 
-					var top = new THREE.TextureNode( grass );
-					var bottom = new THREE.TextureNode( brick );
+					var top = new THREE.TextureNode( getTexture( "grass" ) );
+					var bottom = new THREE.TextureNode( getTexture( "brick" ) );
 
 					var normal = new THREE.NormalNode( THREE.NormalNode.WORLD );
 					var normalY = new THREE.SwitchNode( normal, 'y' );
@@ -890,7 +989,7 @@
 						THREE.OperatorNode.ADD
 					);
 
-					var tex = new THREE.TextureNode( cloud, displaceOffset );
+					var tex = new THREE.TextureNode( getTexture( "cloud" ), displaceOffset );
 					var texArea = new THREE.SwitchNode( tex, 'w' );
 
 					var displace = new THREE.OperatorNode(
@@ -983,8 +1082,8 @@
 						THREE.OperatorNode.ADD
 					);
 
-					var cloudA = new THREE.TextureNode( cloud, uvOffsetA );
-					var cloudB = new THREE.TextureNode( cloud, uvOffsetB );
+					var cloudA = new THREE.TextureNode( getTexture( "cloud" ), uvOffsetA );
+					var cloudB = new THREE.TextureNode( getTexture( "cloud" ), uvOffsetB );
 
 					var clouds = new THREE.OperatorNode(
 						cloudA,
@@ -1062,45 +1161,45 @@
 
 					var hash2 = new THREE.FunctionNode( [
 					"vec2 hash2(vec2 p) {",
-						"return fract(sin(vec2(dot(p, vec2(123.4, 748.6)), dot(p, vec2(547.3, 659.3))))*5232.85324);",
+					"	return fract(sin(vec2(dot(p, vec2(123.4, 748.6)), dot(p, vec2(547.3, 659.3))))*5232.85324);",
 					"}"
 					].join( "\n" ) );
 
 					var voronoi = new THREE.FunctionNode( [
 					// Based off of iq's described here: http://www.iquilezles.org/www/articles/voronoili
 					"float voronoi(vec2 p, in float time) {",
-						"vec2 n = floor(p);",
-						"vec2 f = fract(p);",
-						"float md = 5.0;",
-						"vec2 m = vec2(0.0);",
-						"for (int i = -1; i <= 1; i++) {",
-							"for (int j = -1; j <= 1; j++) {",
-								"vec2 g = vec2(i, j);",
-								"vec2 o = hash2(n + g);",
-								"o = 0.5 + 0.5 * sin(time + 5.038 * o);",
-								"vec2 r = g + o - f;",
-								"float d = dot(r, r);",
-								"if (d < md) {",
-									"md = d;",
-									"m = n+g+o;",
-								"}",
-							"}",
-						"}",
-						"return md;",
+					"	vec2 n = floor(p);",
+					"	vec2 f = fract(p);",
+					"	float md = 5.0;",
+					"	vec2 m = vec2(0.0);",
+					"	for (int i = -1; i <= 1; i++) {",
+					"		for (int j = -1; j <= 1; j++) {",
+					"			vec2 g = vec2(i, j);",
+					"			vec2 o = hash2(n + g);",
+					"			o = 0.5 + 0.5 * sin(time + 5.038 * o);",
+					"			vec2 r = g + o - f;",
+					"			float d = dot(r, r);",
+					"			if (d < md) {",
+					"				md = d;",
+					"				m = n+g+o;",
+					"			}",
+					"		}",
+					"	}",
+					"	return md;",
 					"}"
 					].join( "\n" ), [ hash2 ] ); // define hash2 as dependencies
 
 					var voronoiLayers = new THREE.FunctionNode( [
 					// based on https://www.shadertoy.com/view/4tXSDf
 					"float voronoiLayers(vec2 p, in float time) {",
-						"float v = 0.0;",
-						"float a = 0.4;",
-						"for (int i = 0; i < 3; i++) {",
-							"v += voronoi(p, time) * a;",
-							"p *= 2.0;",
-							"a *= 0.5;",
-						"}",
-						"return v;",
+					"	float v = 0.0;",
+					"	float a = 0.4;",
+					"	for (int i = 0; i < 3; i++) {",
+					"		v += voronoi(p, time) * a;",
+					"		p *= 2.0;",
+					"		a *= 0.5;",
+					"	}",
+					"	return v;",
 					"}"
 					].join( "\n" ), [ voronoi ] ); // define voronoi as dependencies
 
@@ -1340,7 +1439,7 @@
 					);
 
 					mtl.color = color;
-					mtl.normal = new THREE.TextureNode( grassNormal );
+					mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
 					mtl.normalScale = furScale;
 					mtl.environment = mildnessColor;
 					mtl.environmentAlpha = new THREE.Math1Node( viewZ, THREE.Math1Node.INVERT );
@@ -1424,8 +1523,7 @@
 						mtl.roughness = new THREE.FloatNode( .5 );
 						mtl.environment = new THREE.CubeTextureNode( cubemap );
 
-					}
-					else {
+					} else {
 
 						// PhongNodeMaterial
 
@@ -1579,6 +1677,155 @@
 
 					break;
 
+				case 'custom-attribute':
+
+					// GEOMETRY
+
+					// add "position" buffer to "custom" attribute
+					teapot.attributes[ 'custom' ] = teapot.attributes[ 'position' ];
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					mtl.color = new THREE.AttributeNode( "custom", 3 );
+
+					// or
+
+					//mtl.color = new THREE.AttributeNode( "custom", "vec3" );
+
+					break;
+
+				case 'expression':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var speed = new THREE.FloatNode( .5 );
+
+					mtl.color = new THREE.FunctionNode( "myCustomUv + (sin(time*speed)*.5) + (position * .05)", "vec3" );
+					mtl.color.keywords[ "speed" ] = speed;
+
+					mtl.transform = new THREE.FunctionNode( "mod(time*speed,1.0) < 0.5 ? position + (worldNormal*(1.0+sin(time*speed*1.0))*3.0) : position + sin( position.x * sin(time*speed*2.0))", "vec3" );
+					mtl.transform.keywords[ "speed" ] = speed;
+
+					// add global keyword ( variable or const )
+					THREE.NodeLib.addKeyword( 'myCustomUv', function( builder ) {
+
+						return new THREE.ReflectNode();
+
+					} );
+
+					// GUI
+
+					addGui( 'speed', speed.number, function( val ) {
+
+						speed.number = val;
+
+					}, false, 0, 1 );
+
+					break;
+
+				case 'reserved-keywords':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var keywordsexample = new THREE.FunctionNode( [
+					// use "uv" reserved keyword
+					"vec4 keywordsexample( sampler2D texture ) {",
+					"	return texture2D( texture, myUV ) + vec4( position * myAlpha, 0.0 );",
+					"}"
+					].join( "\n" ) );
+
+					// add local keyword ( const only )
+					keywordsexample.keywords[ "myAlpha" ] = new THREE.ConstNode( "float myAlpha .05" );
+
+					// add global keyword ( const only )
+					THREE.NodeLib.addKeyword( 'myUV', function( builder ) {
+
+						return new THREE.UVNode();
+
+					} );
+
+					// add global const or function
+					//THREE.NodeLib.add( new THREE.ConstNode("float MY_CONST .05") )
+
+					// reserved keywords
+					console.log( THREE.NodeLib.keywords );
+
+					// keywords conflit? use this to disable:
+					//blurtexture.useKeywords = false; // ( true is default )
+
+					mtl.color = new THREE.FunctionCallNode( keywordsexample, [ new THREE.TextureNode( getTexture( "brick" ) ) ] );
+
+					break;
+
+				case 'triangle-blur':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var delta = new THREE.Vector2Node( .5, .25 );
+					var alpha = new THREE.FloatNode( 1 );
+
+					var blurtexture = new THREE.FunctionNode( [
+					// Reference: TriangleBlurShader.js
+					"vec4 blurtexture(sampler2D texture, vec2 uv, vec2 delta) {",
+					"	vec4 color = vec4( 0.0 );",
+					"	float total = 0.0;",
+					// randomize the lookup values to hide the fixed number of samples
+					"	float offset = rand( uv );",
+					"	for ( float t = -BLUR_ITERATIONS; t <= BLUR_ITERATIONS; t ++ ) {",
+					"		float percent = ( t + offset - 0.5 ) / BLUR_ITERATIONS;",
+					"		float weight = 1.0 - abs( percent );",
+					"		color += texture2D( texture, uv + delta * percent ) * weight;",
+					"		total += weight;",
+					"	}",
+					"	return color / total;",
+					"}"
+					].join( "\n" ), [ new THREE.ConstNode( "float BLUR_ITERATIONS 10.0" ) ] );
+
+					var blurredTexture = new THREE.FunctionCallNode( blurtexture, {
+						texture : new THREE.TextureNode( getTexture( "brick" ) ),
+						delta : delta,
+						uv : new THREE.UVNode()
+					} );
+
+					var color = new THREE.Math3Node(
+						new THREE.TextureNode( getTexture( "brick" ) ),
+						blurredTexture,
+						alpha,
+						THREE.Math3Node.MIX
+					);
+
+					mtl.color = color;
+
+					// GUI
+
+					addGui( 'alpha', alpha.number, function( val ) {
+
+						alpha.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'deltaX', delta.x, function( val ) {
+
+						delta.x = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'deltaY', delta.x, function( val ) {
+
+						delta.y = val;
+
+					}, false, 0, 1 );
+
+					break;
+
 				case 'firefly':
 
 					// MATERIAL
@@ -1727,8 +1974,7 @@
 						mtl.light = light;
 						mtl.environment = color;
 
-					}
-					else {
+					} else {
 
 						frontColor = new THREE.ColorNode( 0xd04327 );
 						backColor = new THREE.ColorNode( 0x1a0e14 );
@@ -1810,8 +2056,7 @@
 				mesh.position.z = Math.cos( time ) * 10;
 				mesh.position.y = Math.sin( time ) * 10;
 
-			}
-			else {
+			} else {
 
 				mesh.position.z = mesh.position.y = 0;
 
@@ -1820,7 +2065,7 @@
 			//mesh.rotation.z += .01;
 
 			// update material animation and/or gpu calcs (pre-renderer)
-			mesh.material.updateAnimation( delta );
+			mesh.material.updateFrame( delta );
 
 			// render to texture for sss/translucent material only
 			if ( rtTexture ) {

+ 22 - 2
examples/webgl_mirror_nodes.html

@@ -35,6 +35,7 @@
 		</div>
 
 		<script src="../build/three.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
 		<script src="js/Mirror.js"></script>
 		<script src="js/controls/OrbitControls.js"></script>
 
@@ -46,7 +47,7 @@
 		<script src="js/nodes/ConstNode.js"></script>
 		<script src="js/nodes/FunctionNode.js"></script>
 		<script src="js/nodes/FunctionCallNode.js"></script>
-		<script src="js/nodes/BuilderNode.js"></script>
+		<script src="js/nodes/NodeBuilder.js"></script>
 		<script src="js/nodes/NodeLib.js"></script>
 		<script src="js/nodes/NodeMaterial.js"></script>
 
@@ -88,6 +89,7 @@
 		<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
 		<script src="js/nodes/utils/NoiseNode.js"></script>
 		<script src="js/nodes/utils/ResolutionNode.js"></script>
+		<script src="js/nodes/utils/BlurNode.js"></script>
 
 		<!-- Phong Material -->
 		<script src="js/nodes/materials/PhongNode.js"></script>
@@ -111,8 +113,11 @@
 			decalDiffuse.wrapS = decalDiffuse.wrapT = THREE.RepeatWrapping;
 
 			var camera, scene, renderer;
+			var clock = new THREE.Clock();
 
 			var cameraControls;
+			
+			var gui = new dat.GUI();
 
 			var groundMirror;
 			var sphereGroup, smallSphere;
@@ -181,8 +186,20 @@
 					THREE.Math3Node.MIX
 				);
 
+				var blurMirror = new THREE.BlurNode( mirror );
+				blurMirror.size = new THREE.Vector2( WIDTH, HEIGHT );
+				blurMirror.coord = new THREE.FunctionNode( "(projCoord.xyz / projCoord.q)", "vec3" );
+				blurMirror.coord.keywords[ "projCoord" ] = new THREE.OperatorNode( mirror.offset, mirror.coord, THREE.OperatorNode.ADD );
+				blurMirror.radius.x = blurMirror.radius.y = 0;
+
+				gui.add( { blur : blurMirror.radius.x }, "blur", 0, 25 ).onChange( function(v) {
+
+					blurMirror.radius.x = blurMirror.radius.y = v;
+
+				} );
+
 				groundMirrorMaterial = new THREE.PhongNodeMaterial();
-				groundMirrorMaterial.environment = mirror;
+				groundMirrorMaterial.environment = blurMirror; // or add "mirror" variable to disable blur
 				groundMirrorMaterial.environmentAlpha = mask;
 				groundMirrorMaterial.normal = normal;
 				//groundMirrorMaterial.normalScale = new THREE.FloatNode( 1 );
@@ -281,6 +298,7 @@
 
 				requestAnimationFrame( update );
 
+				var delta = clock.getDelta();
 				var timer = Date.now() * 0.01;
 
 				sphereGroup.rotation.y -= 0.002;
@@ -293,6 +311,8 @@
 				smallSphere.rotation.y = ( Math.PI / 2 ) - timer * 0.1;
 				smallSphere.rotation.z = timer * 0.8;
 
+				groundMirrorMaterial.updateFrame( delta );
+
 				cameraControls.update();
 
 				render();

+ 36 - 2
examples/webgl_postprocessing_nodes.html

@@ -49,7 +49,7 @@
 		<script src="js/nodes/ConstNode.js"></script>
 		<script src="js/nodes/FunctionNode.js"></script>
 		<script src="js/nodes/FunctionCallNode.js"></script>
-		<script src="js/nodes/BuilderNode.js"></script>
+		<script src="js/nodes/NodeBuilder.js"></script>
 		<script src="js/nodes/NodeLib.js"></script>
 		<script src="js/nodes/NodeMaterial.js"></script>
 
@@ -81,6 +81,7 @@
 		<script src="js/nodes/utils/JoinNode.js"></script>
 		<script src="js/nodes/utils/TimerNode.js"></script>
 		<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
+		<script src="js/nodes/utils/BlurNode.js"></script>
 
 		<!-- Post-Processing -->
 		<script src="js/nodes/postprocessing/NodePass.js"></script>
@@ -90,6 +91,7 @@
 			var camera, scene, renderer, composer;
 			var object, light, nodepass;
 			var gui, guiElements = [];
+			var clock = new THREE.Clock();
 
 			var param = { example: 'color-adjustment' };
 
@@ -117,7 +119,8 @@
 					'basic / invert': 'invert',
 					'adv / saturation': 'saturation',
 					'adv / refraction': 'refraction',
-					'adv / mosaic': 'mosaic'
+					'adv / mosaic': 'mosaic',
+					'adv / blur': 'blur'
 				} ).onFinishChange( function() {
 
 					updateMaterial();
@@ -464,6 +467,33 @@
 						} );
 
 						break;
+						
+					case 'blur':
+
+						// PASS
+
+						var size = renderer.getSize();
+
+						var blurScreen = new THREE.BlurNode( new THREE.ScreenNode() );
+						blurScreen.size = new THREE.Vector2( size.width, size.height );
+
+						nodepass.value = blurScreen;
+
+						// GUI
+
+						addGui( 'blurX', blurScreen.radius.x, function( val ) {
+
+							blurScreen.radius.x = val;
+
+						}, false, 0, 15 );
+
+						addGui( 'blurY', blurScreen.radius.y, function( val ) {
+
+							blurScreen.radius.y = val;
+
+						}, false, 0, 15 );
+
+						break;
 
 				}
 
@@ -541,9 +571,13 @@
 
 				requestAnimationFrame( animate );
 
+				var delta = clock.getDelta();
+
 				object.rotation.x += 0.005;
 				object.rotation.y += 0.01;
 
+				nodepass.node.updateFrame( delta );
+
 				composer.render();
 
 			}