Parcourir la source

Merge branch 'dev' of https://github.com/sunag/three.js into dev

Conflicts:
	examples/index.html
Mr.doob il y a 9 ans
Parent
commit
956b74d8b6
81 fichiers modifiés avec 7924 ajouts et 10 suppressions
  1. 2 0
      examples/files.js
  2. 146 0
      examples/js/nodes/BuilderNode.js
  3. 63 0
      examples/js/nodes/ConstNode.js
  4. 60 0
      examples/js/nodes/FunctionCallNode.js
  5. 159 0
      examples/js/nodes/FunctionNode.js
  6. 98 0
      examples/js/nodes/GLNode.js
  7. 46 0
      examples/js/nodes/InputNode.js
  8. 95 0
      examples/js/nodes/LibNode.js
  9. 476 0
      examples/js/nodes/NodeMaterial.js
  10. 37 0
      examples/js/nodes/RawNode.js
  11. 124 0
      examples/js/nodes/TempNode.js
  12. 128 0
      examples/js/nodes/accessors/CameraNode.js
  13. 31 0
      examples/js/nodes/accessors/ColorsNode.js
  14. 66 0
      examples/js/nodes/accessors/NormalNode.js
  15. 87 0
      examples/js/nodes/accessors/PositionNode.js
  16. 39 0
      examples/js/nodes/accessors/ReflectNode.js
  17. 31 0
      examples/js/nodes/accessors/UVNode.js
  18. 16 0
      examples/js/nodes/inputs/ColorNode.js
  19. 43 0
      examples/js/nodes/inputs/CubeTextureNode.js
  20. 29 0
      examples/js/nodes/inputs/FloatNode.js
  21. 29 0
      examples/js/nodes/inputs/IntNode.js
  22. 24 0
      examples/js/nodes/inputs/ScreenNode.js
  23. 37 0
      examples/js/nodes/inputs/TextureNode.js
  24. 16 0
      examples/js/nodes/inputs/Vector2Node.js
  25. 17 0
      examples/js/nodes/inputs/Vector3Node.js
  26. 16 0
      examples/js/nodes/inputs/Vector4Node.js
  27. 274 0
      examples/js/nodes/materials/PhongNode.js
  28. 17 0
      examples/js/nodes/materials/PhongNodeMaterial.js
  29. 286 0
      examples/js/nodes/materials/StandardNode.js
  30. 17 0
      examples/js/nodes/materials/StandardNodeMaterial.js
  31. 78 0
      examples/js/nodes/math/Math1Node.js
  32. 96 0
      examples/js/nodes/math/Math2Node.js
  33. 84 0
      examples/js/nodes/math/Math3Node.js
  34. 47 0
      examples/js/nodes/math/OperatorNode.js
  35. 33 0
      examples/js/nodes/postprocessing/NodePass.js
  36. 69 0
      examples/js/nodes/utils/JoinNode.js
  37. 43 0
      examples/js/nodes/utils/NormalMapNode.js
  38. 44 0
      examples/js/nodes/utils/RoughnessToBlinnExponentNode.js
  39. 68 0
      examples/js/nodes/utils/SwitchNode.js
  40. 21 0
      examples/js/nodes/utils/TimeNode.js
  41. 59 0
      examples/js/nodes/utils/VelocityNode.js
  42. 21 10
      examples/js/postprocessing/ShaderPass.js
  43. 146 0
      examples/materials/nodes/NodeBuilder.js
  44. 52 0
      examples/materials/nodes/NodeConst.js
  45. 157 0
      examples/materials/nodes/NodeFunction.js
  46. 60 0
      examples/materials/nodes/NodeFunctionCall.js
  47. 94 0
      examples/materials/nodes/NodeGL.js
  48. 44 0
      examples/materials/nodes/NodeInput.js
  49. 87 0
      examples/materials/nodes/NodeLib.js
  50. 467 0
      examples/materials/nodes/NodeMaterial.js
  51. 37 0
      examples/materials/nodes/NodeRaw.js
  52. 116 0
      examples/materials/nodes/NodeTemp.js
  53. 106 0
      examples/materials/nodes/accessors/NodeCamera.js
  54. 31 0
      examples/materials/nodes/accessors/NodeColors.js
  55. 66 0
      examples/materials/nodes/accessors/NodeNormal.js
  56. 87 0
      examples/materials/nodes/accessors/NodePosition.js
  57. 39 0
      examples/materials/nodes/accessors/NodeReflect.js
  58. 31 0
      examples/materials/nodes/accessors/NodeUV.js
  59. 59 0
      examples/materials/nodes/extras/NodeVelocity.js
  60. 16 0
      examples/materials/nodes/inputs/NodeColor.js
  61. 37 0
      examples/materials/nodes/inputs/NodeCubeTexture.js
  62. 21 0
      examples/materials/nodes/inputs/NodeFloat.js
  63. 21 0
      examples/materials/nodes/inputs/NodeInt.js
  64. 31 0
      examples/materials/nodes/inputs/NodeTexture.js
  65. 16 0
      examples/materials/nodes/inputs/NodeVector2.js
  66. 17 0
      examples/materials/nodes/inputs/NodeVector3.js
  67. 16 0
      examples/materials/nodes/inputs/NodeVector4.js
  68. 257 0
      examples/materials/nodes/interfaces/NodePhong.js
  69. 17 0
      examples/materials/nodes/interfaces/NodePhongMaterial.js
  70. 268 0
      examples/materials/nodes/interfaces/NodeStandard.js
  71. 17 0
      examples/materials/nodes/interfaces/NodeStandardMaterial.js
  72. 78 0
      examples/materials/nodes/math/NodeMath1.js
  73. 93 0
      examples/materials/nodes/math/NodeMath2.js
  74. 80 0
      examples/materials/nodes/math/NodeMath3.js
  75. 45 0
      examples/materials/nodes/math/NodeOperator.js
  76. 65 0
      examples/materials/nodes/utils/NodeJoin.js
  77. 44 0
      examples/materials/nodes/utils/NodeRoughnessToBlinnExponent.js
  78. 66 0
      examples/materials/nodes/utils/NodeSwitch.js
  79. 20 0
      examples/materials/nodes/utils/NodeTime.js
  80. 1320 0
      examples/webgl_materials_nodes.html
  81. 528 0
      examples/webgl_postprocessing_nodes.html

+ 2 - 0
examples/files.js

@@ -118,6 +118,7 @@ var files = {
 		"webgl_materials_envmaps",
 		"webgl_materials_grass",
 		"webgl_materials_lightmap",
+		"webgl_materials_nodes",
 		"webgl_materials_normalmap",
 		"webgl_materials_parallaxmap",
 		"webgl_materials_shaders_fresnel",
@@ -170,6 +171,7 @@ var files = {
 		"webgl_postprocessing_glitch",
 		"webgl_postprocessing_godrays",
 		"webgl_postprocessing_masking",
+		"webgl_postprocessing_nodes",
 		"webgl_postprocessing_ssao",
 		"webgl_raycast_texture",
 		"webgl_read_float_buffer",

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

@@ -0,0 +1,146 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.BuilderNode = function( material ) {
+
+	this.material = material;
+
+	this.require = {};
+	this.isVerify = false;
+	this.cache = '';
+
+};
+
+THREE.BuilderNode.prototype = {
+	constructor: THREE.BuilderNode,
+
+	include : function( func ) {
+
+		this.material.include( this.shader, func );
+
+	},
+
+	getFormatConstructor : function( len ) {
+
+		return THREE.BuilderNode.constructors[ len - 1 ];
+
+	},
+
+	getFormat : function( format ) {
+
+		return format.replace( 'c', 'v3' ).replace( /fv1|iv1/, 'v1' );
+
+	},
+
+	getFormatLength : function( format ) {
+
+		return parseInt( this.getFormat( format ).substr( 1 ) );
+
+	},
+
+	getFormatByLength : function( len ) {
+
+		if ( len == 1 ) return 'fv1';
+
+		return 'v' + len;
+
+	},
+
+	format : function( code, from, to ) {
+
+		var format = this.getFormat( 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,0.0)';
+
+			case 'v3=v1': return code + '.x';
+			case 'v3=v2': return code + '.xy';
+			case 'v3=v4': return 'vec4(' + code + ',0.0)';
+
+			case 'v4=v1': return code + '.x';
+			case 'v4=v2': return code + '.xy';
+			case 'v4=v3': return code + '.xyz';
+		}
+
+		return code;
+
+	},
+
+	getType : 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;
+
+	},
+
+	setCache : function( name ) {
+
+		this.cache = name || '';
+
+		return this;
+
+	},
+
+	getElementByIndex : function( index ) {
+
+		return THREE.BuilderNode.elements[ index ];
+
+	},
+
+	getElementIndex : function( elm ) {
+
+		return THREE.BuilderNode.elements.indexOf( elm );
+
+	},
+
+	isShader : function( shader ) {
+
+		return this.shader == shader || this.isVerify;
+
+	},
+
+	setShader : function( shader ) {
+
+		this.shader = shader;
+
+		return this;
+
+	}
+};
+
+THREE.BuilderNode.type = {
+	'float' : 'fv1',
+	vec2 : 'v2',
+	vec3 : 'v3',
+	vec4 : 'v4'
+};
+
+THREE.BuilderNode.constructors = [
+	'',
+	'vec2',
+	'vec3',
+	'vec4'
+];
+
+THREE.BuilderNode.elements = [
+	'x',
+	'y',
+	'z',
+	'w'
+];

+ 63 - 0
examples/js/nodes/ConstNode.js

@@ -0,0 +1,63 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.ConstNode = function( name, useDefine ) {
+
+	THREE.TempNode.call( this );
+
+	this.parse( name || THREE.ConstNode.PI, useDefine );
+
+};
+
+THREE.ConstNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.ConstNode.prototype.constructor = THREE.ConstNode;
+
+THREE.ConstNode.PI = 'PI';
+THREE.ConstNode.PI2 = 'PI2';
+THREE.ConstNode.RECIPROCAL_PI = 'RECIPROCAL_PI';
+THREE.ConstNode.RECIPROCAL_PI2 = 'RECIPROCAL_PI2';
+THREE.ConstNode.LOG2 = 'LOG2';
+THREE.ConstNode.EPSILON = 'EPSILON';
+
+THREE.ConstNode.prototype.parse = function( src, useDefine ) {
+
+	var name, type;
+
+	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=(.*?)\;/i;
+	var match = src.match( rDeclaration );
+
+	if ( match && match.length > 1 ) {
+
+		type = match[ 1 ];
+		name = match[ 2 ];
+
+		if ( useDefine ) {
+
+			this.src = '#define ' + name + ' ' + match[ 3 ];
+
+		}
+		else {
+
+			this.src = 'const ' + type + ' ' + name + ' = ' + match[ 3 ] + ';';
+
+		}
+
+	}
+	else {
+
+		name = src;
+		type = 'fv1';
+
+	}
+
+	this.name = name;
+	this.type = type;
+
+};
+
+THREE.ConstNode.prototype.generate = function( builder, output ) {
+
+	return builder.format( this.name, this.getType( builder ), output );
+
+};

+ 60 - 0
examples/js/nodes/FunctionCallNode.js

@@ -0,0 +1,60 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.FunctionCallNode = function( value ) {
+
+	THREE.TempNode.call( this );
+
+	this.setFunction( value );
+
+};
+
+THREE.FunctionCallNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.FunctionCallNode.prototype.constructor = THREE.FunctionCallNode;
+
+THREE.FunctionCallNode.prototype.setFunction = function( val ) {
+
+	this.input = [];
+	this.value = val;
+
+};
+
+THREE.FunctionCallNode.prototype.getFunction = function() {
+
+	return this.value;
+
+};
+
+THREE.FunctionCallNode.prototype.getType = function( builder ) {
+
+	return this.value.getType( builder );
+
+};
+
+THREE.FunctionCallNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+
+	var type = this.getType( builder );
+	var func = this.value;
+
+	builder.include( func );
+
+	var code = func.name + '(';
+	var params = [];
+
+	for ( var i = 0; i < func.input.length; i ++ ) {
+
+		var inpt = func.input[ i ];
+		var param = this.input[ i ] || this.input[ inpt.name ];
+
+		params.push( param.build( builder, builder.getType( inpt.type ) ) );
+
+	}
+
+	code += params.join( ',' ) + ')';
+
+	return builder.format( code, type, output );
+
+};

+ 159 - 0
examples/js/nodes/FunctionNode.js

@@ -0,0 +1,159 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ * @thanks bhouston / https://clara.io/
+ */
+
+THREE.FunctionNode = function( src, includes, extensions ) {
+
+	THREE.GLNode.call( this );
+
+	this.parse( src || '', includes, extensions );
+
+};
+
+THREE.FunctionNode.prototype = Object.create( THREE.GLNode.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;
+	}
+
+	return name;
+
+};
+
+THREE.FunctionNode.prototype.getTypeNode = function( builder, type ) {
+
+	return builder.getType( type ) || type;
+
+};
+
+THREE.FunctionNode.prototype.getInputByName = function( name ) {
+
+	var i = this.input.length;
+
+	while ( i -- ) {
+
+		if ( this.input[ i ].name === name )
+			return this.input[ i ];
+
+	}
+
+};
+
+THREE.FunctionNode.prototype.getType = function( builder ) {
+
+	return this.getTypeNode( builder, this.type );
+
+};
+
+THREE.FunctionNode.prototype.getInclude = function( name ) {
+
+	var i = this.includes.length;
+
+	while ( i -- ) {
+
+		if ( this.includes[ i ].name === name )
+			return this.includes[ i ];
+
+	}
+
+	return undefined;
+
+};
+
+THREE.FunctionNode.prototype.parse = function( src, includes, extensions ) {
+
+	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
+	var rProperties = /[a-z_0-9]+/ig;
+
+	this.includes = includes || [];
+	this.extensions = extensions || {};
+
+	var match = src.match( rDeclaration );
+
+	this.input = [];
+
+	if ( match && match.length == 4 ) {
+
+		this.type = match[ 1 ];
+		this.name = match[ 2 ];
+
+		var inputs = match[ 3 ].match( 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.input.push( {
+					name : name,
+					type : type,
+					qualifier : qualifier
+				} );
+
+			}
+
+		}
+
+		var match, offset = 0;
+
+		while ( match = rProperties.exec( src ) ) {
+
+			var prop = match[ 0 ];
+			var reference = this.parseReference( prop );
+
+			if ( prop != reference ) {
+
+				src = src.substring( 0, match.index + offset ) + reference + src.substring( match.index + prop.length + offset );
+
+				offset += reference.length - prop.length;
+
+			}
+
+			if ( this.getInclude( reference ) === undefined && THREE.LibNode.contains( reference ) ) {
+
+				this.includes.push( THREE.LibNode.get( reference ) );
+
+			}
+
+		}
+
+		this.src = src;
+
+	}
+	else {
+
+		this.type = '';
+		this.name = '';
+
+	}
+
+};

+ 98 - 0
examples/js/nodes/GLNode.js

@@ -0,0 +1,98 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.GLNode = function( type ) {
+
+	this.uuid = THREE.Math.generateUUID();
+
+	this.allow = {};
+	this.requestUpdate = false;
+
+	this.type = type;
+
+};
+
+THREE.GLNode.prototype.verify = function( builder ) {
+
+	builder.isVerify = true;
+
+	var material = builder.material;
+
+	this.build( builder, 'v4' );
+
+	material.clearVertexNode();
+	material.clearFragmentNode();
+
+	builder.setCache(); // reset cache
+
+	builder.isVerify = false;
+
+};
+
+THREE.GLNode.prototype.verifyAndBuildCode = function( builder, output, cache ) {
+
+	this.verify( builder.setCache( cache ) );
+
+	return this.buildCode( builder.setCache( cache ), output );
+
+};
+
+THREE.GLNode.prototype.buildCode = function( builder, output, uuid ) {
+
+	var material = builder.material;
+	var data = { result : this.build( builder, output, uuid ) };
+
+	if ( builder.isShader( 'vertex' ) ) data.code = material.clearVertexNode();
+	else data.code = material.clearFragmentNode();
+
+	builder.setCache(); // reset cache
+
+	return data;
+
+};
+
+THREE.GLNode.prototype.verifyDepsNode = function( builder, data, output ) {
+
+	data.deps = ( data.deps || 0 ) + 1;
+
+	var outputLen = builder.getFormatLength( output );
+
+	if ( outputLen > data.outputMax || this.getType( builder ) ) {
+
+		data.outputMax = outputLen;
+		data.output = output;
+
+	}
+
+};
+
+THREE.GLNode.prototype.build = function( builder, output, uuid ) {
+
+	var material = builder.material;
+	var data = material.getDataNode( uuid || this.uuid );
+
+	if ( builder.isShader( 'verify' ) ) this.verifyDepsNode( builder, data, output );
+
+	if ( this.allow[ builder.shader ] === false ) {
+
+		throw new Error( 'Shader ' + shader + ' is not compatible with this node.' );
+
+	}
+
+	if ( this.requestUpdate && ! data.requestUpdate ) {
+
+		material.requestUpdate.push( this );
+		data.requestUpdate = true;
+
+	}
+
+	return this.generate( builder, output, uuid );
+
+};
+
+THREE.GLNode.prototype.getType = function( builder ) {
+
+	return this.type;
+
+};

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

@@ -0,0 +1,46 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.InputNode = function( type, params ) {
+
+	THREE.TempNode.call( this, type, params );
+
+};
+
+THREE.InputNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.InputNode.prototype.constructor = THREE.InputNode;
+
+THREE.InputNode.prototype.generate = function( builder, output, uuid, type, ns, needsUpdate ) {
+
+	var material = builder.material;
+
+	uuid = builder.getUuid( uuid || this.uuid );
+	type = type || this.type;
+
+	var data = material.getDataNode( uuid );
+
+	if ( builder.isShader( 'vertex' ) ) {
+
+		if ( ! data.vertex ) {
+
+			data.vertex = material.getVertexUniform( this.value, type, ns, needsUpdate );
+
+		}
+
+		return builder.format( data.vertex.name, type, output );
+
+	}
+	else {
+
+		if ( ! data.fragment ) {
+
+			data.fragment = material.getFragmentUniform( this.value, type, ns, needsUpdate );
+
+		}
+
+		return builder.format( data.fragment.name, type, output );
+
+	}
+
+};

+ 95 - 0
examples/js/nodes/LibNode.js

@@ -0,0 +1,95 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.LibNode = {
+	nodes: {},
+	add: function( node ) {
+
+		this.nodes[ node.name ] = node;
+
+	},
+	remove: function( node ) {
+
+		delete this.nodes[ node.name ];
+
+	},
+	get: function( name ) {
+
+		return this.nodes[ name ];
+
+	},
+	contains: function( name ) {
+
+		return this.nodes[ name ] != undefined;
+
+	}
+};
+
+//
+//	Luma
+//
+
+THREE.LibNode.add( new THREE.ConstNode( "vec3 LUMA = vec3(0.2125, 0.7154, 0.0721);" ) );
+
+//
+//	DepthColor
+//
+
+THREE.LibNode.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",
+
+	"return 1.0 - smoothstep( mNear, mFar, depth );",
+"}"
+].join( "\n" ) ) );
+
+//
+//	NormalMap
+//
+
+THREE.LibNode.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, float scale ) {",
+	"vec3 q0 = dFdx( eye_pos );",
+	"vec3 q1 = dFdy( eye_pos );",
+	"vec2 st0 = dFdx( mUv.st );",
+	"vec2 st1 = dFdy( mUv.st );",
+	"vec3 S = normalize( q0 * st1.t - q1 * st0.t );",
+	"vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
+	"vec3 N = normalize( surf_norm );",
+	"vec3 mapN = map * 2.0 - 1.0;",
+	"mapN.xy = scale * mapN.xy;",
+	"mat3 tsn = mat3( S, T, N );",
+	"return normalize( tsn * mapN );",
+"}"
+].join( "\n" ), null, { derivatives: true } ) );
+
+//
+//	Saturation
+//
+
+THREE.LibNode.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);",
+"}"
+].join( "\n" ) ) );
+
+//
+//	Luminance
+//
+
+THREE.LibNode.add( new THREE.FunctionNode( [
+// Algorithm from Chapter 10 of Graphics Shaders.
+"float luminance_rgb(vec3 rgb) {",
+	"return dot(rgb, LUMA);",
+"}"
+].join( "\n" ) ) );

+ 476 - 0
examples/js/nodes/NodeMaterial.js

@@ -0,0 +1,476 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeMaterial = function( vertex, fragment ) {
+
+	THREE.ShaderMaterial.call( this );
+
+	this.vertex = vertex || new THREE.RawNode( new THREE.PositionNode( THREE.PositionNode.PROJECTION ) );
+	this.fragment = fragment || new THREE.RawNode( new THREE.ColorNode( 0xFF0000 ) );
+
+};
+
+THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
+THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
+
+THREE.NodeMaterial.Type = {
+	t : 'sampler2D',
+	tc : 'samplerCube',
+	bv1 : 'bool',
+	iv1 : 'int',
+	fv1 : 'float',
+	c : 'vec3',
+	v2 : 'vec2',
+	v3 : 'vec3',
+	v4 : 'vec4'
+};
+
+THREE.NodeMaterial.GetShortcuts = function( prop, name ) {
+
+	return {
+		get: function() {
+
+			return this[ prop ][ name ];
+
+		},
+		set: function( val ) {
+
+			this[ prop ][ name ] = val;
+
+		}
+	};
+
+};
+
+THREE.NodeMaterial.Shortcuts = function( proto, prop, list ) {
+
+	var shortcuts = {};
+
+	for ( var i = 0; i < list.length; ++ i ) {
+
+		var name = list[ i ];
+
+		shortcuts[ name ] = this.GetShortcuts( prop, name );
+
+	}
+
+	Object.defineProperties( proto, shortcuts );
+
+};
+
+THREE.NodeMaterial.prototype.updateAnimation = function( delta ) {
+
+	for ( var i = 0; i < this.requestUpdate.length; ++ i ) {
+
+		this.requestUpdate[ i ].updateAnimation( delta );
+
+	}
+
+};
+
+THREE.NodeMaterial.prototype.build = function() {
+
+	var vertex, fragment;
+
+	this.defines = {};
+	this.uniforms = {};
+
+	this.nodeData = {};
+
+	this.vertexUniform = [];
+	this.fragmentUniform = [];
+
+	this.vertexTemps = [];
+	this.fragmentTemps = [];
+
+	this.uniformList = [];
+
+	this.consts = [];
+	this.functions = [];
+
+	this.requestUpdate = [];
+
+	this.requestAttrib = {
+		uv: [],
+		color: []
+	};
+
+	this.vertexPars = '';
+	this.fragmentPars = '';
+
+	this.vertexCode = '';
+	this.fragmentCode = '';
+
+	this.vertexNode = '';
+	this.fragmentNode = '';
+
+	var builder = new THREE.BuilderNode( this );
+
+	vertex = this.vertex.build( builder.setShader( 'vertex' ), 'v4' );
+	fragment = this.fragment.build( builder.setShader( 'fragment' ), 'v4' );
+
+	if ( this.requestAttrib.uv[ 0 ] ) {
+
+		this.addVertexPars( 'varying vec2 vUv;' );
+		this.addFragmentPars( 'varying vec2 vUv;' );
+
+		this.addVertexCode( 'vUv = uv;' );
+
+	}
+
+	if ( this.requestAttrib.uv[ 1 ] ) {
+
+		this.addVertexPars( 'varying vec2 vUv2; attribute vec2 uv2;' );
+		this.addFragmentPars( 'varying vec2 vUv2;' );
+
+		this.addVertexCode( 'vUv2 = uv2;' );
+
+	}
+
+	if ( this.requestAttrib.color[ 0 ] ) {
+
+		this.addVertexPars( 'varying vec4 vColor; attribute vec4 color;' );
+		this.addFragmentPars( 'varying vec4 vColor;' );
+
+		this.addVertexCode( 'vColor = color;' );
+
+	}
+
+	if ( this.requestAttrib.color[ 1 ] ) {
+
+		this.addVertexPars( 'varying vec4 vColor2; attribute vec4 color2;' );
+		this.addFragmentPars( 'varying vec4 vColor2;' );
+
+		this.addVertexCode( 'vColor2 = color2;' );
+
+	}
+
+	if ( this.requestAttrib.position ) {
+
+		this.addVertexPars( 'varying vec3 vPosition;' );
+		this.addFragmentPars( 'varying vec3 vPosition;' );
+
+		this.addVertexCode( 'vPosition = transformed;' );
+
+	}
+
+	if ( this.requestAttrib.worldPosition ) {
+
+		// for future update replace from the native "varying vec3 vWorldPosition" for optimization
+
+		this.addVertexPars( 'varying vec3 vWPosition;' );
+		this.addFragmentPars( 'varying vec3 vWPosition;' );
+
+		this.addVertexCode( 'vWPosition = worldPosition.xyz;' );
+
+	}
+
+	if ( this.requestAttrib.normal ) {
+
+		this.addVertexPars( 'varying vec3 vObjectNormal;' );
+		this.addFragmentPars( 'varying vec3 vObjectNormal;' );
+
+		this.addVertexCode( 'vObjectNormal = normal;' );
+
+	}
+
+	if ( this.requestAttrib.worldNormal ) {
+
+		this.addVertexPars( 'varying vec3 vWNormal;' );
+		this.addFragmentPars( 'varying vec3 vWNormal;' );
+
+		this.addVertexCode( 'vWNormal = ( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz;' );
+
+	}
+
+	this.lights = this.requestAttrib.light;
+	this.transparent = this.requestAttrib.transparent;
+
+	this.vertexShader = [
+		this.vertexPars,
+		this.getCodePars( this.vertexUniform, 'uniform' ),
+		this.getIncludes( this.consts[ 'vertex' ] ),
+		this.getIncludes( this.functions[ 'vertex' ] ),
+		'void main(){',
+		this.getCodePars( this.vertexTemps ),
+		vertex,
+		this.vertexCode,
+		'}'
+	].join( "\n" );
+
+	this.fragmentShader = [
+		this.fragmentPars,
+		this.getCodePars( this.fragmentUniform, 'uniform' ),
+		this.getIncludes( this.consts[ 'fragment' ] ),
+		this.getIncludes( this.functions[ 'fragment' ] ),
+		'void main(){',
+		this.getCodePars( this.fragmentTemps ),
+		this.fragmentCode,
+		fragment,
+		'}'
+	].join( "\n" );
+
+	this.needsUpdate = true;
+	this.dispose(); // force update
+
+	return this;
+
+};
+
+THREE.NodeMaterial.prototype.define = function( name, value ) {
+
+	this.defines[ name ] = value == undefined ? 1 : value;
+
+};
+
+THREE.NodeMaterial.prototype.isDefined = function( name ) {
+
+	return this.defines[ name ] != undefined;
+
+};
+
+THREE.NodeMaterial.prototype.mergeUniform = function( uniforms ) {
+
+	for ( var name in uniforms ) {
+
+		this.uniforms[ name ] = uniforms[ name ];
+
+	}
+
+};
+
+THREE.NodeMaterial.prototype.createUniform = function( value, type, ns, needsUpdate ) {
+
+	var index = this.uniformList.length;
+
+	var uniform = {
+		type : type,
+		value : value,
+		name : ns ? ns : 'nVu' + index,
+		needsUpdate : needsUpdate
+	};
+
+	this.uniformList.push( uniform );
+
+	return uniform;
+
+};
+
+THREE.NodeMaterial.prototype.getVertexTemp = function( uuid, type, ns ) {
+
+	if ( ! this.vertexTemps[ uuid ] ) {
+
+		var index = this.vertexTemps.length,
+			name = ns ? ns : 'nVt' + index,
+			data = { name : name, type : type };
+
+		this.vertexTemps.push( data );
+		this.vertexTemps[ uuid ] = data;
+
+	}
+
+	return this.vertexTemps[ uuid ];
+
+};
+
+THREE.NodeMaterial.prototype.getFragmentTemp = function( uuid, type, ns ) {
+
+	if ( ! this.fragmentTemps[ uuid ] ) {
+
+		var index = this.fragmentTemps.length,
+			name = ns ? ns : 'nVt' + index,
+			data = { name : name, type : type };
+
+		this.fragmentTemps.push( data );
+		this.fragmentTemps[ uuid ] = data;
+
+	}
+
+	return this.fragmentTemps[ uuid ];
+
+};
+
+THREE.NodeMaterial.prototype.getIncludes = function( incs ) {
+
+	function sortByPosition( a, b ) {
+
+		return b.deps - a.deps;
+
+	}
+
+	return function( incs ) {
+
+		if ( ! incs ) return '';
+
+		var code = '';
+		var incs = incs.sort( sortByPosition );
+
+		for ( var i = 0; i < incs.length; i ++ ) {
+
+			code += incs[ i ].node.src + '\n';
+
+		}
+
+		return code;
+
+	}
+
+}();
+
+THREE.NodeMaterial.prototype.addVertexPars = function( code ) {
+
+	this.vertexPars += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.addFragmentPars = function( code ) {
+
+	this.fragmentPars += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.addVertexCode = function( code ) {
+
+	this.vertexCode += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.addFragmentCode = function( code ) {
+
+	this.fragmentCode += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.addVertexNode = function( code ) {
+
+	this.vertexNode += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.clearVertexNode = function() {
+
+	var code = this.fragmentNode;
+
+	this.fragmentNode = '';
+
+	return code;
+
+};
+
+THREE.NodeMaterial.prototype.addFragmentNode = function( code ) {
+
+	this.fragmentNode += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.clearFragmentNode = function() {
+
+	var code = this.fragmentNode;
+
+	this.fragmentNode = '';
+
+	return code;
+
+};
+
+THREE.NodeMaterial.prototype.getCodePars = function( pars, prefix ) {
+
+	prefix = prefix || '';
+
+	var code = '';
+
+	for ( var i = 0, l = pars.length; i < l; ++ i ) {
+
+		var parsType = pars[ i ].type;
+		var parsName = pars[ i ].name;
+		var parsValue = pars[ i ].value;
+
+		if ( parsType == 't' && parsValue instanceof THREE.CubeTexture ) parsType = 'tc';
+
+		var type = THREE.NodeMaterial.Type[ parsType ];
+
+		if ( type == undefined ) throw new Error( "Node pars " + parsType + " not found." );
+
+		code += prefix + ' ' + type + ' ' + parsName + ';\n';
+
+	}
+
+	return code;
+
+};
+
+THREE.NodeMaterial.prototype.getVertexUniform = function( value, type, ns, needsUpdate ) {
+
+	var uniform = this.createUniform( value, type, ns, needsUpdate );
+
+	this.vertexUniform.push( uniform );
+	this.vertexUniform[ uniform.name ] = uniform;
+
+	this.uniforms[ uniform.name ] = uniform;
+
+	return uniform;
+
+};
+
+THREE.NodeMaterial.prototype.getFragmentUniform = function( value, type, ns, needsUpdate ) {
+
+	var uniform = this.createUniform( value, type, ns, needsUpdate );
+
+	this.fragmentUniform.push( uniform );
+	this.fragmentUniform[ uniform.name ] = uniform;
+
+	this.uniforms[ uniform.name ] = uniform;
+
+	return uniform;
+
+};
+
+THREE.NodeMaterial.prototype.getDataNode = function( uuid ) {
+
+	return this.nodeData[ uuid ] = this.nodeData[ uuid ] || {};
+
+};
+
+THREE.NodeMaterial.prototype.include = function( shader, node ) {
+
+	var includes;
+
+	node = typeof node === 'string' ? THREE.LibNode.get( node ) : node;
+
+	if ( node instanceof THREE.FunctionNode ) {
+
+		for ( var i = 0; i < node.includes.length; i ++ ) {
+
+			this.include( shader, node.includes[ i ] );
+
+		}
+
+		includes = this.functions[ shader ] = this.functions[ shader ] || [];
+
+	}
+	else if ( node instanceof THREE.ConstNode ) {
+
+		includes = this.consts[ shader ] = this.consts[ shader ] || [];
+
+	}
+
+	if ( includes[ node.name ] === undefined ) {
+
+		for ( var ext in node.extensions ) {
+
+			this.extensions[ ext ] = true;
+
+		}
+
+		includes[ node.name ] = {
+			node : node,
+			deps : 1
+		};
+
+		includes.push( includes[ node.name ] );
+
+	}
+	else ++ includes[ node.name ].deps;
+
+};

+ 37 - 0
examples/js/nodes/RawNode.js

@@ -0,0 +1,37 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.RawNode = function( value ) {
+
+	THREE.GLNode.call( this, 'v4' );
+
+	this.value = value;
+
+};
+
+THREE.RawNode.prototype = Object.create( THREE.GLNode.prototype );
+THREE.RawNode.prototype.constructor = THREE.RawNode;
+
+THREE.GLNode.prototype.generate = function( builder ) {
+
+	var material = builder.material;
+
+	var data = this.value.verifyAndBuildCode( builder, this.type );
+
+	var code = data.code + '\n';
+
+	if ( builder.shader == 'vertex' ) {
+
+		code += 'gl_Position = ' + data.result + ';';
+
+	}
+	else {
+
+		code += 'gl_FragColor = ' + data.result + ';';
+
+	}
+
+	return code;
+
+};

+ 124 - 0
examples/js/nodes/TempNode.js

@@ -0,0 +1,124 @@
+/**
+ * Automatic node cache
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.TempNode = function( type, params ) {
+
+	THREE.GLNode.call( this, type );
+
+	params = params || {};
+
+	this.shared = params.shared !== undefined ? params.shared : true;
+	this.unique = params.unique !== undefined ? params.unique : false;
+
+};
+
+THREE.TempNode.prototype = Object.create( THREE.GLNode.prototype );
+THREE.TempNode.prototype.constructor = THREE.TempNode;
+
+THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
+
+	var material = builder.material;
+
+	if ( this.isShared() ) {
+
+		var isUnique = this.isUnique();
+
+		if ( isUnique && this.constructor.uuid === undefined ) {
+
+			this.constructor.uuid = THREE.Math.generateUUID();
+
+		}
+
+		uuid = builder.getUuid( uuid || this.getUuid(), ! isUnique );
+
+		var data = material.getDataNode( uuid );
+
+		if ( builder.isShader( 'verify' ) ) {
+
+			if ( data.deps || 0 > 0 ) {
+
+				this.verifyDepsNode( builder, data, output );
+				return '';
+
+			}
+
+			return THREE.GLNode.prototype.build.call( this, builder, output, uuid );
+
+		}
+		else if ( data.deps == 1 ) {
+
+			return THREE.GLNode.prototype.build.call( this, builder, output, uuid );
+
+		}
+
+		var name = this.getTemp( builder, uuid );
+		var type = data.output || this.getType( builder );
+
+		if ( name ) {
+
+			return builder.format( name, type, output );
+
+		}
+		else {
+
+			name = THREE.TempNode.prototype.generate.call( this, builder, output, uuid, data.output, ns );
+
+			var code = this.generate( builder, type, uuid );
+
+			if ( builder.isShader( 'vertex' ) ) material.addVertexNode( name + '=' + code + ';' );
+			else material.addFragmentNode( name + '=' + code + ';' );
+
+			return builder.format( name, type, output );
+
+		}
+
+	}
+	else {
+
+		return builder.format( this.generate( builder, this.getType( builder ), uuid ), this.getType( builder ), output );
+
+	}
+
+};
+
+THREE.TempNode.prototype.isShared = function() {
+
+	return this.shared;
+
+};
+
+THREE.TempNode.prototype.isUnique = function() {
+
+	return this.unique;
+
+};
+
+THREE.TempNode.prototype.getUuid = function() {
+
+	return this.constructor.uuid || this.uuid;
+
+};
+
+THREE.TempNode.prototype.getTemp = function( builder, uuid ) {
+
+	uuid = uuid || this.uuid;
+
+	var material = builder.material;
+
+	if ( builder.isShader( 'vertex' ) && material.vertexTemps[ uuid ] ) return material.vertexTemps[ uuid ].name;
+	else if ( material.fragmentTemps[ uuid ] ) return material.fragmentTemps[ uuid ].name;
+
+};
+
+THREE.TempNode.prototype.generate = function( builder, output, uuid, type, ns ) {
+
+	if ( ! this.isShared() ) console.error( "THREE.TempNode is not shared!" );
+
+	uuid = uuid || this.uuid;
+
+	if ( builder.isShader( 'vertex' ) ) return builder.material.getVertexTemp( uuid, type || this.getType( builder ), ns ).name;
+	else return builder.material.getFragmentTemp( uuid, type || this.getType( builder ), ns ).name;
+
+};

+ 128 - 0
examples/js/nodes/accessors/CameraNode.js

@@ -0,0 +1,128 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.CameraNode = function( scope, camera ) {
+
+	THREE.TempNode.call( this, 'v3' );
+
+	this.setScope( scope || THREE.CameraNode.POSITION );
+	this.setCamera( camera );
+
+};
+
+THREE.CameraNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.CameraNode.prototype.constructor = THREE.CameraNode;
+
+THREE.CameraNode.POSITION = 'position';
+THREE.CameraNode.DEPTH = 'depth';
+
+THREE.CameraNode.prototype.setCamera = function( camera ) {
+
+	this.camera = camera;
+	this.requestUpdate = camera !== undefined;
+
+};
+
+THREE.CameraNode.prototype.setScope = function( scope ) {
+
+	switch ( this.scope ) {
+
+		case THREE.CameraNode.DEPTH:
+
+			delete this.near;
+			delete this.far;
+
+			break;
+
+	}
+
+	this.scope = scope;
+
+	switch ( scope ) {
+
+		case THREE.CameraNode.DEPTH:
+
+			this.near = new THREE.FloatNode( camera ? camera.near : 1 );
+			this.far = new THREE.FloatNode( camera ? camera.far : 1200 );
+
+			break;
+
+	}
+
+};
+
+THREE.CameraNode.prototype.getType = function( builder ) {
+
+	switch ( this.scope ) {
+		case THREE.CameraNode.DEPTH:
+			return 'fv1';
+	}
+
+	return this.type;
+
+};
+
+THREE.CameraNode.prototype.isUnique = function( builder ) {
+
+	switch ( this.scope ) {
+		case THREE.CameraNode.DEPTH:
+			return true;
+	}
+
+	return false;
+
+};
+
+THREE.CameraNode.prototype.isShared = function( builder ) {
+
+	switch ( this.scope ) {
+		case THREE.CameraNode.POSITION:
+			return false;
+	}
+
+	return true;
+
+};
+
+THREE.CameraNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+	var result;
+
+	switch ( this.scope ) {
+
+		case THREE.CameraNode.POSITION:
+
+			result = 'cameraPosition';
+
+			break;
+
+		case THREE.CameraNode.DEPTH:
+
+			builder.include( 'depthcolor' );
+
+			result = 'depthcolor(' + this.near.build( builder, 'fv1' ) + ',' + this.far.build( builder, 'fv1' ) + ')';
+
+			break;
+
+	}
+
+	return builder.format( result, this.getType( builder ), output );
+
+};
+
+THREE.CameraNode.prototype.updateAnimation = function( delta ) {
+
+	switch ( this.scope ) {
+
+		case THREE.CameraNode.DEPTH:
+
+			this.near.number = camera.near;
+			this.far.number = camera.far;
+
+			break;
+
+	}
+
+};

+ 31 - 0
examples/js/nodes/accessors/ColorsNode.js

@@ -0,0 +1,31 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.ColorsNode = function( index ) {
+
+	THREE.TempNode.call( this, 'v4', { share: false } );
+
+	this.index = index || 0;
+
+};
+
+THREE.ColorsNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.ColorsNode.prototype.constructor = THREE.ColorsNode;
+
+THREE.ColorsNode.vertexDict = [ 'color', 'color2' ];
+THREE.ColorsNode.fragmentDict = [ 'vColor', 'vColor2' ];
+
+THREE.ColorsNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+	var result;
+
+	material.requestAttrib.color[ this.index ] = true;
+
+	if ( builder.isShader( 'vertex' ) ) result = THREE.ColorsNode.vertexDict[ this.index ];
+	else result = THREE.ColorsNode.fragmentDict[ this.index ];
+
+	return builder.format( result, this.getType( builder ), output );
+
+};

+ 66 - 0
examples/js/nodes/accessors/NormalNode.js

@@ -0,0 +1,66 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NormalNode = function( scope ) {
+
+	THREE.TempNode.call( this, 'v3' );
+
+	this.scope = scope || THREE.NormalNode.LOCAL;
+
+};
+
+THREE.NormalNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.NormalNode.prototype.constructor = THREE.NormalNode;
+
+THREE.NormalNode.LOCAL = 'local';
+THREE.NormalNode.WORLD = 'world';
+THREE.NormalNode.VIEW = 'view';
+
+THREE.NormalNode.prototype.isShared = function( builder ) {
+
+	switch ( this.scope ) {
+		case THREE.NormalNode.WORLD:
+			return true;
+	}
+
+	return false;
+
+};
+
+THREE.NormalNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+	var result;
+
+	switch ( this.scope ) {
+
+		case THREE.NormalNode.LOCAL:
+
+			material.requestAttrib.normal = true;
+
+			if ( builder.isShader( 'vertex' ) ) result = 'normal';
+			else result = 'vObjectNormal';
+
+			break;
+
+		case THREE.NormalNode.WORLD:
+
+			material.requestAttrib.worldNormal = true;
+
+			if ( builder.isShader( 'vertex' ) ) result = '( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz';
+			else result = 'vWNormal';
+
+			break;
+
+		case THREE.NormalNode.VIEW:
+
+			result = 'vNormal';
+
+			break;
+
+	}
+
+	return builder.format( result, this.getType( builder ), output );
+
+};

+ 87 - 0
examples/js/nodes/accessors/PositionNode.js

@@ -0,0 +1,87 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.PositionNode = function( scope ) {
+
+	THREE.TempNode.call( this, 'v3' );
+
+	this.scope = scope || THREE.PositionNode.LOCAL;
+
+};
+
+THREE.PositionNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.PositionNode.prototype.constructor = THREE.PositionNode;
+
+THREE.PositionNode.LOCAL = 'local';
+THREE.PositionNode.WORLD = 'world';
+THREE.PositionNode.VIEW = 'view';
+THREE.PositionNode.PROJECTION = 'projection';
+
+THREE.PositionNode.prototype.getType = function( builder ) {
+
+	switch ( this.scope ) {
+		case THREE.PositionNode.PROJECTION:
+			return 'v4';
+	}
+
+	return this.type;
+
+};
+
+THREE.PositionNode.prototype.isShared = function( builder ) {
+
+	switch ( this.scope ) {
+		case THREE.PositionNode.LOCAL:
+		case THREE.PositionNode.WORLD:
+			return false;
+	}
+
+	return true;
+
+};
+
+THREE.PositionNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+	var result;
+
+	switch ( this.scope ) {
+
+		case THREE.PositionNode.LOCAL:
+
+			material.requestAttrib.position = true;
+
+			if ( builder.isShader( 'vertex' ) ) result = 'transformed';
+			else result = 'vPosition';
+
+		break;
+
+		case THREE.PositionNode.WORLD:
+
+			material.requestAttrib.worldPosition = true;
+
+			if ( builder.isShader( 'vertex' ) ) result = 'vWPosition';
+			else result = 'vWPosition';
+
+		break;
+
+		case THREE.PositionNode.VIEW:
+
+			if ( builder.isShader( 'vertex' ) ) result = '-mvPosition.xyz';
+			else result = 'vViewPosition';
+
+		break;
+
+		case THREE.PositionNode.PROJECTION:
+
+			if ( builder.isShader( 'vertex' ) ) result = '(projectionMatrix * modelViewMatrix * vec4( position, 1.0 ))';
+			else result = 'vec4( 0.0 )';
+
+		break;
+
+	}
+
+	return builder.format( result, this.getType( builder ), output );
+
+};

+ 39 - 0
examples/js/nodes/accessors/ReflectNode.js

@@ -0,0 +1,39 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.ReflectNode = function() {
+
+	THREE.TempNode.call( this, 'v3', { unique: true } );
+
+	this.worldPosition = new THREE.PositionNode( THREE.PositionNode.WORLD );
+
+};
+
+THREE.ReflectNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.ReflectNode.prototype.constructor = THREE.ReflectNode;
+
+THREE.ReflectNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		material.addFragmentNode( [
+			'vec3 cameraToVertex = normalize( ' + this.worldPosition.build( builder, 'v3' ) + ' - cameraPosition );',
+			'vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );',
+			'vec3 vReflect = reflect( cameraToVertex, worldNormal );'
+		].join( "\n" ) );
+
+		return builder.format( 'vReflect', this.type, output );
+
+	}
+	else {
+
+		console.warn( "THREE.ReflectNode is not compatible with " + builder.shader + " shader" );
+
+		return builder.format( 'vec3( 0.0 )', this.type, output );
+
+	}
+
+};

+ 31 - 0
examples/js/nodes/accessors/UVNode.js

@@ -0,0 +1,31 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.UVNode = function( index ) {
+
+	THREE.TempNode.call( this, 'v2', { shared: false } );
+
+	this.index = index || 0;
+
+};
+
+THREE.UVNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.UVNode.prototype.constructor = THREE.UVNode;
+
+THREE.UVNode.vertexDict = [ 'uv', 'uv2' ];
+THREE.UVNode.fragmentDict = [ 'vUv', 'vUv2' ];
+
+THREE.UVNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+	var result;
+
+	material.requestAttrib.uv[ this.index ] = true;
+
+	if ( builder.isShader( 'vertex' ) ) result = THREE.UVNode.vertexDict[ this.index ];
+	else result = THREE.UVNode.fragmentDict[ this.index ];
+
+	return builder.format( result, this.getType( builder ), output );
+
+};

+ 16 - 0
examples/js/nodes/inputs/ColorNode.js

@@ -0,0 +1,16 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.ColorNode = function( color ) {
+
+	THREE.InputNode.call( this, 'c', { share: false } );
+
+	this.value = new THREE.Color( color || 0 );
+
+};
+
+THREE.ColorNode.prototype = Object.create( THREE.InputNode.prototype );
+THREE.ColorNode.prototype.constructor = THREE.ColorNode;
+
+THREE.NodeMaterial.Shortcuts( THREE.ColorNode.prototype, 'value', [ 'r', 'g', 'b' ] );

+ 43 - 0
examples/js/nodes/inputs/CubeTextureNode.js

@@ -0,0 +1,43 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.CubeTextureNode = function( value, coord, bias ) {
+
+	THREE.InputNode.call( this, 'v4' );
+
+	this.value = value;
+	this.coord = coord || new THREE.ReflectNode();
+	this.bias = bias;
+
+};
+
+THREE.CubeTextureNode.prototype = Object.create( THREE.InputNode.prototype );
+THREE.CubeTextureNode.prototype.constructor = THREE.CubeTextureNode;
+
+THREE.CubeTextureNode.prototype.getTexture = function( builder, output ) {
+
+	return THREE.InputNode.prototype.generate.call( this, builder, output, this.value.uuid, 't' );
+
+};
+
+THREE.CubeTextureNode.prototype.generate = function( 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;;
+
+	if ( bias == undefined && builder.require.cubeTextureBias ) {
+
+		bias = builder.require.cubeTextureBias.build( builder, 'fv1' );
+
+	}
+
+	var code;
+
+	if ( bias ) code = 'textureCube(' + cubetex + ',' + coord + ',' + bias + ')';
+	else code = 'textureCube(' + cubetex + ',' + coord + ')';
+
+	return builder.format( code, this.type, output );
+
+};

+ 29 - 0
examples/js/nodes/inputs/FloatNode.js

@@ -0,0 +1,29 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.FloatNode = function( value ) {
+
+	THREE.InputNode.call( this, 'fv1', { share: false } );
+
+	this.value = [ value || 0 ];
+
+};
+
+THREE.FloatNode.prototype = Object.create( THREE.InputNode.prototype );
+THREE.FloatNode.prototype.constructor = THREE.FloatNode;
+
+Object.defineProperties( THREE.FloatNode.prototype, {
+	number: {
+		get: function() {
+
+			return this.value[ 0 ];
+
+		},
+		set: function( val ) {
+
+			this.value[ 0 ] = val;
+
+		}
+	}
+} );

+ 29 - 0
examples/js/nodes/inputs/IntNode.js

@@ -0,0 +1,29 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.IntNode = function( value ) {
+
+	THREE.InputNode.call( this, 'fv1', { share: false } );
+
+	this.value = [ Math.floor( value || 0 ) ];
+
+};
+
+THREE.IntNode.prototype = Object.create( THREE.InputNode.prototype );
+THREE.IntNode.prototype.constructor = THREE.IntNode;
+
+Object.defineProperties( THREE.IntNode.prototype, {
+	number: {
+		get: function() {
+
+			return this.value[ 0 ];
+
+		},
+		set: function( val ) {
+
+			this.value[ 0 ] = Math.floor( val );
+
+		}
+	}
+} );

+ 24 - 0
examples/js/nodes/inputs/ScreenNode.js

@@ -0,0 +1,24 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.ScreenNode = function( coord ) {
+
+	THREE.TextureNode.call( this, undefined, coord );
+
+};
+
+THREE.ScreenNode.prototype = Object.create( THREE.TextureNode.prototype );
+THREE.ScreenNode.prototype.constructor = THREE.ScreenNode;
+
+THREE.ScreenNode.prototype.isUnique = function() {
+
+	return true;
+
+};
+
+THREE.ScreenNode.prototype.getTexture = function( builder, output ) {
+
+	return THREE.InputNode.prototype.generate.call( this, builder, output, this.getUuid(), 't', 'renderTexture' );
+
+};

+ 37 - 0
examples/js/nodes/inputs/TextureNode.js

@@ -0,0 +1,37 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.TextureNode = function( value, coord, bias ) {
+
+	THREE.InputNode.call( this, 'v4' );
+
+	this.value = value;
+	this.coord = coord || new THREE.UVNode();
+	this.bias = bias;
+
+};
+
+THREE.TextureNode.prototype = Object.create( THREE.InputNode.prototype );
+THREE.TextureNode.prototype.constructor = THREE.TextureNode;
+
+THREE.TextureNode.prototype.getTexture = function( builder, output ) {
+
+	return THREE.InputNode.prototype.generate.call( this, builder, output, this.value.uuid, 't' );
+
+};
+
+THREE.TextureNode.prototype.generate = function( builder, output ) {
+
+	var tex = this.getTexture( builder, output );
+	var coord = this.coord.build( builder, 'v2' );
+	var bias = this.bias ? this.bias.build( builder, 'fv1' ) : undefined;
+
+	var code;
+
+	if ( bias ) code = 'texture2D(' + tex + ',' + coord + ',' + bias + ')';
+	else code = 'texture2D(' + tex + ',' + coord + ')';
+
+	return builder.format( code, this.type, output );
+
+};

+ 16 - 0
examples/js/nodes/inputs/Vector2Node.js

@@ -0,0 +1,16 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.Vector2Node = function( x, y ) {
+
+	THREE.InputNode.call( this, 'v2', { share: false } );
+
+	this.value = new THREE.Vector2( x, y );
+
+};
+
+THREE.Vector2Node.prototype = Object.create( THREE.InputNode.prototype );
+THREE.Vector2Node.prototype.constructor = THREE.Vector2Node;
+
+THREE.NodeMaterial.Shortcuts( THREE.Vector2Node.prototype, 'value', [ 'x', 'y' ] );

+ 17 - 0
examples/js/nodes/inputs/Vector3Node.js

@@ -0,0 +1,17 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.Vector3Node = function( x, y, z ) {
+
+	THREE.InputNode.call( this, 'v3', { share: false } );
+
+	this.type = 'v3';
+	this.value = new THREE.Vector3( x, y, z );
+
+};
+
+THREE.Vector3Node.prototype = Object.create( THREE.InputNode.prototype );
+THREE.Vector3Node.prototype.constructor = THREE.Vector3Node;
+
+THREE.NodeMaterial.Shortcuts( THREE.Vector3Node.prototype, 'value', [ 'x', 'y', 'z' ] );

+ 16 - 0
examples/js/nodes/inputs/Vector4Node.js

@@ -0,0 +1,16 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.Vector4Node = function( x, y, z, w ) {
+
+	THREE.InputNode.call( this, 'v4', { share: false } );
+
+	this.value = new THREE.Vector4( x, y, z, w );
+
+};
+
+THREE.Vector4Node.prototype = Object.create( THREE.InputNode.prototype );
+THREE.Vector4Node.prototype.constructor = THREE.Vector4Node;
+
+THREE.NodeMaterial.Shortcuts( THREE.Vector4Node.prototype, 'value', [ 'x', 'y', 'z', 'w' ] );

+ 274 - 0
examples/js/nodes/materials/PhongNode.js

@@ -0,0 +1,274 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.PhongNode = function() {
+
+	THREE.GLNode.call( this );
+
+	this.color = new THREE.ColorNode( 0xEEEEEE );
+	this.specular = new THREE.ColorNode( 0x111111 );
+	this.shininess = new THREE.FloatNode( 30 );
+
+};
+
+THREE.PhongNode.prototype = Object.create( THREE.GLNode.prototype );
+THREE.PhongNode.prototype.constructor = THREE.PhongNode;
+
+THREE.PhongNode.prototype.build = function( builder ) {
+
+	var material = builder.material;
+	var code;
+
+	material.define( 'PHONG' );
+	material.define( 'ALPHATEST', '0.0' );
+
+	material.requestAttrib.light = true;
+
+	if ( builder.isShader( 'vertex' ) ) {
+
+		var transform = this.transform ? this.transform.verifyAndBuildCode( builder, 'v3' ) : undefined;
+
+		material.mergeUniform( THREE.UniformsUtils.merge( [
+
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "lights" ],
+			THREE.UniformsLib[ "shadowmap" ]
+
+		] ) );
+
+		material.addVertexPars( [
+			"varying vec3 vViewPosition;",
+
+			"#ifndef FLAT_SHADED",
+
+			"	varying vec3 vNormal;",
+
+			"#endif",
+
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ]
+
+		].join( "\n" ) );
+
+		var output = [
+				THREE.ShaderChunk[ "beginnormal_vertex" ],
+				THREE.ShaderChunk[ "morphnormal_vertex" ],
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+				THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+			"#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED
+
+			"	vNormal = normalize( transformedNormal );",
+
+			"#endif",
+
+				THREE.ShaderChunk[ "begin_vertex" ]
+		];
+
+		if ( transform ) {
+
+			output.push( transform.code );
+			output.push( "transformed = " + transform.result + ";" );
+
+		}
+
+		output.push(
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "project_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+			"	vViewPosition = - mvPosition.xyz;",
+
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "lights_phong_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ]
+		);
+
+		code = output.join( "\n" );
+
+	}
+	else {
+
+		// verify all nodes to reuse generate codes
+
+		this.color.verify( builder );
+		this.specular.verify( builder );
+		this.shininess.verify( builder );
+
+		if ( this.alpha ) this.alpha.verify( builder );
+
+		if ( this.ao ) this.ao.verify( builder );
+		if ( this.ambient ) this.ambient.verify( builder );
+		if ( this.shadow ) this.shadow.verify( builder );
+		if ( this.emissive ) this.emissive.verify( builder );
+
+		if ( this.normal ) this.normal.verify( builder );
+		if ( this.normalScale && this.normal ) this.normalScale.verify( builder );
+
+		if ( this.environment ) this.environment.verify( builder );
+		if ( this.reflectivity && this.environment ) this.reflectivity.verify( builder );
+
+		// build code
+
+		var color = this.color.buildCode( builder, 'v4' );
+		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 ao = this.ao ? this.ao.buildCode( builder, 'c' ) : 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 normal = this.normal ? this.normal.buildCode( builder, 'v3' ) : undefined;
+		var normalScale = this.normalScale && this.normal ? this.normalScale.buildCode( builder, 'fv1' ) : undefined;
+
+		var environment = this.environment ? this.environment.buildCode( builder.setCache( 'env' ), 'c' ) : undefined;
+		var reflectivity = this.reflectivity && this.environment ? this.reflectivity.buildCode( builder, 'fv1' ) : undefined;
+
+		material.requestAttrib.transparent = alpha != undefined;
+
+		material.addFragmentPars( [
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "bsdfs" ],
+			THREE.ShaderChunk[ "lights_pars" ],
+			THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ]
+		].join( "\n" ) );
+
+		var output = [
+				// prevent undeclared normal
+				THREE.ShaderChunk[ "normal_fragment" ],
+
+				color.code,
+			"	vec4 diffuseColor = " + color.result + ";",
+			"	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
+
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+			specular.code,
+			"	vec3 specular = " + specular.result + ";",
+
+			shininess.code,
+			"	float shininess = max(0.0001," + shininess.result + ");",
+
+			"	float specularStrength = 1.0;" // Ignored in MaterialNode ( replace to specular )
+		];
+
+		if ( alpha ) {
+
+			output.push(
+				alpha.code,
+				'if ( ' + alpha.result + ' <= ALPHATEST ) discard;'
+			);
+
+		}
+
+		if ( normal ) {
+
+			builder.include( 'perturbNormal2Arb' );
+
+			output.push( normal.code );
+
+			if ( normalScale ) output.push( normalScale.code );
+
+			output.push(
+				'normal = perturbNormal2Arb(-vViewPosition,normal,' +
+				normal.result + ',' +
+				new THREE.UVNode().build( builder, 'v2' ) + ',' +
+				( normalScale ? normalScale.result : '1.0' ) + ');'
+			);
+
+		}
+
+		output.push(
+			THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+			// accumulation
+			THREE.ShaderChunk[ "lights_phong_fragment" ],
+			THREE.ShaderChunk[ "lights_template" ]
+		);
+
+		if ( ao ) {
+
+			output.push( ao.code );
+			output.push( "reflectedLight.indirectDiffuse *= " + ao.result + ";" );
+
+		}
+
+		if ( ambient ) {
+
+			output.push( ambient.code );
+			output.push( "reflectedLight.indirectDiffuse += " + ambient.result + ";" );
+
+		}
+
+		if ( shadow ) {
+
+			output.push( shadow.code );
+			output.push( "reflectedLight.directDiffuse *= " + shadow.result + ";" );
+			output.push( "reflectedLight.directSpecular *= " + shadow.result + ";" );
+
+		}
+
+		if ( emissive ) {
+
+			output.push( emissive.code );
+			output.push( "reflectedLight.directDiffuse += " + emissive.result + ";" );
+
+		}
+
+		output.push( "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;" );
+
+		if ( environment ) {
+
+			output.push( environment.code );
+
+			if ( reflectivity ) {
+
+				output.push( reflectivity.code );
+
+				output.push( "outgoingLight = mix(" + 'outgoingLight' + "," + environment.result + "," + reflectivity.result + ");" );
+
+			}
+			else {
+
+				output.push( "outgoingLight = " + environment.result + ";" );
+
+			}
+
+		}
+
+		output.push(
+			THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+			THREE.ShaderChunk[ "fog_fragment" ]
+		);
+
+		if ( alpha ) {
+
+			output.push( "gl_FragColor = vec4( outgoingLight, " + alpha.result + " );" );
+
+		}
+		else {
+
+			output.push( "gl_FragColor = vec4( outgoingLight, 1.0 );" );
+
+		}
+
+		code = output.join( "\n" );
+
+	}
+
+	return code;
+
+};

+ 17 - 0
examples/js/nodes/materials/PhongNodeMaterial.js

@@ -0,0 +1,17 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.PhongNodeMaterial = function() {
+
+	this.node = new THREE.PhongNode();
+
+	THREE.NodeMaterial.call( this, this.node, this.node );
+
+};
+
+THREE.PhongNodeMaterial.prototype = Object.create( THREE.NodeMaterial.prototype );
+THREE.PhongNodeMaterial.prototype.constructor = THREE.PhongNodeMaterial;
+
+THREE.NodeMaterial.Shortcuts( THREE.PhongNodeMaterial.prototype, 'node',
+[ 'color', 'alpha', 'specular', 'shininess', 'normal', 'normalScale', 'emissive', 'ambient', 'shadow', 'ao', 'environment', 'reflectivity', 'transform' ] );

+ 286 - 0
examples/js/nodes/materials/StandardNode.js

@@ -0,0 +1,286 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.StandardNode = function() {
+
+	THREE.GLNode.call( this );
+
+	this.color = new THREE.ColorNode( 0xEEEEEE );
+	this.roughness = new THREE.FloatNode( 0.5 );
+	this.metalness = new THREE.FloatNode( 0.5 );
+
+};
+
+THREE.StandardNode.prototype = Object.create( THREE.GLNode.prototype );
+THREE.StandardNode.prototype.constructor = THREE.StandardNode;
+
+THREE.StandardNode.prototype.build = function( builder ) {
+
+	var material = builder.material;
+	var code;
+
+	material.define( 'STANDARD' );
+	material.define( 'ALPHATEST', '0.0' );
+
+	material.requestAttrib.light = true;
+
+	if ( builder.isShader( 'vertex' ) ) {
+
+		var transform = this.transform ? this.transform.verifyAndBuildCode( builder, 'v3' ) : undefined;
+
+		material.mergeUniform( THREE.UniformsUtils.merge( [
+
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "lights" ],
+			THREE.UniformsLib[ "shadowmap" ]
+
+		] ) );
+
+		material.addVertexPars( [
+			"varying vec3 vViewPosition;",
+
+			"#ifndef FLAT_SHADED",
+
+			"	varying vec3 vNormal;",
+
+			"#endif",
+
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ]
+
+		].join( "\n" ) );
+
+		var output = [
+				THREE.ShaderChunk[ "beginnormal_vertex" ],
+				THREE.ShaderChunk[ "morphnormal_vertex" ],
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+				THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+			"#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED
+
+			"	vNormal = normalize( transformedNormal );",
+
+			"#endif",
+
+				THREE.ShaderChunk[ "begin_vertex" ]
+		];
+
+		if ( transform ) {
+
+			output.push( transform.code );
+			output.push( "transformed = " + transform.result + ";" );
+
+		}
+
+		output.push(
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "project_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+			"	vViewPosition = - mvPosition.xyz;",
+
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "lights_phong_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ]
+		);
+
+		code = output.join( "\n" );
+
+	}
+	else {
+
+		// CubeMap blur effect (PBR)
+
+		builder.require.cubeTextureBias = builder.require.cubeTextureBias || new THREE.RoughnessToBlinnExponentNode();
+
+		// verify all nodes to reuse generate codes
+
+		this.color.verify( builder );
+		this.roughness.verify( builder );
+		this.metalness.verify( builder );
+
+		if ( this.alpha ) this.alpha.verify( builder );
+
+		if ( this.ao ) this.ao.verify( builder );
+		if ( this.ambient ) this.ambient.verify( builder );
+		if ( this.shadow ) this.shadow.verify( builder );
+		if ( this.emissive ) this.emissive.verify( builder );
+
+		if ( this.normal ) this.normal.verify( builder );
+		if ( this.normalScale && this.normal ) this.normalScale.verify( builder );
+
+		if ( this.environment ) this.environment.verify( builder.setCache( 'env' ) ); // isolate environment from others inputs ( see TextureNode, CubeTextureNode )
+		if ( this.reflectivity && this.environment ) this.reflectivity.verify( builder );
+
+		// build code
+
+		var color = this.color.buildCode( builder, 'v4' );
+		var roughness = this.roughness.buildCode( builder, 'fv1' );
+		var metalness = this.metalness.buildCode( builder, 'fv1' );
+
+		var alpha = this.alpha ? this.alpha.buildCode( builder, 'fv1' ) : undefined;
+
+		var ao = this.ao ? this.ao.buildCode( builder, 'c' ) : 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 normal = this.normal ? this.normal.buildCode( builder, 'v3' ) : undefined;
+		var normalScale = this.normalScale && this.normal ? this.normalScale.buildCode( builder, 'fv1' ) : undefined;
+
+		var environment = this.environment ? this.environment.buildCode( builder.setCache( 'env' ), 'c' ) : undefined;
+		var reflectivity = this.reflectivity && this.environment ? this.reflectivity.buildCode( builder, 'fv1' ) : undefined;
+
+		material.requestAttrib.transparent = alpha != undefined;
+
+		material.addFragmentPars( [
+
+			"varying vec3 vViewPosition;",
+
+			"#ifndef FLAT_SHADED",
+
+			"	varying vec3 vNormal;",
+
+			"#endif",
+
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "bsdfs" ],
+			THREE.ShaderChunk[ "lights_pars" ],
+			THREE.ShaderChunk[ "lights_standard_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+		].join( "\n" ) );
+
+		var output = [
+				// prevent undeclared normal
+				THREE.ShaderChunk[ "normal_fragment" ],
+
+				// prevent undeclared material
+			"	StandardMaterial material;",
+
+				color.code,
+			"	vec4 diffuseColor = " + color.result + ";",
+			"	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
+
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+
+			roughness.code,
+			"	float roughnessFactor = " + roughness.result + ";",
+
+			metalness.code,
+			"	float metalnessFactor = " + metalness.result + ";"
+		];
+
+		if ( alpha ) {
+
+			output.push(
+				alpha.code,
+				'if ( ' + alpha.result + ' <= ALPHATEST ) discard;'
+			);
+
+		}
+
+		if ( normal ) {
+
+			builder.include( 'perturbNormal2Arb' );
+
+			output.push( normal.code );
+
+			if ( normalScale ) output.push( normalScale.code );
+
+			output.push(
+				'normal = perturbNormal2Arb(-vViewPosition,normal,' +
+				normal.result + ',' +
+				new THREE.UVNode().build( builder, 'v2' ) + ',' +
+				( normalScale ? normalScale.result : '1.0' ) + ');'
+			);
+
+		}
+
+		output.push(
+			THREE.ShaderChunk[ "shadowmap_fragment" ],
+
+			// accumulation
+			'material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );',
+			'material.specularRoughness = clamp( roughnessFactor, 0.001, 1.0 );', // disney's remapping of [ 0, 1 ] roughness to [ 0.001, 1 ]
+			'material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );',
+
+			THREE.ShaderChunk[ "lights_template" ]
+		);
+
+		if ( ao ) {
+
+			output.push( ao.code );
+			output.push( "reflectedLight.indirectDiffuse *= " + ao.result + ";" );
+
+		}
+
+		if ( ambient ) {
+
+			output.push( ambient.code );
+			output.push( "reflectedLight.indirectDiffuse += " + ambient.result + ";" );
+
+		}
+
+		if ( shadow ) {
+
+			output.push( shadow.code );
+			output.push( "reflectedLight.directDiffuse *= " + shadow.result + ";" );
+			output.push( "reflectedLight.directSpecular *= " + shadow.result + ";" );
+
+		}
+
+		if ( emissive ) {
+
+			output.push( emissive.code );
+			output.push( "reflectedLight.directDiffuse += " + emissive.result + ";" );
+
+		}
+
+		if ( environment ) {
+
+			output.push( environment.code );
+			output.push( "RE_IndirectSpecular(" + environment.result + ", geometry, material, reflectedLight );" );
+
+		}
+
+		if ( reflectivity ) {
+
+			output.push( reflectivity.code );
+			output.push( "reflectedLight.indirectSpecular *= " + reflectivity.result + ";" );
+
+		}
+
+		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 + " );" );
+
+		}
+		else {
+
+			output.push( "gl_FragColor = vec4( outgoingLight, 1.0 );" );
+
+		}
+
+		code = output.join( "\n" );
+
+	}
+
+	return code;
+
+};

+ 17 - 0
examples/js/nodes/materials/StandardNodeMaterial.js

@@ -0,0 +1,17 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.StandardNodeMaterial = function() {
+
+	this.node = new THREE.StandardNode();
+
+	THREE.NodeMaterial.call( this, this.node, this.node );
+
+};
+
+THREE.StandardNodeMaterial.prototype = Object.create( THREE.NodeMaterial.prototype );
+THREE.StandardNodeMaterial.prototype.constructor = THREE.StandardNodeMaterial;
+
+THREE.NodeMaterial.Shortcuts( THREE.StandardNodeMaterial.prototype, 'node',
+[ 'color', 'alpha', 'roughness', 'metalness', 'normal', 'normalScale', 'emissive', 'ambient', 'shadow', 'ao', 'environment', 'reflectivity', 'transform' ] );

+ 78 - 0
examples/js/nodes/math/Math1Node.js

@@ -0,0 +1,78 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.Math1Node = function( a, method ) {
+
+	THREE.TempNode.call( this );
+
+	this.a = a;
+
+	this.method = method || THREE.Math1Node.SIN;
+
+};
+
+THREE.Math1Node.prototype = Object.create( THREE.TempNode.prototype );
+THREE.Math1Node.prototype.constructor = THREE.Math1Node;
+
+THREE.Math1Node.RAD = 'radians';
+THREE.Math1Node.DEG = 'degrees';
+THREE.Math1Node.EXP = 'exp';
+THREE.Math1Node.EXP2 = 'exp2';
+THREE.Math1Node.LOG = 'log';
+THREE.Math1Node.LOG2 = 'log2';
+THREE.Math1Node.INVERSE_SQRT = 'inversesqrt';
+THREE.Math1Node.FLOOR = 'floor';
+THREE.Math1Node.CEIL = 'ceil';
+THREE.Math1Node.NORMALIZE = 'normalize';
+THREE.Math1Node.FRACT = 'fract';
+THREE.Math1Node.SAT = 'saturate';
+THREE.Math1Node.SIN = 'sin';
+THREE.Math1Node.COS = 'cos';
+THREE.Math1Node.TAN = 'tan';
+THREE.Math1Node.ASIN = 'asin';
+THREE.Math1Node.ACOS = 'acos';
+THREE.Math1Node.ARCTAN = 'atan';
+THREE.Math1Node.ABS = 'abc';
+THREE.Math1Node.SIGN = 'sign';
+THREE.Math1Node.LENGTH = 'length';
+THREE.Math1Node.NEGATE = 'negate';
+THREE.Math1Node.INVERT = 'invert';
+
+THREE.Math1Node.prototype.getType = function( builder ) {
+
+	switch ( this.method ) {
+		case THREE.Math1Node.DISTANCE:
+			return 'fv1';
+	}
+
+	return this.a.getType( builder );
+
+};
+
+THREE.Math1Node.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+
+	var type = this.getType( builder );
+
+	var result = this.a.build( builder, type );
+
+	switch ( this.method ) {
+
+		case THREE.Math1Node.NEGATE:
+			result = '(-' + result + ')';
+			break;
+
+		case THREE.Math1Node.INVERT:
+			result = '(1.0-' + result + ')';
+			break;
+
+		default:
+			result = this.method + '(' + result + ')';
+			break;
+	}
+
+	return builder.format( result, type, output );
+
+};

+ 96 - 0
examples/js/nodes/math/Math2Node.js

@@ -0,0 +1,96 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.Math2Node = function( a, b, method ) {
+
+	THREE.TempNode.call( this );
+
+	this.a = a;
+	this.b = b;
+
+	this.method = method || THREE.Math2Node.DISTANCE;
+
+};
+
+THREE.Math2Node.prototype = Object.create( THREE.TempNode.prototype );
+THREE.Math2Node.prototype.constructor = THREE.Math2Node;
+
+THREE.Math2Node.MIN = 'min';
+THREE.Math2Node.MAX = 'max';
+THREE.Math2Node.MOD = 'mod';
+THREE.Math2Node.STEP = 'step';
+THREE.Math2Node.REFLECT = 'reflect';
+THREE.Math2Node.DISTANCE = 'distance';
+THREE.Math2Node.DOT = 'dot';
+THREE.Math2Node.CROSS = 'cross';
+THREE.Math2Node.POW = 'pow';
+
+THREE.Math2Node.prototype.getInputType = function( builder ) {
+
+	// use the greater length vector
+	if ( builder.getFormatLength( this.b.getType( builder ) ) > builder.getFormatLength( this.a.getType( builder ) ) ) {
+
+		return this.b.getType( builder );
+
+	}
+
+	return this.a.getType( builder );
+
+};
+
+THREE.Math2Node.prototype.getType = function( builder ) {
+
+	switch ( this.method ) {
+		case THREE.Math2Node.DISTANCE:
+		case THREE.Math2Node.DOT:
+			return 'fv1';
+
+		case THREE.Math2Node.CROSS:
+			return 'v3';
+	}
+
+	return this.getInputType( builder );
+
+};
+
+THREE.Math2Node.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+
+	var type = this.getInputType( builder );
+
+	var a, b,
+		al = builder.getFormatLength( this.a.getType( builder ) ),
+		bl = builder.getFormatLength( this.b.getType( builder ) );
+
+	// optimzer
+
+	switch ( this.method ) {
+		case THREE.Math2Node.CROSS:
+			a = this.a.build( builder, 'v3' );
+			b = this.b.build( builder, 'v3' );
+			break;
+
+		case THREE.Math2Node.STEP:
+			a = this.a.build( builder, al == 1 ? 'fv1' : type );
+			b = this.b.build( builder, type );
+			break;
+
+		case THREE.Math2Node.MIN:
+		case THREE.Math2Node.MAX:
+		case THREE.Math2Node.MODULO:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, bl == 1 ? 'fv1' : type );
+			break;
+
+		default:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, type );
+			break;
+
+	}
+
+	return builder.format( this.method + '(' + a + ',' + b + ')', this.getType( builder ), output );
+
+};

+ 84 - 0
examples/js/nodes/math/Math3Node.js

@@ -0,0 +1,84 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.Math3Node = function( a, b, c, method ) {
+
+	THREE.TempNode.call( this );
+
+	this.a = a;
+	this.b = b;
+	this.c = c;
+
+	this.method = method || THREE.Math3Node.MIX;
+
+};
+
+THREE.Math3Node.prototype = Object.create( THREE.TempNode.prototype );
+THREE.Math3Node.prototype.constructor = THREE.Math3Node;
+
+THREE.Math3Node.MIX = 'mix';
+THREE.Math3Node.REFRACT = 'refract';
+THREE.Math3Node.SMOOTHSTEP = 'smoothstep';
+THREE.Math3Node.FACEFORWARD = 'faceforward';
+
+THREE.Math3Node.prototype.getType = function( builder ) {
+
+	var a = builder.getFormatLength( this.a.getType( builder ) );
+	var b = builder.getFormatLength( this.b.getType( builder ) );
+	var c = builder.getFormatLength( this.c.getType( builder ) );
+
+	if ( a > b ) {
+
+		if ( a > c ) return this.a.getType( builder );
+		return this.c.getType( builder );
+
+	}
+	else {
+
+		if ( b > c ) return this.b.getType( builder );
+
+		return this.c.getType( builder );
+
+	}
+
+};
+
+THREE.Math3Node.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+
+	var type = this.getType( builder );
+
+	var a, b, c,
+		al = builder.getFormatLength( this.a.getType( builder ) ),
+		bl = builder.getFormatLength( this.b.getType( builder ) ),
+		cl = builder.getFormatLength( this.c.getType( builder ) )
+
+	// optimzer
+
+	switch ( this.method ) {
+		case THREE.Math3Node.REFRACT:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, type );
+			c = this.c.build( builder, 'fv1' );
+		break;
+
+		case THREE.Math3Node.MIX:
+		case THREE.Math3Node.SMOOTHSTEP:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, type );
+			c = this.c.build( builder, cl == 1 ? 'fv1' : type );
+		break;
+
+		default:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, type );
+			c = this.c.build( builder, type );
+		break;
+
+	}
+
+	return builder.format( this.method + '(' + a + ',' + b + ',' + c + ')', type, output );
+
+};

+ 47 - 0
examples/js/nodes/math/OperatorNode.js

@@ -0,0 +1,47 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.OperatorNode = function( a, b, op ) {
+
+	THREE.TempNode.call( this );
+
+	this.op = op || THREE.OperatorNode.ADD;
+
+	this.a = a;
+	this.b = b;
+
+};
+
+THREE.OperatorNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.OperatorNode.prototype.constructor = THREE.OperatorNode;
+
+THREE.OperatorNode.ADD = '+';
+THREE.OperatorNode.SUB = '-';
+THREE.OperatorNode.MUL = '*';
+THREE.OperatorNode.DIV = '/';
+
+THREE.OperatorNode.prototype.getType = function( builder ) {
+
+	// use the greater length vector
+	if ( builder.getFormatLength( this.b.getType( builder ) ) > builder.getFormatLength( this.a.getType( builder ) ) ) {
+
+		return this.b.getType( builder );
+
+	}
+
+	return this.a.getType( builder );
+
+};
+
+THREE.OperatorNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+	var data = material.getDataNode( this.uuid );
+
+	var a = this.a.build( builder, output );
+	var b = this.b.build( builder, output );
+
+	return '(' + a + this.op + b + ')';
+
+};

+ 33 - 0
examples/js/nodes/postprocessing/NodePass.js

@@ -0,0 +1,33 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodePass = function() {
+
+	THREE.ShaderPass.call( this );
+
+	this.fragment = new THREE.RawNode( new THREE.ScreenNode() );
+
+	this.node = new THREE.NodeMaterial();
+	this.node.fragment = this.fragment;
+
+	this.build();
+
+	this.textureID = 'renderTexture';
+
+};
+
+THREE.NodePass.prototype = Object.create( THREE.ShaderPass.prototype );
+THREE.NodePass.prototype.constructor = THREE.NodePass;
+
+THREE.NodePass.prototype.build = function() {
+
+	this.node.build();
+
+	this.uniforms = this.node.uniforms;
+	this.material = this.node;
+
+};
+
+THREE.NodeMaterial.Shortcuts( THREE.NodePass.prototype, 'fragment',
+[ 'value' ] );

+ 69 - 0
examples/js/nodes/utils/JoinNode.js

@@ -0,0 +1,69 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.JoinNode = function( x, y, z, w ) {
+
+	THREE.GLNode.call( this, 'fv1' );
+
+	this.x = x;
+	this.y = y;
+	this.z = z;
+	this.w = w;
+
+};
+
+THREE.JoinNode.prototype = Object.create( THREE.GLNode.prototype );
+THREE.JoinNode.prototype.constructor = THREE.JoinNode;
+
+THREE.JoinNode.inputs = [ 'x', 'y', 'z', 'w' ];
+
+THREE.JoinNode.prototype.getNumElements = function() {
+
+	var inputs = THREE.JoinNode.inputs;
+	var i = inputs.length;
+
+	while ( i -- ) {
+
+		if ( this[ inputs[ i ] ] !== undefined ) {
+
+			++ i;
+			break;
+
+		}
+
+	}
+
+	return Math.max( i, 2 );
+
+};
+
+THREE.JoinNode.prototype.getType = function( builder ) {
+
+	return builder.getFormatByLength( this.getNumElements() );
+
+};
+
+THREE.JoinNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+
+	var type = this.getType( builder );
+	var length = this.getNumElements();
+
+	var inputs = THREE.JoinNode.inputs;
+	var outputs = [];
+
+	for ( var i = 0; i < length; i ++ ) {
+
+		var elm = this[ inputs[ i ]];
+
+		outputs.push( elm ? elm.build( builder, 'fv1' ) : '0.' );
+
+	}
+
+	var code = builder.getFormatConstructor( length ) + '(' + outputs.join( ',' ) + ')';
+
+	return builder.format( code, type, output );
+
+};

+ 43 - 0
examples/js/nodes/utils/NormalMapNode.js

@@ -0,0 +1,43 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NormalMapNode = function( value, uv, scale, normal, position ) {
+
+	THREE.TempNode.call( this, 'v3' );
+
+	this.value = value;
+	this.scale = scale || new THREE.FloatNode( 1 );
+
+	this.normal = normal || new THREE.NormalNode( THREE.NormalNode.LOCAL );
+	this.position = position || new THREE.PositionNode( THREE.NormalNode.VIEW );
+
+};
+
+THREE.NormalMapNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.NormalMapNode.prototype.constructor = THREE.NormalMapNode;
+
+THREE.NormalMapNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+
+	builder.include( 'perturbNormal2Arb' );
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		return builder.format( 'perturbNormal2Arb(-' + this.position.build( builder, 'v3' ) + ',' +
+			this.normal.build( builder, 'v3' ) + ',' +
+			this.value.build( builder, 'v3' ) + ',' +
+			this.value.coord.build( builder, 'v2' ) + ',' +
+			this.scale.build( builder, 'fv1' ) + ')', this.type, output );
+
+	}
+	else {
+
+		console.warn( "THREE.NormalMap is not compatible with " + builder.shader + " shader" );
+
+		return builder.format( 'vec3( 0.0 )', this.type, output );
+
+	}
+
+};

+ 44 - 0
examples/js/nodes/utils/RoughnessToBlinnExponentNode.js

@@ -0,0 +1,44 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.RoughnessToBlinnExponentNode = function() {
+
+	THREE.TempNode.call( this, 'fv1', { unique: true } );
+
+};
+
+THREE.RoughnessToBlinnExponentNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.RoughnessToBlinnExponentNode.prototype.constructor = THREE.RoughnessToBlinnExponentNode;
+
+THREE.RoughnessToBlinnExponentNode.prototype.generate = function( builder, output ) {
+
+	var material = builder.material;
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		if ( material.isDefined( 'STANDARD' ) ) {
+
+			material.addFragmentNode( 'float specularMIPLevel = GGXRoughnessToBlinnExponent( 1.0 - material.specularRoughness );' );
+
+		}
+		else {
+
+			console.warn( "THREE.RoughnessToBlinnExponentNode is compatible with StandardMaterial only" );
+
+			material.addFragmentNode( 'float specularMIPLevel = 0.0;' );
+
+		}
+
+		return builder.format( 'specularMIPLevel', this.type, output );
+
+	}
+	else {
+
+		console.warn( "THREE.RoughnessToBlinnExponentNode is not compatible with " + builder.shader + " shader" );
+
+		return builder.format( '0.0', this.type, output );
+
+	}
+
+};

+ 68 - 0
examples/js/nodes/utils/SwitchNode.js

@@ -0,0 +1,68 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.SwitchNode = function( a, component ) {
+
+	THREE.GLNode.call( this, 'fv1' );
+
+	this.component = component || 'x';
+
+	this.a = a;
+
+};
+
+THREE.SwitchNode.prototype = Object.create( THREE.GLNode.prototype );
+THREE.SwitchNode.prototype.constructor = THREE.SwitchNode;
+
+THREE.SwitchNode.prototype.getType = function( builder ) {
+
+	return builder.getFormatByLength( this.component.length );
+
+};
+
+THREE.SwitchNode.prototype.generate = function( builder, output ) {
+
+	var type = this.a.getType( builder );
+	var inputLength = builder.getFormatLength( type ) - 1;
+
+	var a = this.a.build( builder, type );
+
+	var outputLength = 0;
+
+	var i, len = this.component.length;
+
+	// get max length
+
+	for ( i = 0; i < len; i ++ ) {
+
+		outputLength = Math.max( outputLength, builder.getElementIndex( this.component.charAt( i ) ) );
+
+	}
+
+	if ( outputLength > inputLength ) outputLength = inputLength;
+
+	// build switch
+
+	a += '.';
+
+	for ( i = 0; i < len; i ++ ) {
+
+		var elm = this.component.charAt( i );
+		var idx = builder.getElementIndex( this.component.charAt( i ) );
+
+		if ( idx > outputLength ) idx = outputLength;
+
+		if ( builder.getElementByIndex( idx ) == undefined ) {
+
+			console.log( builder.getElementByIndex( idx ) );
+
+		}
+
+		a += builder.getElementByIndex( idx );
+
+	}
+
+	return builder.format( a, this.getType( builder ), output );
+
+};

+ 21 - 0
examples/js/nodes/utils/TimeNode.js

@@ -0,0 +1,21 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.TimeNode = function( value ) {
+
+	THREE.FloatNode.call( this, value );
+
+	this.requestUpdate = true;
+	this.scale = 1;
+
+};
+
+THREE.TimeNode.prototype = Object.create( THREE.FloatNode.prototype );
+THREE.TimeNode.prototype.constructor = THREE.TimeNode;
+
+THREE.TimeNode.prototype.updateAnimation = function( delta ) {
+
+	this.number += delta * this.scale;
+
+};

+ 59 - 0
examples/js/nodes/utils/VelocityNode.js

@@ -0,0 +1,59 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.VelocityNode = function( target, params ) {
+
+	THREE.Vector3Node.call( this );
+
+	this.requestUpdate = true;
+
+	this.target = target;
+
+	this.position = this.target.position.clone();
+	this.velocity = new THREE.Vector3();
+	this.moment = new THREE.Vector3();
+
+	this.params = params || {};
+
+};
+
+THREE.VelocityNode.prototype = Object.create( THREE.Vector3Node.prototype );
+THREE.VelocityNode.prototype.constructor = THREE.VelocityNode;
+
+THREE.VelocityNode.prototype.updateAnimation = function( delta ) {
+
+	this.velocity.subVectors( this.target.position, this.position );
+	this.position.copy( this.target.position );
+
+	switch ( this.params.type ) {
+
+		case "elastic":
+
+			delta *= this.params.fps || 60;
+
+			var spring = Math.pow( this.params.spring, delta );
+			var friction = Math.pow( this.params.friction, delta );
+
+			// spring
+			this.moment.x += this.velocity.x * spring;
+			this.moment.y += this.velocity.y * spring;
+			this.moment.z += this.velocity.z * spring;
+
+			// friction
+			this.moment.x *= friction;
+			this.moment.y *= friction;
+			this.moment.z *= friction;
+
+			this.value.copy( this.moment );
+
+			break;
+
+		default:
+
+			this.value.copy( this.velocity );
+
+			break;
+	}
+
+};

+ 21 - 10
examples/js/postprocessing/ShaderPass.js

@@ -2,20 +2,31 @@
  * @author alteredq / http://alteredqualia.com/
  */
 
-THREE.ShaderPass = function ( shader, textureID ) {
+THREE.ShaderPass = function( shader, textureID ) {
 
 	this.textureID = ( textureID !== undefined ) ? textureID : "tDiffuse";
 
-	this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+	if ( shader instanceof THREE.ShaderMaterial ) {
 
-	this.material = new THREE.ShaderMaterial( {
+		this.uniforms = shader.uniforms;
 
-		defines: shader.defines || {},
-		uniforms: this.uniforms,
-		vertexShader: shader.vertexShader,
-		fragmentShader: shader.fragmentShader
+		this.material = shader;
 
-	} );
+	}
+	else if ( shader ) {
+
+		this.uniforms = THREE.UniformsUtils.clone( shader.uniforms );
+
+		this.material = new THREE.ShaderMaterial( {
+
+			defines: shader.defines || {},
+			uniforms: this.uniforms,
+			vertexShader: shader.vertexShader,
+			fragmentShader: shader.fragmentShader
+
+		} );
+
+	}
 
 	this.renderToScreen = false;
 
@@ -25,7 +36,7 @@ THREE.ShaderPass = function ( shader, textureID ) {
 
 
 	this.camera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
-	this.scene  = new THREE.Scene();
+	this.scene = new THREE.Scene();
 
 	this.quad = new THREE.Mesh( new THREE.PlaneBufferGeometry( 2, 2 ), null );
 	this.scene.add( this.quad );
@@ -34,7 +45,7 @@ THREE.ShaderPass = function ( shader, textureID ) {
 
 THREE.ShaderPass.prototype = {
 
-	render: function ( renderer, writeBuffer, readBuffer, delta ) {
+	render: function( renderer, writeBuffer, readBuffer, delta ) {
 
 		if ( this.uniforms[ this.textureID ] ) {
 

+ 146 - 0
examples/materials/nodes/NodeBuilder.js

@@ -0,0 +1,146 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeBuilder = function( material ) {
+	
+	this.material = material;
+	
+	this.require = {};
+	this.isVerify = false;
+	this.cache = '';
+	
+};
+
+THREE.NodeBuilder.prototype = {
+	constructor: THREE.NodeBuilder,
+
+	include : function ( func ) {
+		
+		this.material.include( this.shader, func );
+
+	},
+	
+	getFormatConstructor : function ( len ) {
+		
+		return THREE.NodeBuilder.constructors[len-1];
+	
+	},
+	
+	getFormat : function ( format ) {
+		
+		return format.replace('c','v3').replace(/fv1|iv1/, 'v1');
+	
+	},
+	
+	getFormatLength : function ( format ) {
+		
+		return parseInt( this.getFormat(format).substr(1) );
+	
+	},
+	
+	getFormatByLength : function ( len ) {
+		
+		if (len == 1) return 'fv1';
+		
+		return 'v' + len;
+	
+	},
+	
+	format : function ( code, from, to ) {
+		
+		var format = this.getFormat(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,0.0)';
+			
+			case 'v3=v1': return code + '.x';
+			case 'v3=v2': return code + '.xy';
+			case 'v3=v4': return 'vec4(' + code + ',0.0)';
+			
+			case 'v4=v1': return code + '.x';
+			case 'v4=v2': return code + '.xy';
+			case 'v4=v3': return code + '.xyz';
+		}
+		
+		return code;
+	
+	},
+	
+	getType : function ( format ) {
+		
+		return THREE.NodeBuilder.type[ format ];
+	
+	},
+	
+	getUuid : function ( uuid, useCache ) {
+		
+		useCache = useCache !== undefined ? useCache : true;
+		
+		if (useCache && this.cache) uuid = this.cache + '-' + uuid;
+		
+		return uuid;
+
+	},
+	
+	setCache : function ( name ) {
+		
+		this.cache = name || '';
+		
+		return this;
+
+	},
+	
+	getElementByIndex : function ( index ) {
+		
+		return THREE.NodeBuilder.elements[ index ];
+	
+	},
+	
+	getElementIndex : function ( elm ) {
+		
+		return THREE.NodeBuilder.elements.indexOf( elm );
+	
+	},
+	
+	isShader : function ( shader ) {
+		
+		return this.shader == shader || this.isVerify;
+
+	},
+	
+	setShader : function ( shader ) {
+		
+		this.shader = shader;
+		
+		return this;
+
+	}
+};
+
+THREE.NodeBuilder.type = {
+	float : 'fv1',
+	vec2 : 'v2',
+	vec3 : 'v3',
+	vec4 : 'v4'
+};
+
+THREE.NodeBuilder.constructors = [
+	'', 
+	'vec2', 
+	'vec3', 
+	'vec4'
+];
+
+THREE.NodeBuilder.elements = [
+	'x', 
+	'y', 
+	'z', 
+	'w'
+];

+ 52 - 0
examples/materials/nodes/NodeConst.js

@@ -0,0 +1,52 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeConst = function(name, useDefine) {
+	
+	name = name || THREE.NodeConst.PI;
+	
+	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=(.*?)\;/i;
+	var type = 'fv1';
+	
+	var match = name.match( rDeclaration );
+	
+	if (match && match.length > 1) {
+	
+		type = match[1];
+		name = match[2];
+		
+		if (useDefine) {
+			
+			this.src = '#define ' + name + ' ' + match[3];
+		
+		}
+		else {
+			
+			this.src = 'const ' + type + ' ' + name + ' = ' + match[3] + ';';
+		
+		}
+	
+	}
+	
+	this.name = name;
+	
+	THREE.NodeTemp.call( this, type );
+	
+};
+
+THREE.NodeConst.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeConst.prototype.constructor = THREE.NodeConst;
+
+THREE.NodeConst.PI = 'PI';
+THREE.NodeConst.PI2 = 'PI2';
+THREE.NodeConst.RECIPROCAL_PI = 'RECIPROCAL_PI';
+THREE.NodeConst.RECIPROCAL_PI2 = 'RECIPROCAL_PI2';
+THREE.NodeConst.LOG2 = 'LOG2';
+THREE.NodeConst.EPSILON = 'EPSILON';
+
+THREE.NodeConst.prototype.generate = function( builder, output ) {
+	
+	return builder.format( this.name, this.getType( builder ), output );
+
+};

+ 157 - 0
examples/materials/nodes/NodeFunction.js

@@ -0,0 +1,157 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ * @thanks bhouston / https://clara.io/
+ */
+
+THREE.NodeFunction = function( src, includes, extensions ) {
+	
+	THREE.NodeGL.call( this );
+	
+	this.parse( src || '', includes, extensions );
+	
+};
+
+THREE.NodeFunction.prototype = Object.create( THREE.NodeGL.prototype );
+THREE.NodeFunction.prototype.constructor = THREE.NodeFunction;
+
+THREE.NodeFunction.prototype.parseReference = function( name ) {
+	
+	switch(name) {
+		case 'uv': return new THREE.NodeUV().name;
+		case 'uv2': return new THREE.NodeUV(1).name;
+		case 'position': return new THREE.NodePosition().name;
+		case 'worldPosition': return new THREE.NodePosition( THREE.NodePosition.WORLD ).name;
+		case 'normal': return new THREE.NodeNormal().name;
+		case 'normalPosition': return new THREE.NodeNormal( THREE.NodeNormal.WORLD ).name;
+		case 'viewPosition': return new THREE.NodePosition( THREE.NodeNormal.VIEW ).name;
+		case 'viewNormal': return new THREE.NodeNormal( THREE.NodeNormal.VIEW ).name;
+	}
+	
+	return name;
+	
+};
+
+THREE.NodeFunction.prototype.getNodeType = function( builder, type ) {
+
+	return builder.getType( type ) || type;
+
+};
+
+THREE.NodeFunction.prototype.getInputByName = function( name ) {
+	
+	var i = this.input.length;
+	
+	while(i--) {
+	
+		if (this.input[i].name === name)
+			return this.input[i];
+		
+	}
+	
+};
+
+THREE.NodeFunction.prototype.getType = function( builder ) {
+	
+	return this.getNodeType( builder, this.type );
+	
+};
+
+THREE.NodeFunction.prototype.getInclude = function( name ) {
+	
+	var i = this.includes.length;
+	
+	while(i--) {
+		
+		if (this.includes[i].name === name)
+			return this.includes[i];
+	
+	}
+	
+	return undefined;
+	
+};
+
+THREE.NodeFunction.prototype.parse = function( src, includes, extensions ) {
+	
+	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
+	var rProperties = /[a-z_0-9]+/ig;
+	
+	this.includes = includes || [];
+	this.extensions = extensions || {};
+	
+	var match = src.match( rDeclaration );
+	
+	this.input = [];
+	
+	if (match && match.length == 4) {
+	
+		this.type = match[1];
+		this.name = match[2];
+		
+		var inputs = match[3].match( 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.input.push({
+					name : name,
+					type : type,
+					qualifier : qualifier
+				});
+			}
+			
+		}
+
+		var match, offset = 0;
+		
+		while (match = rProperties.exec(src)) {
+			
+			var prop = match[0];
+			var reference = this.parseReference( prop );
+			
+			if (prop != reference) {
+				
+				src = src.substring( 0, match.index + offset ) + reference + src.substring( match.index + prop.length + offset  );
+				
+				offset += reference.length - prop.length;
+				
+			}
+			
+			if (this.getInclude(reference) === undefined && THREE.NodeLib.contains(reference)) {
+					
+				this.includes.push( THREE.NodeLib.get(reference) );
+				
+			}
+			
+		}
+		
+		this.src = src;
+
+	}
+	else {
+		
+		this.type = '';
+		this.name = '';
+	
+	}
+};

+ 60 - 0
examples/materials/nodes/NodeFunctionCall.js

@@ -0,0 +1,60 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeFunctionCall = function( value ) {
+	
+	THREE.NodeTemp.call( this );
+	
+	this.setFunction( value );
+	
+};
+
+THREE.NodeFunctionCall.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeFunctionCall.prototype.constructor = THREE.NodeFunctionCall;
+
+THREE.NodeFunctionCall.prototype.setFunction = function(val) {
+	
+	this.input = [];
+	this.value = val;
+	
+};
+
+THREE.NodeFunctionCall.prototype.getFunction = function() {
+	
+	return this.value;
+	
+};
+
+THREE.NodeFunctionCall.prototype.getType = function( builder ) {
+	
+	return this.value.getType( builder );
+	
+};
+
+THREE.NodeFunctionCall.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	
+	var type = this.getType( builder );
+	var func = this.value;
+	
+	builder.include( func );
+	
+	var code = func.name + '(';
+	var params = [];
+	
+	for(var i = 0; i < func.input.length; i++) {
+		
+		var inpt = func.input[i];
+		var param = this.input[i] || this.input[inpt.name];
+		
+		params.push( param.build( builder, builder.getType( inpt.type ) ) );
+	
+	}
+	
+	code += params.join(',') + ')';
+	
+	return builder.format( code, type, output );
+
+};

+ 94 - 0
examples/materials/nodes/NodeGL.js

@@ -0,0 +1,94 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeGL = function( type ) {
+	
+	this.uuid = THREE.Math.generateUUID();
+	
+	this.allow = {};
+	this.requestUpdate = false;
+	
+	this.type = type;
+	
+};
+
+THREE.NodeGL.prototype.verify = function( builder ) {
+	
+	builder.isVerify = true;
+	
+	var material = builder.material;
+	
+	this.build( builder, 'v4' );
+	
+	material.clearVertexNode();
+	material.clearFragmentNode();
+	
+	builder.setCache(); // reset cache
+	
+	builder.isVerify = false;
+
+};
+
+THREE.NodeGL.prototype.verifyAndBuildCode = function( builder, output, cache ) {
+	
+	this.verify( builder.setCache(cache) );
+	
+	return this.buildCode( builder.setCache(cache), output );
+	
+};
+
+THREE.NodeGL.prototype.buildCode = function( builder, output, uuid ) {
+	
+	var material = builder.material;
+	var data = { result : this.build( builder, output, uuid ) };
+	
+	if (builder.isShader('vertex')) data.code = material.clearVertexNode();
+	else data.code = material.clearFragmentNode();
+	
+	builder.setCache(); // reset cache
+	
+	return data;
+
+};
+
+THREE.NodeGL.prototype.verifyNodeDeps = function( builder, data, output ) {
+	
+	data.deps = (data.deps || 0) + 1;
+	
+	var outputLen = builder.getFormatLength( output );
+	
+	if (outputLen > data.outputMax || this.getType( builder )) {
+		
+		data.outputMax = outputLen;
+		data.output = output;
+		
+	}
+
+};
+
+THREE.NodeGL.prototype.build = function( builder, output, uuid ) {
+
+	var material = builder.material;
+	var data = material.getNodeData( uuid || this.uuid );
+	
+	if (builder.isShader('verify')) this.verifyNodeDeps( builder, data, output );
+	
+	if (this.allow[builder.shader] === false) {
+		throw new Error( 'Shader ' + shader + ' is not compatible with this node.' );
+	}
+	
+	if (this.requestUpdate && !data.requestUpdate) {
+		material.requestUpdate.push( this );
+		data.requestUpdate = true;
+	}
+	
+	return this.generate( builder, output, uuid );
+	
+};
+
+THREE.NodeGL.prototype.getType = function( builder ) {
+
+	return this.type;
+	
+};

+ 44 - 0
examples/materials/nodes/NodeInput.js

@@ -0,0 +1,44 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeInput = function(type, params) {
+	
+	THREE.NodeTemp.call( this, type, params );
+	
+};
+
+THREE.NodeInput.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeInput.prototype.constructor = THREE.NodeInput;
+
+THREE.NodeInput.prototype.generate = function( builder, output, uuid, type ) {
+
+	var material = builder.material;
+
+	uuid = builder.getUuid( uuid || this.uuid );
+	type = type || this.type;
+	
+	var data = material.getNodeData( uuid );
+	
+	if (builder.isShader('vertex')) {
+	
+		if (!data.vertex) {
+		
+			data.vertex = material.getVertexUniform( this.value, type );
+			
+		}
+		
+		return builder.format( data.vertex.name, type, output );
+	}
+	else {
+		
+		if (!data.fragment) { 
+			
+			data.fragment = material.getFragmentUniform( this.value, type );
+			
+		}
+		
+		return builder.format( data.fragment.name, type, output );
+	}
+
+};

+ 87 - 0
examples/materials/nodes/NodeLib.js

@@ -0,0 +1,87 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeLib = {
+	nodes:{},
+	add:function(node) {
+		this.nodes[node.name] = node;
+	},
+	remove:function(node) {
+		delete this.nodes[node.name];
+	},
+	get:function(name) {
+		return this.nodes[name];
+	},
+	contains:function(name) {
+		return this.nodes[name] != undefined;
+	}
+};
+
+//
+//	Luma
+//
+
+THREE.NodeLib.add(new THREE.NodeConst("vec3 LUMA = vec3(0.2125, 0.7154, 0.0721);") );
+
+//
+//	DepthColor
+//
+			
+THREE.NodeLib.add(new THREE.NodeFunction([
+"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" )));
+
+//
+//	NormalMap
+//
+			
+THREE.NodeLib.add(new THREE.NodeFunction([
+// 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, float scale ) {",
+	"vec3 q0 = dFdx( eye_pos );",
+	"vec3 q1 = dFdy( eye_pos );",
+	"vec2 st0 = dFdx( mUv.st );",
+	"vec2 st1 = dFdy( mUv.st );",
+	"vec3 S = normalize( q0 * st1.t - q1 * st0.t );",
+	"vec3 T = normalize( -q0 * st1.s + q1 * st0.s );",
+	"vec3 N = normalize( surf_norm );",
+	"vec3 mapN = map * 2.0 - 1.0;",
+	"mapN.xy = scale * mapN.xy;",
+	"mat3 tsn = mat3( S, T, N );",
+	"return normalize( tsn * mapN );",
+"}"
+].join( "\n" ), null, {derivatives:true}));
+
+//
+//	Saturation
+//
+
+THREE.NodeLib.add(new THREE.NodeFunction([
+// Algorithm from Chapter 16 of OpenGL Shading Language
+"vec3 saturation_rgb(vec3 rgb, float adjustment) {",
+	"vec3 intensity = vec3(dot(rgb, LUMA));",
+	"return mix(intensity, rgb, adjustment);",
+"}"
+].join( "\n" )));
+
+//
+//	Luminance
+//
+
+THREE.NodeLib.add(new THREE.NodeFunction([
+// Algorithm from Chapter 10 of Graphics Shaders.
+"float luminance_rgb(vec3 rgb) {",
+	"return dot(rgb, LUMA);",
+"}"
+].join( "\n" )));

+ 467 - 0
examples/materials/nodes/NodeMaterial.js

@@ -0,0 +1,467 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeMaterial = function( vertex, fragment ) {
+	
+	THREE.ShaderMaterial.call( this );
+	
+	this.vertex = vertex || new THREE.NodeRaw( new THREE.NodePosition( THREE.NodePosition.PROJECTION ) );
+	this.fragment = fragment || new THREE.NodeRaw( new THREE.NodeColor( 0xFF0000 ) );
+	
+};
+
+THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
+THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
+
+THREE.NodeMaterial.Type = {
+	t : 'sampler2D',
+	tc : 'samplerCube',
+	bv1 : 'bool',
+	iv1 : 'int',
+	fv1 : 'float',
+	c : 'vec3',
+	v2 : 'vec2',
+	v3 : 'vec3',
+	v4 : 'vec4'
+};
+
+THREE.NodeMaterial.GetShortcuts = function( prop, name ) {
+	
+	return {
+		get: function () { return this[prop][name]; },
+		set: function ( val ) { this[prop][name] = val; }
+	};
+
+};
+
+THREE.NodeMaterial.Shortcuts = function( proto, prop, list ) {
+	
+	var shortcuts = {};
+	
+	for(var i = 0; i < list.length; ++i) {
+		
+		var name = list[i];
+		
+		shortcuts[name] =  this.GetShortcuts( prop, name );
+	
+	}
+	
+	Object.defineProperties( proto, shortcuts );
+
+};
+
+THREE.NodeMaterial.prototype.updateAnimation = function( delta ) {
+	
+	for(var i = 0; i < this.requestUpdate.length; ++i) {
+
+		this.requestUpdate[i].updateAnimation( delta );
+	
+	}
+	
+};
+
+THREE.NodeMaterial.prototype.build = function() {
+	
+	var vertex, fragment;
+	
+	this.defines = {};
+	this.uniforms = {}; 
+	
+	this.nodeData = {};	
+	
+	this.vertexUniform = [];
+	this.fragmentUniform = [];
+	
+	this.vertexTemps = [];
+	this.fragmentTemps = [];
+	
+	this.uniformList = [];
+	
+	this.consts = [];
+	this.functions = [];
+	
+	this.requestUpdate = [];
+	
+	this.requestAttrib = {
+		uv:[],
+		color:[]
+	};
+	
+	this.needsColor = false;
+	this.needsLight = false;
+	this.needsPosition = false;
+	this.needsTransparent = false;
+	
+	this.vertexPars = '';
+	this.fragmentPars = '';
+	
+	this.vertexCode = '';
+	this.fragmentCode = '';
+	
+	this.vertexNode = '';
+	this.fragmentNode = '';
+	
+	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]) {
+		
+		this.addVertexPars( 'varying vec2 vUv;' );
+		this.addFragmentPars( 'varying vec2 vUv;' );
+		
+		this.addVertexCode( 'vUv = uv;' );
+		
+	}
+	
+	if (this.requestAttrib.uv[1]) {
+		
+		this.addVertexPars( 'varying vec2 vUv2; attribute vec2 uv2;' );
+		this.addFragmentPars( 'varying vec2 vUv2;' );
+		
+		this.addVertexCode( 'vUv2 = uv2;' );
+		
+	}
+	
+	if (this.requestAttrib.color[0]) {
+
+		this.addVertexPars( 'varying vec4 vColor; attribute vec4 color;' );
+		this.addFragmentPars( 'varying vec4 vColor;' );
+		
+		this.addVertexCode( 'vColor = color;' );
+		
+	}
+	
+	if (this.requestAttrib.color[1]) {
+
+		this.addVertexPars( 'varying vec4 vColor2; attribute vec4 color2;' );
+		this.addFragmentPars( 'varying vec4 vColor2;' );
+		
+		this.addVertexCode( 'vColor2 = color2;' );
+		
+	}
+	
+	if (this.requestAttrib.position) {
+
+		this.addVertexPars( 'varying vec3 vPosition;' );
+		this.addFragmentPars( 'varying vec3 vPosition;' );
+		
+		this.addVertexCode( 'vPosition = transformed;' );
+		
+	}
+	
+	if (this.requestAttrib.worldPosition) {
+		
+		// for future update replace from the native "varying vec3 vWorldPosition" for optimization
+		
+		this.addVertexPars( 'varying vec3 vWPosition;' );
+		this.addFragmentPars( 'varying vec3 vWPosition;' );
+		
+		this.addVertexCode( 'vWPosition = worldPosition.xyz;' );
+
+	}
+	
+	if (this.requestAttrib.normal) {
+
+		this.addVertexPars( 'varying vec3 vObjectNormal;' );
+		this.addFragmentPars( 'varying vec3 vObjectNormal;' );
+		
+		this.addVertexCode( 'vObjectNormal = normal;' );
+		
+	}
+	
+	if (this.requestAttrib.worldNormal) {
+
+		this.addVertexPars( 'varying vec3 vWNormal;' );
+		this.addFragmentPars( 'varying vec3 vWNormal;' );
+		
+		this.addVertexCode( 'vWNormal = ( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz;' );
+		
+	}
+	
+	this.lights = this.needsLight;
+	this.transparent = this.needsTransparent;
+	
+	this.vertexShader = [
+		this.vertexPars,
+		this.getCodePars( this.vertexUniform, 'uniform' ),
+		this.getIncludes(this.consts['vertex']),
+		this.getIncludes(this.functions['vertex']),
+		'void main(){',
+		this.getCodePars( this.vertexTemps ),
+		vertex,
+		this.vertexCode,
+		'}'
+	].join( "\n" );
+
+	this.fragmentShader = [
+		this.fragmentPars,
+		this.getCodePars( this.fragmentUniform, 'uniform' ),
+		this.getIncludes(this.consts['fragment']),
+		this.getIncludes(this.functions['fragment']),
+		'void main(){',
+		this.getCodePars( this.fragmentTemps ),
+		this.fragmentCode,
+		fragment,
+		'}'
+	].join( "\n" );
+	
+	this.needsUpdate = true;
+	this.dispose(); // force update
+	
+	return this;
+};
+
+THREE.NodeMaterial.prototype.define = function(name, value) {
+
+	this.defines[name] = value == undefined ? 1 : value;
+
+};
+
+THREE.NodeMaterial.prototype.isDefined = function(name) {
+
+	return this.defines[name] != undefined;
+
+};
+
+THREE.NodeMaterial.prototype.mergeUniform = function( uniforms ) {
+	
+	for (var name in uniforms) {
+		
+		this.uniforms[ name ] = uniforms[ name ];
+	
+	}
+	
+};
+
+THREE.NodeMaterial.prototype.createUniform = function( value, type, needsUpdate ) {
+	
+	var index = this.uniformList.length;
+	
+	var uniform = {
+		type : type,
+		value : value,
+		needsUpdate : needsUpdate,
+		name : 'nVu' + index
+	};
+	
+	this.uniformList.push(uniform);
+	
+	return uniform;
+	
+};
+
+THREE.NodeMaterial.prototype.getVertexTemp = function( uuid, type ) {
+	
+	if (!this.vertexTemps[ uuid ]) {
+		
+		var index = this.vertexTemps.length,
+			name = 'nVt' + index,
+			data = { name : name, type : type };
+		
+		this.vertexTemps.push( data );
+		this.vertexTemps[uuid] = data;
+		
+	}
+	
+	return this.vertexTemps[uuid];
+	
+};
+
+THREE.NodeMaterial.prototype.getIncludes = function( incs ) {
+	
+	function sortByPosition(a, b){
+		return b.deps - a.deps;
+	}
+	
+	return function( incs ) {
+		
+		if (!incs) return '';
+		
+		var code = '';
+		var incs = incs.sort(sortByPosition);
+		
+		for(var i = 0; i < incs.length; i++) {
+			
+			code += incs[i].node.src + '\n';
+		
+		}
+		
+		return code;
+	}
+}();
+
+THREE.NodeMaterial.prototype.getFragmentTemp = function( uuid, type ) {
+	
+	if (!this.fragmentTemps[ uuid ]) {
+		
+		var index = this.fragmentTemps.length,
+			name = 'nVt' + index,
+			data = { name : name, type : type };
+		
+		this.fragmentTemps.push( data );
+		this.fragmentTemps[uuid] = data;
+		
+	}
+	
+	return this.fragmentTemps[uuid];
+	
+};
+
+THREE.NodeMaterial.prototype.addVertexPars = function( code ) {
+
+	this.vertexPars += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.addFragmentPars = function( code ) {
+
+	this.fragmentPars += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.addVertexCode = function( code ) {
+
+	this.vertexCode += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.addFragmentCode = function( code ) {
+
+	this.fragmentCode += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.addVertexNode = function( code ) {
+
+	this.vertexNode += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.clearVertexNode = function() {
+
+	var code = this.fragmentNode;
+	
+	this.fragmentNode = '';
+	
+	return code;
+
+};
+
+THREE.NodeMaterial.prototype.addFragmentNode = function( code ) {
+
+	this.fragmentNode += code + '\n';
+
+};
+
+THREE.NodeMaterial.prototype.clearFragmentNode = function() {
+
+	var code = this.fragmentNode;
+	
+	this.fragmentNode = '';
+	
+	return code;
+
+};
+
+THREE.NodeMaterial.prototype.getCodePars = function( pars, prefix ) {
+
+	prefix = prefix || '';
+
+	var code = '';
+	
+	for (var i = 0, l = pars.length; i < l; ++i) {
+		
+		var parsType = pars[i].type;
+		var parsName = pars[i].name;
+		var parsValue = pars[i].value;
+		
+		if (parsType == 't' && parsValue instanceof THREE.CubeTexture) parsType = 'tc';
+		
+		var type = THREE.NodeMaterial.Type[ parsType ];
+		
+		if (type == undefined) throw new Error( "Node pars " + parsType + " not found." );
+		
+		code += prefix + ' ' + type + ' ' + parsName + ';\n';
+	}
+
+	return code;
+
+};
+
+THREE.NodeMaterial.prototype.getVertexUniform = function( value, type, needsUpdate ) {
+
+	var uniform = this.createUniform( value, type, needsUpdate );
+	
+	this.vertexUniform.push(uniform);
+	this.vertexUniform[uniform.name] = uniform;
+	
+	this.uniforms[ uniform.name ] = uniform;
+	
+	return uniform;
+
+};
+
+THREE.NodeMaterial.prototype.getFragmentUniform = function( value, type, needsUpdate ) {
+
+	var uniform = this.createUniform( value, type, needsUpdate );
+	
+	this.fragmentUniform.push(uniform);
+	this.fragmentUniform[uniform.name] = uniform;
+	
+	this.uniforms[ uniform.name ] = uniform;
+	
+	return uniform;
+
+};
+
+THREE.NodeMaterial.prototype.getNodeData = function( uuid ) {
+
+	return this.nodeData[uuid] = this.nodeData[uuid] || {};
+
+};
+
+THREE.NodeMaterial.prototype.include = function( shader, node ) {
+	
+	var includes;
+	
+	node = typeof node === 'string' ? THREE.NodeLib.get(node) : node;
+	
+	if (node instanceof THREE.NodeFunction) {
+	
+		for (var i = 0; i < node.includes.length; i++) {
+			
+			this.include( shader, node.includes[i] );
+		
+		}
+		
+		includes = this.functions[shader] = this.functions[shader] || [];
+		
+	}
+	else if (node instanceof THREE.NodeConst) {
+		
+		includes = this.consts[shader] = this.consts[shader] || [];
+	
+	}
+	
+	if (includes[node.name] === undefined) {
+			
+		for (var ext in node.extensions) {
+			
+			this.extensions[ext] = true;
+			
+		}
+		
+		includes[node.name] = { 
+			node : node,
+			deps : 1
+		};
+		
+		includes.push(includes[node.name]);
+		
+	}
+	else ++includes[node.name].deps;
+
+};

+ 37 - 0
examples/materials/nodes/NodeRaw.js

@@ -0,0 +1,37 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeRaw = function( value ) {
+	
+	THREE.NodeGL.call( this, 'v4' );
+	
+	this.value = value;
+	
+};
+
+THREE.NodeRaw.prototype = Object.create( THREE.NodeGL.prototype );
+THREE.NodeRaw.prototype.constructor = THREE.NodeRaw;
+
+THREE.NodeGL.prototype.generate = function( builder ) {
+	
+	var material = builder.material;
+	
+	var data = this.value.verifyAndBuildCode( builder, this.type );
+	
+	var code = data.code + '\n';
+	
+	if (builder.shader == 'vertex') {
+		
+		code += 'gl_Position = ' + data.result + ';';
+		
+	}
+	else {
+		
+		code += 'gl_FragColor = ' + data.result + ';';
+	
+	}
+	
+	return code;
+
+};

+ 116 - 0
examples/materials/nodes/NodeTemp.js

@@ -0,0 +1,116 @@
+/**
+ * Automatic node cache
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeTemp = function( type, params ) {
+	
+	THREE.NodeGL.call( this, type );
+	
+	params = params || {};
+	
+	this.shared = params.shared !== undefined ? params.shared : true;
+	this.unique = params.unique !== undefined ? params.unique : false;
+	
+};
+
+THREE.NodeTemp.prototype = Object.create( THREE.NodeGL.prototype );
+THREE.NodeTemp.prototype.constructor = THREE.NodeTemp;
+
+THREE.NodeTemp.prototype.build = function( builder, output, uuid ) {
+	
+	var material = builder.material;
+	
+	if (this.isShared()) {
+		
+		var isUnique = this.isUnique();
+		
+		if (isUnique && this.constructor.uuid === undefined) {
+			
+			this.constructor.uuid = THREE.Math.generateUUID();
+		
+		}
+		
+		uuid = builder.getUuid( uuid || this.constructor.uuid || this.uuid, !isUnique );
+		
+		var data = material.getNodeData( uuid );
+		
+		if (builder.isShader('verify')) {
+		
+			if (data.deps || 0 > 0) {
+				this.verifyNodeDeps( builder, data, output );
+				return '';
+			}
+			
+			return THREE.NodeGL.prototype.build.call( this, builder, output, uuid );
+			
+		}
+		else if (data.deps == 1) {
+		
+			return THREE.NodeGL.prototype.build.call( this, builder, output, uuid );
+			
+		}
+		
+		var name = this.getTemp( builder, uuid );
+		var type = data.output || this.getType( builder );
+		
+		if (name) {
+		
+			return builder.format( name, type, output );
+			
+		}
+		else {
+			
+			name = THREE.NodeTemp.prototype.generate.call( this, builder, output, uuid, data.output );
+			
+			var code = this.generate( builder, type, uuid );
+			
+			if (builder.isShader('vertex')) material.addVertexNode(name + '=' + code + ';');
+			else material.addFragmentNode(name + '=' + code + ';');
+			
+			return builder.format( name, type, output );
+		
+		}
+		
+	}
+	else {
+		
+		return builder.format( this.generate( builder, this.getType( builder ), uuid ), type, output );
+	
+	}
+	
+};
+
+THREE.NodeTemp.prototype.isShared = function() {
+	
+	return this.shared;
+	
+};
+
+THREE.NodeTemp.prototype.isUnique = function() {
+	
+	return this.unique;
+	
+};
+
+THREE.NodeTemp.prototype.getTemp = function( builder, uuid ) {
+	
+	uuid = uuid || this.uuid;
+	
+	var material = builder.material;
+	
+	if (builder.isShader('vertex') && material.vertexTemps[ uuid ]) return material.vertexTemps[ uuid ].name;
+	else if (material.fragmentTemps[ uuid ]) return material.fragmentTemps[ uuid ].name;
+
+};
+
+THREE.NodeTemp.prototype.generate = function( builder, output, uuid, type ) {
+	
+	if (!this.isShared()) console.error("THREE.NodeTemp is not shared!");
+	
+	uuid = uuid || this.uuid;
+	
+	if (builder.isShader('vertex')) return builder.material.getVertexTemp( uuid, type || this.getType( builder ) ).name;
+	else return builder.material.getFragmentTemp( uuid, type || this.getType( builder ) ).name;
+
+};

+ 106 - 0
examples/materials/nodes/accessors/NodeCamera.js

@@ -0,0 +1,106 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeCamera = function( scope, camera ) {
+	
+	THREE.NodeTemp.call( this, 'v3' );
+	
+	this.scope = scope || THREE.NodeCamera.POSITION;
+	this.camera = camera;
+	
+	switch(scope) {
+		
+		case THREE.NodeCamera.DEPTH:
+			
+			this.near = new THREE.NodeFloat( camera ? camera.near : 1);
+			this.far = new THREE.NodeFloat(camera ? camera.far : 1200);
+			
+			break;
+	
+	}
+	
+	this.requestUpdate = this.camera !== undefined;
+	
+};
+
+THREE.NodeCamera.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeCamera.prototype.constructor = THREE.NodeCamera;
+
+THREE.NodeCamera.POSITION = 'position';
+THREE.NodeCamera.DEPTH = 'depth';
+
+THREE.NodeCamera.prototype.getType = function( builder ) {
+	
+	switch(this.scope) {
+		case THREE.NodeCamera.DEPTH:
+			return 'fv1';
+	}
+	
+	return this.type;
+	
+};
+
+THREE.NodeCamera.prototype.isUnique = function( builder ) {
+	
+	switch(this.scope) {
+		case THREE.NodeCamera.DEPTH:
+			return true;
+	}
+	
+	return false;
+	
+};
+
+THREE.NodeCamera.prototype.isShared = function( builder ) {
+	
+	switch(this.scope) {
+		case THREE.NodeCamera.POSITION:
+			return false;
+	}
+	
+	return true;
+	
+};
+
+THREE.NodeCamera.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	var result;
+	
+	switch (this.scope) {
+	
+		case THREE.NodeCamera.POSITION:
+	
+			result = 'cameraPosition';
+			
+			break;
+			
+		case THREE.NodeCamera.DEPTH:
+			
+			builder.include('depthcolor');
+			
+			result = 'depthcolor(' + this.near.build( builder, 'fv1' ) + ',' + this.far.build( builder, 'fv1' ) + ')';
+		
+			break;
+			
+	}
+	
+	return builder.format( result, this.getType( builder ), output );
+
+};
+
+THREE.NodeCamera.prototype.updateAnimation = function( delta ) {
+	
+	switch(this.scope) {
+		
+		case THREE.NodeCamera.DEPTH:
+		
+			this.near.number = camera.near;
+			this.far.number = camera.far;
+		
+			break;
+	
+	}
+	
+};

+ 31 - 0
examples/materials/nodes/accessors/NodeColors.js

@@ -0,0 +1,31 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeColors = function( index ) {
+	
+	THREE.NodeTemp.call( this, 'v4', {share:false} );
+	
+	this.index = index || 0;
+	
+};
+
+THREE.NodeColors.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeColors.prototype.constructor = THREE.NodeColors;
+
+THREE.NodeColors.vertexDict = ['color', 'color2'];
+THREE.NodeColors.fragmentDict = ['vColor', 'vColor2'];
+
+THREE.NodeColors.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	var result;
+	
+	material.requestAttrib.color[this.index] = true; 
+	
+	if (builder.isShader('vertex')) result = THREE.NodeColors.vertexDict[this.index];
+	else result = THREE.NodeColors.fragmentDict[this.index];
+	
+	return builder.format( result, this.getType( builder ), output );
+
+};

+ 66 - 0
examples/materials/nodes/accessors/NodeNormal.js

@@ -0,0 +1,66 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeNormal = function( scope ) {
+	
+	THREE.NodeTemp.call( this, 'v3' );
+	
+	this.scope = scope || THREE.NodeNormal.LOCAL;
+	
+};
+
+THREE.NodeNormal.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeNormal.prototype.constructor = THREE.NodeNormal;
+
+THREE.NodeNormal.LOCAL = 'local';
+THREE.NodeNormal.WORLD = 'world';
+THREE.NodeNormal.VIEW = 'view';
+
+THREE.NodeNormal.prototype.isShared = function( builder ) {
+	
+	switch(this.method) {
+		case THREE.NodeNormal.WORLD:
+			return true;
+	}
+	
+	return false;
+	
+};
+
+THREE.NodeNormal.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	var result;
+	
+	switch (this.scope) {
+	
+		case THREE.NodeNormal.LOCAL:
+	
+			material.requestAttrib.normal = true;
+	
+			if (builder.isShader('vertex')) result = 'normal';
+			else result = 'vObjectNormal';
+			
+			break;
+			
+		case THREE.NodeNormal.WORLD:
+	
+			material.requestAttrib.worldNormal = true;
+			
+			if (builder.isShader('vertex')) result = '( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz';
+			else result = 'vWNormal';
+			
+			break;
+			
+		case THREE.NodeNormal.VIEW:
+	
+			result = 'vNormal';
+			
+			break;
+			
+	}
+	
+	return builder.format( result, this.getType( builder ), output );
+
+};

+ 87 - 0
examples/materials/nodes/accessors/NodePosition.js

@@ -0,0 +1,87 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodePosition = function( scope ) {
+	
+	THREE.NodeTemp.call( this, 'v3' );
+	
+	this.scope = scope || THREE.NodePosition.LOCAL;
+	
+};
+
+THREE.NodePosition.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodePosition.prototype.constructor = THREE.NodePosition;
+
+THREE.NodePosition.LOCAL = 'local';
+THREE.NodePosition.WORLD = 'world';
+THREE.NodePosition.VIEW = 'view';
+THREE.NodePosition.PROJECTION = 'projection';
+
+THREE.NodePosition.prototype.getType = function( builder ) {
+	
+	switch(this.method) {
+		case THREE.NodePosition.PROJECTION:
+			return 'v4';
+	}
+	
+	return this.type;
+	
+};
+
+THREE.NodePosition.prototype.isShared = function( builder ) {
+	
+	switch(this.method) {
+		case THREE.NodePosition.LOCAL:
+		case THREE.NodePosition.WORLD:
+			return false;
+	}
+	
+	return true;
+	
+};
+
+THREE.NodePosition.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	var result;
+	
+	switch (this.scope) {
+	
+		case THREE.NodePosition.LOCAL:
+	
+			material.requestAttrib.position = true;
+			
+			if (builder.isShader('vertex')) result = 'transformed';
+			else result = 'vPosition';
+			
+			break;
+			
+		case THREE.NodePosition.WORLD:
+	
+			material.requestAttrib.worldPosition = true;
+			
+			if (builder.isShader('vertex')) result = 'vWPosition';
+			else result = 'vWPosition';
+			
+			break;
+			
+		case THREE.NodePosition.VIEW:
+	
+			if (builder.isShader('vertex')) result = '-mvPosition.xyz';
+			else result = 'vViewPosition';
+			
+			break;
+			
+		case THREE.NodePosition.PROJECTION:
+	
+			if (builder.isShader('vertex')) result = '(projectionMatrix * modelViewMatrix * vec4( position, 1.0 ))';
+			else result = 'vec4( 0.0 )';
+			
+			break;
+			
+	}
+	
+	return builder.format( result, this.getType( builder ), output );
+
+};

+ 39 - 0
examples/materials/nodes/accessors/NodeReflect.js

@@ -0,0 +1,39 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeReflect = function() {
+	
+	THREE.NodeTemp.call( this, 'v3', {unique:true} );
+	
+	this.worldPosition = new THREE.NodePosition( THREE.NodePosition.WORLD );
+	
+};
+
+THREE.NodeReflect.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeReflect.prototype.constructor = THREE.NodeReflect;
+
+THREE.NodeReflect.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	
+	if (builder.isShader('fragment')) {
+		
+		material.addFragmentNode( [
+			'vec3 cameraToVertex = normalize( ' + this.worldPosition.build( builder, 'v3' )  + ' - cameraPosition );',
+			'vec3 worldNormal = inverseTransformDirection( normal, viewMatrix );',
+			'vec3 vReflect = reflect( cameraToVertex, worldNormal );'
+		].join( "\n" ) );
+		
+		return builder.format( 'vReflect', this.type, output );
+		
+	}
+	else {
+		
+		console.warn("THREE.NodeReflect is not compatible with " + builder.shader + " shader");
+		
+		return builder.format( 'vec3( 0.0 )', this.type, output );
+	
+	}
+
+};

+ 31 - 0
examples/materials/nodes/accessors/NodeUV.js

@@ -0,0 +1,31 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeUV = function( index ) {
+	
+	THREE.NodeTemp.call( this, 'v2', {shared:false} );
+	
+	this.index = index || 0;
+	
+};
+
+THREE.NodeUV.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeUV.prototype.constructor = THREE.NodeUV;
+
+THREE.NodeUV.vertexDict = ['uv', 'uv2'];
+THREE.NodeUV.fragmentDict = ['vUv', 'vUv2'];
+
+THREE.NodeUV.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	var result;
+	
+	material.requestAttrib.uv[this.index] = true; 
+	
+	if (builder.isShader('vertex')) result = THREE.NodeUV.vertexDict[this.index];
+	else result = THREE.NodeUV.fragmentDict[this.index];
+	
+	return builder.format( result, this.getType( builder ), output );
+
+};

+ 59 - 0
examples/materials/nodes/extras/NodeVelocity.js

@@ -0,0 +1,59 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeVelocity = function( target, params ) {
+	
+	THREE.NodeVector3.call( this );
+	
+	this.requestUpdate = true;
+	
+	this.target = target;
+	
+	this.position = this.target.position.clone();
+	this.velocity = new THREE.Vector3();
+	this.moment = new THREE.Vector3();
+	
+	this.params = params || {};
+	
+};
+
+THREE.NodeVelocity.prototype = Object.create( THREE.NodeVector3.prototype );
+THREE.NodeVelocity.prototype.constructor = THREE.NodeVelocity;
+
+THREE.NodeVelocity.prototype.updateAnimation = function( delta ) {
+	
+	this.velocity.subVectors( this.target.position, this.position );
+	this.position.copy( this.target.position );
+	
+	switch(this.params.type) {
+			
+		case "elastic":
+			
+			delta *= this.params.fps || 60;
+			
+			var spring = Math.pow( this.params.spring, delta );
+			var friction = Math.pow( this.params.friction, delta );
+			
+			// spring
+			this.moment.x += this.velocity.x * spring;
+			this.moment.y += this.velocity.y * spring;
+			this.moment.z += this.velocity.z * spring;
+
+			// friction
+			this.moment.x *= friction;
+			this.moment.y *= friction;
+			this.moment.z *= friction;
+
+			this.value.copy( this.moment );
+		
+			break;
+	
+		default:
+			
+			this.value.copy( this.velocity );
+		
+			break;
+	}
+	
+};

+ 16 - 0
examples/materials/nodes/inputs/NodeColor.js

@@ -0,0 +1,16 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeColor = function( color ) {
+	
+	THREE.NodeInput.call( this, 'c', {share:false} );
+	
+	this.value = new THREE.Color( color || 0 );
+	
+};
+
+THREE.NodeColor.prototype = Object.create( THREE.NodeInput.prototype );
+THREE.NodeColor.prototype.constructor = THREE.NodeColor;
+
+THREE.NodeMaterial.Shortcuts( THREE.NodeColor.prototype, 'value', [ 'r', 'g', 'b' ] );

+ 37 - 0
examples/materials/nodes/inputs/NodeCubeTexture.js

@@ -0,0 +1,37 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeCubeTexture = function( value, coord, bias ) {
+	
+	THREE.NodeInput.call( this, 'v4' );
+
+	this.value = value;
+	this.coord = coord || new THREE.NodeReflect();
+	this.bias = bias;
+	
+};
+
+THREE.NodeCubeTexture.prototype = Object.create( THREE.NodeInput.prototype );
+THREE.NodeCubeTexture.prototype.constructor = THREE.NodeCubeTexture;
+
+THREE.NodeCubeTexture.prototype.generate = function( builder, output ) {
+
+	var cubetex = THREE.NodeInput.prototype.generate.call( this, builder, output, this.value.uuid, 't' );
+	var coord = this.coord.build( builder, 'v3' );
+	var bias = this.bias ? this.bias.build( builder, 'fv1' ) : undefined;;
+	
+	if (bias == undefined && builder.require.cubeTextureBias) {
+		
+		bias = builder.require.cubeTextureBias.build( builder, 'fv1' );
+		
+	}
+	
+	var code;
+
+	if (bias) code = 'textureCube(' + cubetex + ',' + coord + ',' + bias + ')';
+	else code = 'textureCube(' + cubetex + ',' + coord + ')';
+	
+	return builder.format(code, this.type, output );
+
+};

+ 21 - 0
examples/materials/nodes/inputs/NodeFloat.js

@@ -0,0 +1,21 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeFloat = function( value ) {
+	
+	THREE.NodeInput.call( this, 'fv1', {share:false} );
+	
+	this.value = [ value || 0 ];
+	
+};
+
+THREE.NodeFloat.prototype = Object.create( THREE.NodeInput.prototype );
+THREE.NodeFloat.prototype.constructor = THREE.NodeFloat;
+
+Object.defineProperties( THREE.NodeFloat.prototype, {
+	number: {
+		get: function () { return this.value[0]; },
+		set: function ( val ) { this.value[0] = val; }
+	}
+});

+ 21 - 0
examples/materials/nodes/inputs/NodeInt.js

@@ -0,0 +1,21 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeInt = function( value ) {
+	
+	THREE.NodeInput.call( this, 'fv1', {share:false} );
+	
+	this.value = [ Math.floor(value || 0) ];
+	
+};
+
+THREE.NodeInt.prototype = Object.create( THREE.NodeInput.prototype );
+THREE.NodeInt.prototype.constructor = THREE.NodeInt;
+
+Object.defineProperties( THREE.NodeInt.prototype, {
+	number: {
+		get: function () { return this.value[0]; },
+		set: function ( val ) { this.value[0] = Math.floor(val); }
+	}
+});

+ 31 - 0
examples/materials/nodes/inputs/NodeTexture.js

@@ -0,0 +1,31 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeTexture = function( value, coord, bias ) {
+	
+	THREE.NodeInput.call( this, 'v4' );
+	
+	this.value = value;
+	this.coord = coord || new THREE.NodeUV();
+	this.bias = bias;
+	
+};
+
+THREE.NodeTexture.prototype = Object.create( THREE.NodeInput.prototype );
+THREE.NodeTexture.prototype.constructor = THREE.NodeTexture;
+
+THREE.NodeTexture.prototype.generate = function( builder, output ) {
+
+	var tex = THREE.NodeInput.prototype.generate.call( this, builder, output, this.value.uuid, 't' );
+	var coord = this.coord.build( builder, 'v2' );
+	var bias = this.bias ? this.bias.build( builder, 'fv1' ) : undefined;
+	
+	var code;
+
+	if (bias) code = 'texture2D(' + tex + ',' + coord + ',' + bias + ')';
+	else code = 'texture2D(' + tex + ',' + coord + ')';
+	
+	return builder.format(code, this.type, output );
+
+};

+ 16 - 0
examples/materials/nodes/inputs/NodeVector2.js

@@ -0,0 +1,16 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeVector2 = function( x, y ) {
+	
+	THREE.NodeInput.call( this, 'v2', {share:false} );
+	
+	this.value = new THREE.Vector2( x, y );
+	
+};
+
+THREE.NodeVector2.prototype = Object.create( THREE.NodeInput.prototype );
+THREE.NodeVector2.prototype.constructor = THREE.NodeVector2;
+
+THREE.NodeMaterial.Shortcuts( THREE.NodeVector2.prototype, 'value', [ 'x', 'y' ] );

+ 17 - 0
examples/materials/nodes/inputs/NodeVector3.js

@@ -0,0 +1,17 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeVector3 = function( x, y, z ) {
+	
+	THREE.NodeInput.call( this, 'v3', {share:false} );
+	
+	this.type = 'v3';
+	this.value = new THREE.Vector3( x, y, z );
+	
+};
+
+THREE.NodeVector3.prototype = Object.create( THREE.NodeInput.prototype );
+THREE.NodeVector3.prototype.constructor = THREE.NodeVector3;
+
+THREE.NodeMaterial.Shortcuts( THREE.NodeVector3.prototype, 'value', [ 'x', 'y', 'z' ] );

+ 16 - 0
examples/materials/nodes/inputs/NodeVector4.js

@@ -0,0 +1,16 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeVector4 = function( x, y, z, w ) {
+	
+	THREE.NodeInput.call( this, 'v4', {share:false} );
+	
+	this.value = new THREE.Vector4( x, y, z, w );
+	
+};
+
+THREE.NodeVector4.prototype = Object.create( THREE.NodeInput.prototype );
+THREE.NodeVector4.prototype.constructor = THREE.NodeVector4;
+
+THREE.NodeMaterial.Shortcuts( THREE.NodeVector4.prototype, 'value', [ 'x', 'y', 'z', 'w' ] );

+ 257 - 0
examples/materials/nodes/interfaces/NodePhong.js

@@ -0,0 +1,257 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodePhong = function() {
+	
+	THREE.NodeGL.call( this );
+	
+	this.color = new THREE.NodeColor( 0xEEEEEE );
+	this.specular = new THREE.NodeColor( 0x111111 );
+	this.shininess = new THREE.NodeFloat( 30 );
+	
+};
+
+THREE.NodePhong.prototype = Object.create( THREE.NodeGL.prototype );
+THREE.NodePhong.prototype.constructor = THREE.NodePhong;
+
+THREE.NodePhong.prototype.build = function( builder ) {
+	
+	var material = builder.material;
+	var code;
+	
+	material.define( 'PHONG' );
+	material.define( 'ALPHATEST', '0.0' );
+	
+	material.needsLight = true;
+	
+	if (builder.isShader('vertex')) {
+		
+		var transform = this.transform ? this.transform.verifyAndBuildCode( builder, 'v3' ) : undefined;
+		
+		material.mergeUniform( THREE.UniformsUtils.merge( [
+
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "lights" ],
+			THREE.UniformsLib[ "shadowmap" ]
+
+		] ) );
+		
+		material.addVertexPars( [
+			"varying vec3 vViewPosition;",
+
+			"#ifndef FLAT_SHADED",
+
+			"	varying vec3 vNormal;",
+
+			"#endif",
+
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ]
+
+		].join( "\n" ) );
+		
+		var output = [
+				THREE.ShaderChunk[ "beginnormal_vertex" ],
+				THREE.ShaderChunk[ "morphnormal_vertex" ],
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+				THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+			"#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED
+
+			"	vNormal = normalize( transformedNormal );",
+
+			"#endif",
+
+				THREE.ShaderChunk[ "begin_vertex" ]
+		];
+		
+		if ( transform ) {
+			output.push( transform.code );
+			output.push( "transformed = " + transform.result + ";" );
+		}
+		
+		output.push(
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "project_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+			"	vViewPosition = - mvPosition.xyz;",
+
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "lights_phong_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ]
+		);
+		
+		code = output.join( "\n" );
+		
+	}
+	else {
+		
+		// verify all nodes to reuse generate codes
+		
+		this.color.verify( builder );
+		this.specular.verify( builder );
+		this.shininess.verify( builder );
+		
+		if (this.alpha) this.alpha.verify( builder );
+		
+		if (this.ao) this.ao.verify( builder );
+		if (this.ambient) this.ambient.verify( builder );
+		if (this.shadow) this.shadow.verify( builder );
+		if (this.emissive) this.emissive.verify( builder );
+		
+		if (this.normal) this.normal.verify( builder );
+		if (this.normalScale && this.normal) this.normalScale.verify( builder );
+		
+		if (this.environment) this.environment.verify( builder );
+		if (this.reflectivity && this.environment) this.reflectivity.verify( builder );
+		
+		// build code
+		
+		var color = this.color.buildCode( builder, 'v4' );
+		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 ao = this.ao ? this.ao.buildCode( builder, 'c' ) : 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 normal = this.normal ? this.normal.buildCode( builder, 'v3' ) : undefined;
+		var normalScale = this.normalScale && this.normal ? this.normalScale.buildCode( builder, 'fv1' ) : undefined;
+		
+		var environment = this.environment ? this.environment.buildCode( builder.setCache('env'), 'c' ) : undefined; 
+		var reflectivity = this.reflectivity && this.environment ? this.reflectivity.buildCode( builder, 'fv1' ) : undefined;
+		
+		material.needsTransparent = alpha != undefined;
+		
+		material.addFragmentPars( [
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "bsdfs" ],
+			THREE.ShaderChunk[ "lights_pars" ],
+			THREE.ShaderChunk[ "lights_phong_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ]
+		].join( "\n" ) );
+	
+		var output = [
+				// prevent undeclared normal
+				THREE.ShaderChunk[ "normal_fragment" ],
+			
+				color.code,
+			"	vec4 diffuseColor = " + color.result + ";",
+			"	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
+			
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+				
+			specular.code,
+			"	vec3 specular = " + specular.result + ";",
+			
+			shininess.code,
+			"	float shininess = max(0.0001," + shininess.result + ");",
+			
+			"	float specularStrength = 1.0;" // Ignored in NodeMaterial ( replace to specular )
+		];	
+		
+		if (alpha) {
+			
+			output.push( 
+				alpha.code,
+				'if ( ' + alpha.result + ' <= ALPHATEST ) discard;'
+			);
+		
+		}
+		
+		if (normal) {
+			
+			builder.include( 'perturbNormal2Arb' );
+			
+			output.push(normal.code);
+			
+			if (normalScale) output.push(normalScale.code);
+			
+			output.push(
+				'normal = perturbNormal2Arb(-vViewPosition,normal,' +
+				normal.result + ',' +
+				new THREE.NodeUV().build( builder, 'v2' ) + ',' +
+				(normalScale ? normalScale.result : '1.0') + ');'
+			);
+
+		}
+
+		output.push(
+			THREE.ShaderChunk[ "shadowmap_fragment" ],
+			
+			// accumulation
+			THREE.ShaderChunk[ "lights_phong_fragment" ],
+			THREE.ShaderChunk[ "lights_template" ]
+		);
+		
+		if (ao) { 
+			output.push( ao.code );
+			output.push( "reflectedLight.indirectDiffuse *= " + ao.result + ";" );
+		}
+		
+		if (ambient) { 
+			output.push( ambient.code );
+			output.push( "reflectedLight.indirectDiffuse += " + ambient.result + ";" );
+		}
+		
+		if (shadow) {
+			output.push( shadow.code );
+			output.push( "reflectedLight.directDiffuse *= " + shadow.result + ";" );
+			output.push( "reflectedLight.directSpecular *= " + shadow.result + ";" );
+		}
+		
+		if (emissive) { 
+			output.push( emissive.code );
+			output.push( "reflectedLight.directDiffuse += " + emissive.result + ";" );
+		}
+		
+		output.push("vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;");
+		
+		if (environment) {
+			output.push( environment.code );
+			
+			if (reflectivity) {
+				
+				output.push( reflectivity.code );
+				
+				output.push( "outgoingLight = mix(" + 'outgoingLight' + "," + environment.result + "," + reflectivity.result + ");" );
+				
+			}
+			else {
+			
+				output.push( "outgoingLight = " + environment.result + ";" );
+			}
+		}
+		
+		output.push(
+			THREE.ShaderChunk[ "linear_to_gamma_fragment" ],
+			THREE.ShaderChunk[ "fog_fragment" ]
+		);
+		
+		if (alpha) {
+			output.push( "gl_FragColor = vec4( outgoingLight, " + alpha.result + " );" );
+		}
+		else {
+			output.push( "gl_FragColor = vec4( outgoingLight, 1.0 );" );
+		}
+		
+		code = output.join( "\n" );
+	
+	}
+	
+	return code;
+
+};

+ 17 - 0
examples/materials/nodes/interfaces/NodePhongMaterial.js

@@ -0,0 +1,17 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodePhongMaterial = function() {
+	
+	this.node = new THREE.NodePhong();
+	
+	THREE.NodeMaterial.call( this, this.node, this.node );
+	
+};
+
+THREE.NodePhongMaterial.prototype = Object.create( THREE.NodeMaterial.prototype );
+THREE.NodePhongMaterial.prototype.constructor = THREE.NodePhongMaterial;
+
+THREE.NodeMaterial.Shortcuts( THREE.NodePhongMaterial.prototype, 'node', 
+[ 'color',  'alpha', 'specular', 'shininess', 'normal', 'normalScale', 'emissive', 'ambient', 'shadow', 'ao', 'environment', 'reflectivity', 'transform' ] );

+ 268 - 0
examples/materials/nodes/interfaces/NodeStandard.js

@@ -0,0 +1,268 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeStandard = function() {
+	
+	THREE.NodeGL.call( this );
+	
+	this.color = new THREE.NodeColor( 0xEEEEEE );
+	this.roughness = new THREE.NodeFloat( 0.5 );
+	this.metalness = new THREE.NodeFloat( 0.5 );
+	
+};
+
+THREE.NodeStandard.prototype = Object.create( THREE.NodeGL.prototype );
+THREE.NodeStandard.prototype.constructor = THREE.NodeStandard;
+
+THREE.NodeStandard.prototype.build = function( builder ) {
+	
+	var material = builder.material;
+	var code;
+	
+	material.define( 'STANDARD' );
+	material.define( 'ALPHATEST', '0.0' );
+	
+	material.needsLight = true;
+	
+	if (builder.isShader('vertex')) {
+		
+		var transform = this.transform ? this.transform.verifyAndBuildCode( builder, 'v3' ) : undefined;
+		
+		material.mergeUniform( THREE.UniformsUtils.merge( [
+
+			THREE.UniformsLib[ "fog" ],
+			THREE.UniformsLib[ "lights" ],
+			THREE.UniformsLib[ "shadowmap" ]
+
+		] ) );
+		
+		material.addVertexPars( [
+			"varying vec3 vViewPosition;",
+
+			"#ifndef FLAT_SHADED",
+
+			"	varying vec3 vNormal;",
+
+			"#endif",
+
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "lights_phong_pars_vertex" ],
+			THREE.ShaderChunk[ "morphtarget_pars_vertex" ],
+			THREE.ShaderChunk[ "skinning_pars_vertex" ],
+			THREE.ShaderChunk[ "shadowmap_pars_vertex" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_vertex" ]
+
+		].join( "\n" ) );
+		
+		var output = [
+				THREE.ShaderChunk[ "beginnormal_vertex" ],
+				THREE.ShaderChunk[ "morphnormal_vertex" ],
+				THREE.ShaderChunk[ "skinbase_vertex" ],
+				THREE.ShaderChunk[ "skinnormal_vertex" ],
+				THREE.ShaderChunk[ "defaultnormal_vertex" ],
+
+			"#ifndef FLAT_SHADED", // Normal computed with derivatives when FLAT_SHADED
+
+			"	vNormal = normalize( transformedNormal );",
+
+			"#endif",
+
+				THREE.ShaderChunk[ "begin_vertex" ]
+		];
+		
+		if ( transform ) {
+			output.push( transform.code );
+			output.push( "transformed = " + transform.result + ";" );
+		}
+		
+		output.push(
+				THREE.ShaderChunk[ "morphtarget_vertex" ],
+				THREE.ShaderChunk[ "skinning_vertex" ],
+				THREE.ShaderChunk[ "project_vertex" ],
+				THREE.ShaderChunk[ "logdepthbuf_vertex" ],
+
+			"	vViewPosition = - mvPosition.xyz;",
+
+				THREE.ShaderChunk[ "worldpos_vertex" ],
+				THREE.ShaderChunk[ "lights_phong_vertex" ],
+				THREE.ShaderChunk[ "shadowmap_vertex" ]
+		);
+		
+		code = output.join( "\n" );
+		
+	}
+	else {
+		
+		// CubeMap blur effect (PBR)
+		
+		builder.require.cubeTextureBias = builder.require.cubeTextureBias || new THREE.NodeRoughnessToBlinnExponent();
+		
+		// verify all nodes to reuse generate codes
+		
+		this.color.verify( builder );
+		this.roughness.verify( builder );
+		this.metalness.verify( builder );
+		
+		if (this.alpha) this.alpha.verify( builder );
+		
+		if (this.ao) this.ao.verify( builder );
+		if (this.ambient) this.ambient.verify( builder );
+		if (this.shadow) this.shadow.verify( builder );
+		if (this.emissive) this.emissive.verify( builder );
+		
+		if (this.normal) this.normal.verify( builder );
+		if (this.normalScale && this.normal) this.normalScale.verify( builder );
+		
+		if (this.environment) this.environment.verify( builder.setCache('env') ); // isolate environment from others inputs ( see NodeTexture, NodeCubeTexture )
+		if (this.reflectivity && this.environment) this.reflectivity.verify( builder );
+		
+		// build code
+		
+		var color = this.color.buildCode( builder, 'v4' );
+		var roughness = this.roughness.buildCode( builder, 'fv1' );
+		var metalness = this.metalness.buildCode( builder, 'fv1' );
+		
+		var alpha = this.alpha ? this.alpha.buildCode( builder, 'fv1' ) : undefined;
+		
+		var ao = this.ao ? this.ao.buildCode( builder, 'c' ) : 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 normal = this.normal ? this.normal.buildCode( builder, 'v3' ) : undefined;
+		var normalScale = this.normalScale && this.normal ? this.normalScale.buildCode( builder, 'fv1' ) : undefined;
+		
+		var environment = this.environment ? this.environment.buildCode( builder.setCache('env'), 'c' ) : undefined;
+		var reflectivity = this.reflectivity && this.environment ? this.reflectivity.buildCode( builder, 'fv1' ) : undefined;
+		
+		material.needsTransparent = alpha != undefined;
+		
+		material.addFragmentPars( [
+			
+			"varying vec3 vViewPosition;",
+			
+			"#ifndef FLAT_SHADED",
+
+			"	varying vec3 vNormal;",
+
+			"#endif",
+			
+			THREE.ShaderChunk[ "common" ],
+			THREE.ShaderChunk[ "fog_pars_fragment" ],
+			THREE.ShaderChunk[ "bsdfs" ],
+			THREE.ShaderChunk[ "lights_pars" ],
+			THREE.ShaderChunk[ "lights_standard_pars_fragment" ],
+			THREE.ShaderChunk[ "shadowmap_pars_fragment" ],
+			THREE.ShaderChunk[ "logdepthbuf_pars_fragment" ],
+		].join( "\n" ) );
+	
+		var output = [
+				// prevent undeclared normal
+				THREE.ShaderChunk[ "normal_fragment" ],
+				
+				// prevent undeclared material
+			"	StandardMaterial material;",
+			
+				color.code,
+			"	vec4 diffuseColor = " + color.result + ";",
+			"	ReflectedLight reflectedLight = ReflectedLight( vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ), vec3( 0.0 ) );",
+			
+				THREE.ShaderChunk[ "logdepthbuf_fragment" ],
+				
+			roughness.code,
+			"	float roughnessFactor = " + roughness.result + ";",
+			
+			metalness.code,
+			"	float metalnessFactor = " + metalness.result + ";"
+		];	
+		
+		if (alpha) {
+			
+			output.push( 
+				alpha.code,
+				'if ( ' + alpha.result + ' <= ALPHATEST ) discard;'
+			);
+		
+		}
+		
+		if (normal) {
+			
+			builder.include( 'perturbNormal2Arb' );
+			
+			output.push(normal.code);
+			
+			if (normalScale) output.push(normalScale.code);
+			
+			output.push(
+				'normal = perturbNormal2Arb(-vViewPosition,normal,' +
+				normal.result + ',' +
+				new THREE.NodeUV().build( builder, 'v2' ) + ',' +
+				(normalScale ? normalScale.result : '1.0') + ');'
+			);
+
+		}
+
+		output.push(
+			THREE.ShaderChunk[ "shadowmap_fragment" ],
+			
+			// accumulation
+			'material.diffuseColor = diffuseColor.rgb * ( 1.0 - metalnessFactor );',
+			'material.specularRoughness = clamp( roughnessFactor, 0.001, 1.0 );', // disney's remapping of [ 0, 1 ] roughness to [ 0.001, 1 ]
+			'material.specularColor = mix( vec3( 0.04 ), diffuseColor.rgb, metalnessFactor );',
+			
+			THREE.ShaderChunk[ "lights_template" ]
+		);
+		
+		if (ao) { 
+			output.push( ao.code );
+			output.push( "reflectedLight.indirectDiffuse *= " + ao.result + ";" );
+		}
+		
+		if (ambient) { 
+			output.push( ambient.code );
+			output.push( "reflectedLight.indirectDiffuse += " + ambient.result + ";" );
+		}
+		
+		if (shadow) {
+			output.push( shadow.code );
+			output.push( "reflectedLight.directDiffuse *= " + shadow.result + ";" );
+			output.push( "reflectedLight.directSpecular *= " + shadow.result + ";" );
+		}
+		
+		if (emissive) { 
+			output.push( emissive.code );
+			output.push( "reflectedLight.directDiffuse += " + emissive.result + ";" );
+		}
+		
+		if (environment) {
+			output.push( environment.code );
+			output.push( "RE_IndirectSpecular(" + environment.result + ", geometry, material, reflectedLight );" );
+		}
+		
+		if (reflectivity) {	
+			output.push( reflectivity.code );
+			output.push( "reflectedLight.indirectSpecular *= " + reflectivity.result + ";" );
+		}
+		
+		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 + " );" );
+		}
+		else {
+			output.push( "gl_FragColor = vec4( outgoingLight, 1.0 );" );
+		}
+		
+		code = output.join( "\n" );
+	
+	}
+	
+	return code;
+
+};

+ 17 - 0
examples/materials/nodes/interfaces/NodeStandardMaterial.js

@@ -0,0 +1,17 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeStandardMaterial = function() {
+	
+	this.node = new THREE.NodeStandard();
+	
+	THREE.NodeMaterial.call( this, this.node, this.node );
+	
+};
+
+THREE.NodeStandardMaterial.prototype = Object.create( THREE.NodeMaterial.prototype );
+THREE.NodeStandardMaterial.prototype.constructor = THREE.NodeStandardMaterial;
+
+THREE.NodeMaterial.Shortcuts( THREE.NodeStandardMaterial.prototype, 'node', 
+[ 'color',  'alpha', 'roughness', 'metalness', 'normal', 'normalScale', 'emissive', 'ambient', 'shadow', 'ao', 'environment', 'reflectivity', 'transform' ] );

+ 78 - 0
examples/materials/nodes/math/NodeMath1.js

@@ -0,0 +1,78 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeMath1 = function( a, method ) {
+	
+	THREE.NodeTemp.call( this );
+	
+	this.a = a;
+	
+	this.method = method || THREE.NodeMath1.SIN;
+	
+};
+
+THREE.NodeMath1.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeMath1.prototype.constructor = THREE.NodeMath1;
+
+THREE.NodeMath1.RAD = 'radians';
+THREE.NodeMath1.DEG = 'degrees';
+THREE.NodeMath1.EXP = 'exp';
+THREE.NodeMath1.EXP2 = 'exp2';
+THREE.NodeMath1.LOG = 'log';
+THREE.NodeMath1.LOG2 = 'log2';
+THREE.NodeMath1.INVERSE_SQRT = 'inversesqrt';
+THREE.NodeMath1.FLOOR = 'floor';
+THREE.NodeMath1.CEIL = 'ceil';
+THREE.NodeMath1.NORMALIZE = 'normalize';
+THREE.NodeMath1.FRACT = 'fract';
+THREE.NodeMath1.SAT = 'saturate';
+THREE.NodeMath1.SIN = 'sin';
+THREE.NodeMath1.COS = 'cos';
+THREE.NodeMath1.TAN = 'tan';
+THREE.NodeMath1.ASIN = 'asin';
+THREE.NodeMath1.ACOS = 'acos';
+THREE.NodeMath1.ARCTAN = 'atan';
+THREE.NodeMath1.ABS = 'abc';
+THREE.NodeMath1.SIGN = 'sign';
+THREE.NodeMath1.LENGTH = 'length';
+THREE.NodeMath1.NEGATE = 'negate';
+THREE.NodeMath1.INVERT = 'invert';
+
+THREE.NodeMath1.prototype.getType = function( builder ) {
+	
+	switch(this.method) {
+		case THREE.NodeMath1.DISTANCE:
+			return 'fv1';
+	}
+	
+	return this.a.getType( builder );
+	
+};
+
+THREE.NodeMath1.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	
+	var type = this.getType( builder );
+	
+	var result = this.a.build( builder, type );
+	
+	switch(this.method) {
+		
+		case THREE.NodeMath1.NEGATE:
+			result = '(-' + result + ')';
+			break;
+		
+		case THREE.NodeMath1.INVERT:
+			result = '(1.0-' + result + ')';
+			break;
+		
+		default:
+			result = this.method + '(' + result + ')';
+			break;
+	}
+	
+	return builder.format( result, type, output );
+
+};

+ 93 - 0
examples/materials/nodes/math/NodeMath2.js

@@ -0,0 +1,93 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeMath2 = function( a, b, method ) {
+	
+	THREE.NodeTemp.call( this );
+	
+	this.a = a;
+	this.b = b;
+	
+	this.method = method || THREE.NodeMath2.DISTANCE;
+	
+};
+
+THREE.NodeMath2.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeMath2.prototype.constructor = THREE.NodeMath2;
+
+THREE.NodeMath2.MIN = 'min';
+THREE.NodeMath2.MAX = 'max';
+THREE.NodeMath2.MOD = 'mod';
+THREE.NodeMath2.STEP = 'step';
+THREE.NodeMath2.REFLECT = 'reflect';
+THREE.NodeMath2.DISTANCE = 'distance';
+THREE.NodeMath2.DOT = 'dot';
+THREE.NodeMath2.CROSS = 'cross';
+THREE.NodeMath2.POW = 'pow';
+
+THREE.NodeMath2.prototype.getInputType = function( builder ) {
+	
+	// use the greater length vector
+	if (builder.getFormatLength( this.b.getType( builder ) ) > builder.getFormatLength( this.a.getType( builder ) )) {
+		return this.b.getType( builder );
+	}
+	
+	return this.a.getType( builder );
+	
+};
+
+THREE.NodeMath2.prototype.getType = function( builder ) {
+	
+	switch(this.method) {
+		case THREE.NodeMath2.DISTANCE:
+		case THREE.NodeMath2.DOT:
+			return 'fv1';
+		
+		case THREE.NodeMath2.CROSS:
+			return 'v3';
+	}
+	
+	return this.getInputType( builder );
+};
+
+THREE.NodeMath2.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	
+	var type = this.getInputType( builder );
+	
+	var a, b, 
+		al = builder.getFormatLength( this.a.getType( builder ) ),
+		bl = builder.getFormatLength( this.b.getType( builder ) );
+	
+	// optimzer
+	
+	switch(this.method) {
+		case THREE.NodeMath2.CROSS:
+			a = this.a.build( builder, 'v3' );
+			b = this.b.build( builder, 'v3' );
+			break;
+		
+		case THREE.NodeMath2.STEP:
+			a = this.a.build( builder, al == 1 ? 'fv1' : type );
+			b = this.b.build( builder, type );
+			break;
+			
+		case THREE.NodeMath2.MIN:
+		case THREE.NodeMath2.MAX:
+		case THREE.NodeMath2.MODULO:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, bl == 1 ? 'fv1' : type );
+			break;
+			
+		default:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, type );
+			break;
+	
+	}
+	
+	return builder.format( this.method + '(' + a + ',' + b + ')', this.getType( builder ), output );
+
+};

+ 80 - 0
examples/materials/nodes/math/NodeMath3.js

@@ -0,0 +1,80 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeMath3 = function( a, b, c, method ) {
+	
+	THREE.NodeTemp.call( this );
+	
+	this.a = a;
+	this.b = b;
+	this.c = c;
+	
+	this.method = method || THREE.NodeMath3.MIX;
+	
+};
+
+THREE.NodeMath3.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeMath3.prototype.constructor = THREE.NodeMath3;
+
+THREE.NodeMath3.MIX = 'mix';
+THREE.NodeMath3.REFRACT = 'refract';
+THREE.NodeMath3.SMOOTHSTEP = 'smoothstep';
+THREE.NodeMath3.FACEFORWARD = 'faceforward';
+
+THREE.NodeMath3.prototype.getType = function( builder ) {
+	
+	var a = builder.getFormatLength( this.a.getType( builder ) );
+	var b = builder.getFormatLength( this.b.getType( builder ) );
+	var c = builder.getFormatLength( this.c.getType( builder ) );
+	
+	if (a > b) {
+		if (a > c) return this.a.getType( builder );
+		return this.c.getType( builder );
+	} 
+	else {
+		if (b > c) return this.b.getType( builder );
+	
+		return this.c.getType( builder );
+	}
+	
+};
+
+THREE.NodeMath3.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	
+	var type = this.getType( builder );
+	
+	var a, b, c,
+		al = builder.getFormatLength( this.a.getType( builder ) ),
+		bl = builder.getFormatLength( this.b.getType( builder ) ),
+		cl = builder.getFormatLength( this.c.getType( builder ) )
+	
+	// optimzer
+	
+	switch(this.method) {
+		case THREE.NodeMath3.REFRACT:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, type );
+			c = this.c.build( builder, 'fv1' );
+			break;
+		
+		case THREE.NodeMath3.MIX:
+		case THREE.NodeMath3.SMOOTHSTEP:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, type );
+			c = this.c.build( builder, cl == 1 ? 'fv1' : type );
+			break;
+			
+		default:
+			a = this.a.build( builder, type );
+			b = this.b.build( builder, type );
+			c = this.c.build( builder, type );
+			break;
+	
+	}
+	
+	return builder.format( this.method + '(' + a + ',' + b + ',' + c + ')', type, output );
+
+};

+ 45 - 0
examples/materials/nodes/math/NodeOperator.js

@@ -0,0 +1,45 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+ 
+THREE.NodeOperator = function( a, b, op ) {
+	
+	THREE.NodeTemp.call( this );
+	
+	this.op = op || THREE.NodeOperator.ADD;
+	
+	this.a = a;
+	this.b = b;
+	
+};
+
+THREE.NodeOperator.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeOperator.prototype.constructor = THREE.NodeOperator;
+
+THREE.NodeOperator.ADD = '+';
+THREE.NodeOperator.SUB = '-';
+THREE.NodeOperator.MUL = '*';
+THREE.NodeOperator.DIV = '/';
+
+THREE.NodeOperator.prototype.getType = function( builder ) {
+	
+	// use the greater length vector
+	if (builder.getFormatLength( this.b.getType( builder ) ) > builder.getFormatLength( this.a.getType( builder ) )) {
+		return this.b.getType( builder );
+	}
+	
+	return this.a.getType( builder );
+
+};
+
+THREE.NodeOperator.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	var data = material.getNodeData( this.uuid );
+	
+	var a = this.a.build( builder, output );
+	var b = this.b.build( builder, output );
+	
+	return '(' + a + this.op + b + ')';
+
+};

+ 65 - 0
examples/materials/nodes/utils/NodeJoin.js

@@ -0,0 +1,65 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeJoin = function( x, y, z, w ) {
+	
+	THREE.NodeGL.call( this, 'fv1' );
+	
+	this.x = x;
+	this.y = y;
+	this.z = z;
+	this.w = w;
+	
+};
+
+THREE.NodeJoin.prototype = Object.create( THREE.NodeGL.prototype );
+THREE.NodeJoin.prototype.constructor = THREE.NodeJoin;
+
+THREE.NodeJoin.inputs = ['x','y','z','w'];
+
+THREE.NodeJoin.prototype.getNumElements = function() {
+	
+	var inputs = THREE.NodeJoin.inputs;
+	var i = inputs.length;
+	
+	while (i--) {
+		if ( this[ inputs[i] ] !== undefined ) {
+			++i;
+			break;
+		}
+	}
+	
+	return Math.max(i, 2);
+	
+};
+
+THREE.NodeJoin.prototype.getType = function( builder ) {
+	
+	return builder.getFormatByLength( this.getNumElements() );
+	
+};
+
+THREE.NodeJoin.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	
+	var type = this.getType( builder );
+	var length = this.getNumElements();
+	
+	var inputs = THREE.NodeJoin.inputs;
+	var outputs = [];
+	
+	for(var i = 0; i < length; i++) {
+	
+		var elm = this[inputs[i]];
+		
+		outputs.push( elm ? elm.build( builder, 'fv1' ) : '0.' );
+	
+	}
+	
+	var code = builder.getFormatConstructor(length) + '(' + outputs.join(',') + ')';
+	
+	return builder.format( code, type, output );
+
+};

+ 44 - 0
examples/materials/nodes/utils/NodeRoughnessToBlinnExponent.js

@@ -0,0 +1,44 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeRoughnessToBlinnExponent = function() {
+	
+	THREE.NodeTemp.call( this, 'fv1', {unique:true} );
+	
+};
+
+THREE.NodeRoughnessToBlinnExponent.prototype = Object.create( THREE.NodeTemp.prototype );
+THREE.NodeRoughnessToBlinnExponent.prototype.constructor = THREE.NodeRoughnessToBlinnExponent;
+
+THREE.NodeRoughnessToBlinnExponent.prototype.generate = function( builder, output ) {
+	
+	var material = builder.material;
+	
+	if (builder.isShader('fragment')) {
+		
+		if (material.isDefined('STANDARD')) {
+		
+			material.addFragmentNode('float specularMIPLevel = GGXRoughnessToBlinnExponent( 1.0 - material.specularRoughness );');
+			
+		}
+		else {
+			
+			console.warn("THREE.NodeRoughnessToBlinnExponent is compatible with StandardMaterial only");
+			
+			material.addFragmentNode('float specularMIPLevel = 0.0;');
+		
+		}
+		
+		return builder.format( 'specularMIPLevel', this.type, output );
+		
+	}
+	else {
+		
+		console.warn("THREE.NodeRoughnessToBlinnExponent is not compatible with " + builder.shader + " shader");
+		
+		return builder.format( '0.0', this.type, output );
+	
+	}
+
+};

+ 66 - 0
examples/materials/nodes/utils/NodeSwitch.js

@@ -0,0 +1,66 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeSwitch = function( a, component ) {
+	
+	THREE.NodeGL.call( this, 'fv1' );
+	
+	this.component = component || 'x';
+	
+	this.a = a;
+	
+};
+
+THREE.NodeSwitch.prototype = Object.create( THREE.NodeGL.prototype );
+THREE.NodeSwitch.prototype.constructor = THREE.NodeSwitch;
+
+THREE.NodeSwitch.prototype.getType = function( builder ) {
+	
+	return builder.getFormatByLength( this.component.length );
+	
+};
+
+THREE.NodeSwitch.prototype.generate = function( builder, output ) {
+	
+	var type = this.a.getType( builder );
+	var inputLength = builder.getFormatLength( type ) - 1;
+		
+	var a = this.a.build( builder, type );
+	
+	var outputLength = 0;
+	
+	var i, len = this.component.length;
+	
+	// get max length
+	
+	for (i = 0; i < len; i++) {
+		
+		outputLength = Math.max( outputLength, builder.getElementIndex( this.component.charAt(i) ) );
+		
+	}
+	
+	if (outputLength > inputLength) outputLength = inputLength;
+	
+	// build switch
+	
+	a += '.';
+	
+	for (i = 0; i < len; i++) {
+		
+		var elm = this.component.charAt(i);
+		var idx = builder.getElementIndex( this.component.charAt(i) );
+		
+		if (idx > outputLength) idx = outputLength;
+		
+		if (builder.getElementByIndex( idx ) == undefined) {
+			console.log( builder.getElementByIndex( idx ) );
+		}
+		
+		a += builder.getElementByIndex( idx );
+		
+	}
+	
+	return builder.format( a, this.type, output );
+
+};

+ 20 - 0
examples/materials/nodes/utils/NodeTime.js

@@ -0,0 +1,20 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeTime = function( value ) {
+	
+	THREE.NodeFloat.call( this, value );
+	
+	this.requestUpdate = true;
+	
+};
+
+THREE.NodeTime.prototype = Object.create( THREE.NodeFloat.prototype );
+THREE.NodeTime.prototype.constructor = THREE.NodeTime;
+
+THREE.NodeTime.prototype.updateAnimation = function( delta ) {
+	
+	this.number += delta;
+	
+};

+ 1320 - 0
examples/webgl_materials_nodes.html

@@ -0,0 +1,1320 @@
+<!doctype html>
+<html lang="en">
+	<head>
+		<title>WebGL NodeMaterial</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				color: #fff;
+				font-family:Monospace;
+				font-size:13px;
+				margin: 0px;
+				text-align:center;
+				overflow: hidden;
+			}
+
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				display:block;
+			}
+
+			a { color: white }
+		</style>
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - Node-Based Material
+		</div>
+
+		<script src="../build/three.min.js"></script>
+
+		<script src='js/geometries/TeapotBufferGeometry.js'></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<!-- NodeLibrary -->
+		<script src="js/nodes/GLNode.js"></script>
+		<script src="js/nodes/RawNode.js"></script>
+		<script src="js/nodes/TempNode.js"></script>
+		<script src="js/nodes/InputNode.js"></script>
+		<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/LibNode.js"></script>
+		<script src="js/nodes/NodeMaterial.js"></script>
+
+		<!-- Accessors -->
+		<script src="js/nodes/accessors/PositionNode.js"></script>
+		<script src="js/nodes/accessors/NormalNode.js"></script>
+		<script src="js/nodes/accessors/UVNode.js"></script>
+		<script src="js/nodes/accessors/ColorsNode.js"></script>
+		<script src="js/nodes/accessors/CameraNode.js"></script>
+		<script src="js/nodes/accessors/ReflectNode.js"></script>
+
+		<!-- Inputs -->
+		<script src="js/nodes/inputs/IntNode.js"></script>
+		<script src="js/nodes/inputs/FloatNode.js"></script>
+		<script src="js/nodes/inputs/ColorNode.js"></script>
+		<script src="js/nodes/inputs/Vector2Node.js"></script>
+		<script src="js/nodes/inputs/Vector3Node.js"></script>
+		<script src="js/nodes/inputs/Vector4Node.js"></script>
+		<script src="js/nodes/inputs/TextureNode.js"></script>
+		<script src="js/nodes/inputs/CubeTextureNode.js"></script>
+
+		<!-- Math -->
+		<script src="js/nodes/math/Math1Node.js"></script>
+		<script src="js/nodes/math/Math2Node.js"></script>
+		<script src="js/nodes/math/Math3Node.js"></script>
+		<script src="js/nodes/math/OperatorNode.js"></script>
+
+		<!-- Utils -->
+		<script src="js/nodes/utils/SwitchNode.js"></script>
+		<script src="js/nodes/utils/JoinNode.js"></script>
+		<script src="js/nodes/utils/TimeNode.js"></script>
+		<script src="js/nodes/utils/RoughnessToBlinnExponentNode.js"></script>
+		<script src="js/nodes/utils/VelocityNode.js"></script>
+
+		<!-- Phong Material -->
+		<script src="js/nodes/materials/PhongNode.js"></script>
+		<script src="js/nodes/materials/PhongNodeMaterial.js"></script>
+
+		<!-- Standard Material -->
+		<script src="js/nodes/materials/StandardNode.js"></script>
+		<script src="js/nodes/materials/StandardNodeMaterial.js"></script>
+
+		<script>
+
+		var container = document.getElementById( 'container' );
+
+		var renderer, scene, camera, clock = new THREE.Clock(), fov = 50;
+		var teapot, mesh;
+		var controls;
+		var move = false;
+		var gui, guiElements = [];
+
+		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' );
+
+		var decalDiffuse = new THREE.TextureLoader().load( 'textures/decal/decal-diffuse.png' );
+		decalDiffuse.wrapS = decalDiffuse.wrapT = THREE.RepeatWrapping;
+
+		var cloud = new THREE.TextureLoader().load( 'textures/lava/cloud.png' );
+		cloud.wrapS = cloud.wrapT = THREE.RepeatWrapping;
+
+		var cubemap = function() {
+
+			var path = "textures/cube/Park2/";
+			var format = '.jpg';
+			var urls = [
+					path + 'posx' + format, path + 'negx' + format,
+					path + 'posy' + format, path + 'negy' + format,
+					path + 'posz' + format, path + 'negz' + format
+				];
+
+			var textureCube = new THREE.CubeTextureLoader().load( urls );
+			textureCube.format = THREE.RGBFormat;
+
+			return textureCube;
+
+		}();
+
+		window.addEventListener( 'load', init );
+
+		function init() {
+
+			renderer = new THREE.WebGLRenderer( { antialias: true } );
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			container.appendChild( renderer.domElement );
+
+			scene = new THREE.Scene();
+
+			camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1000 );
+			camera.position.x = 50;
+			camera.position.z = - 50;
+			camera.position.y = 30;
+			camera.target = new THREE.Vector3();
+
+			controls = new THREE.OrbitControls( camera, renderer.domElement );
+			controls.minDistance = 50;
+			controls.maxDistance = 200;
+
+			scene.add( new THREE.AmbientLight( 0x464646 ) );
+
+			var light = new THREE.DirectionalLight( 0xffddcc, 1 );
+			light.position.set( 1, 0.75, 0.5 );
+			scene.add( light );
+
+			var light = new THREE.DirectionalLight( 0xccccff, 1 );
+			light.position.set( - 1, 0.75, - 0.5 );
+			scene.add( light );
+
+			teapot = new THREE.TeapotBufferGeometry( 15, 18 );
+
+			mesh = new THREE.Mesh( teapot );
+			scene.add( mesh );
+
+			window.addEventListener( 'resize', onWindowResize, false );
+
+			updateMaterial();
+
+			onWindowResize();
+			animate();
+
+		}
+
+		function clearGui() {
+
+			if ( gui ) gui.destroy();
+
+			gui = new dat.GUI();
+
+			var example = gui.add( param, 'example', {
+				'basic / standard (PBR)': 'standard',
+				'basic / phong': 'phong',
+				'basic / layers': 'layers',
+				'basic / rim': 'rim',
+				'adv / fresnel': 'fresnel',
+				'adv / saturation': 'saturation',
+				'adv / top-bottom': 'top-bottom',
+				'adv / caustic': 'caustic',
+				'adv / displace': 'displace',
+				'adv / camera-depth': 'camera-depth',
+				'adv / soft-body': 'soft-body',
+				'adv / wave': 'wave',
+				'misc / smoke' : 'smoke',
+				'misc / firefly' : 'firefly'
+			} ).onFinishChange( function() {
+
+				updateMaterial();
+
+			} );
+
+			gui.open();
+
+		}
+
+		function addGui( name, value, callback, isColor, min, max ) {
+
+			var node;
+
+			param[ name ] = value;
+
+			if ( isColor ) {
+
+				node = gui.addColor( param, name ).onChange( function() {
+
+					callback( param[ name ] );
+
+				} );
+
+			}
+			else if (typeof value == 'object') {
+
+				node = gui.add( param, name, value ).onChange( function() {
+
+					callback( param[ name ] );
+
+				} );
+
+			}
+			else {
+
+				node = gui.add( param, name, min, max ).onChange( function() {
+
+					callback( param[ name ] );
+
+				} );
+
+			}
+
+			return node;
+
+		}
+
+		function updateMaterial() {
+
+			move = false;
+
+			if ( mesh.material ) mesh.material.dispose();
+
+			var name = param.example;
+			var mtl;
+
+			clearGui();
+
+			switch ( name ) {
+
+				case 'phong':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					//mtl.color = // albedo color
+					//mtl.alpha = // opacity (0 at 1)
+					//mtl.specular = // specular color
+					//mtl.shininess = // shininess (float)
+					//mtl.normal = // normalmap
+					//mtl.normalScale = // normalmap scale
+					//mtl.emissive = // emissive color
+					//mtl.ambient = // ambient color
+					//mtl.shadow = // shadowmap
+					//mtl.ao = // ambient occlusion
+					//mtl.environment = // reflection map (CubeMap recommended)
+					//mtl.reflectivity = // environment intensity
+					//mtl.transform = // vertex transformation
+
+					var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
+
+					mtl.color = new THREE.TextureNode( grass );
+					mtl.specular = new THREE.FloatNode( .5 );
+					mtl.shininess = new THREE.FloatNode( 15 );
+					mtl.environment = new THREE.CubeTextureNode( cubemap );
+					mtl.reflectivity = mask;
+					mtl.normal = new THREE.TextureNode( grassNormal );
+					mtl.normalScale = new THREE.Math1Node( mask, THREE.Math1Node.INVERT );
+
+					break;
+
+				case 'standard':
+
+					// MATERIAL
+
+					mtl = new THREE.StandardNodeMaterial();
+
+					//mtl.color = // albedo color
+					//mtl.alpha = // opacity (0 at 1)
+					//mtl.roughness = // roughness (float)
+					//mtl.metalness = // metalness (float)
+					//mtl.normal = // normalmap
+					//mtl.normalScale = // normalmap scale
+					//mtl.emissive = // emissive color
+					//mtl.ambient = // ambient color
+					//mtl.shadow = // shadowmap
+					//mtl.ao = // ambient occlusion
+					//mtl.environment = // reflection map (CubeMap recommended)
+					//mtl.reflectivity = // environment intensity
+					//mtl.transform = // vertex transformation
+
+					var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
+
+					var normalScale = new THREE.FloatNode( .3 );
+
+					var roughnessA = new THREE.FloatNode( .5 );
+					var metalnessA = new THREE.FloatNode( .5 );
+
+					var roughnessB = new THREE.FloatNode( 0 );
+					var metalnessB = new THREE.FloatNode( 1 );
+
+					var roughness = new THREE.Math3Node(
+						roughnessA,
+						roughnessB,
+						mask,
+						THREE.Math3Node.MIX
+					);
+
+					var metalness = new THREE.Math3Node(
+						metalnessA,
+						metalnessB,
+						mask,
+						THREE.Math3Node.MIX
+					);
+
+					var normalMask = new THREE.OperatorNode(
+						new THREE.Math1Node( mask, THREE.Math1Node.INVERT ),
+						normalScale,
+						THREE.OperatorNode.MUL
+					);
+
+					mtl.color = new THREE.ColorNode( 0xFFFFFF );
+					mtl.roughness = roughness;
+					mtl.metalness = metalness;
+					mtl.environment = new THREE.CubeTextureNode( cubemap );
+					mtl.normal = new THREE.TextureNode( grassNormal );
+					mtl.normalScale = normalMask;
+
+					// GUI
+
+					addGui( 'color', mtl.color.value.getHex(), function( val ) {
+
+						mtl.color.value.setHex( val );
+
+					}, true );
+
+					addGui( 'roughnessA', roughnessA.number, function( val ) {
+
+						 roughnessA.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'metalnessA', metalnessA.number, function( val ) {
+
+						metalnessA.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'roughnessB', roughnessB.number, function( val ) {
+
+						 roughnessB.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'metalnessB', metalnessB.number, function( val ) {
+
+						metalnessB.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'normalScale', normalScale.number, function( val ) {
+
+						normalScale.number = val;
+
+					}, false, 0, 1 );
+
+					break;
+
+				case 'wave':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var time = new THREE.TimeNode();
+					var speed = new THREE.FloatNode( 5 );
+					var scale = new THREE.FloatNode( 1 );
+					var worldScale = new THREE.FloatNode( .4 );
+					var colorA = new THREE.ColorNode( 0xFFFFFF );
+					var colorB = new THREE.ColorNode( 0x0054df );
+
+					var uv = new THREE.UVNode();
+
+					var timeScale = new THREE.OperatorNode(
+						time,
+						speed,
+						THREE.OperatorNode.MUL
+					);
+
+					var worldScl = new THREE.OperatorNode(
+						new THREE.PositionNode(),
+						worldScale,
+						THREE.OperatorNode.MUL
+					);
+
+					var posContinuous = new THREE.OperatorNode(
+						worldScl,
+						timeScale,
+						THREE.OperatorNode.ADD
+					);
+
+					var wave = new THREE.Math1Node( posContinuous, THREE.Math1Node.SIN );
+					wave = new THREE.SwitchNode( wave, 'x' );
+
+					var waveScale = new THREE.OperatorNode(
+						wave,
+						scale,
+						THREE.OperatorNode.MUL
+					);
+
+					var displaceY = new THREE.JoinNode(
+						new THREE.FloatNode(),
+						waveScale,
+						new THREE.FloatNode()
+					);
+
+					var displace = new THREE.OperatorNode(
+						new THREE.NormalNode(),
+						displaceY,
+						THREE.OperatorNode.MUL
+					);
+
+					var blend = new THREE.OperatorNode(
+						new THREE.PositionNode(),
+						displaceY,
+						THREE.OperatorNode.ADD
+					);
+
+					var color = new THREE.Math3Node(
+						colorB,
+						colorA,
+						wave,
+						THREE.Math3Node.MIX
+					);
+
+					mtl.color = color;
+					mtl.transform = blend;
+
+					// GUI
+
+					addGui( 'speed', speed.number, function( val ) {
+
+						speed.number = val;
+
+					}, false, 0, 10 );
+
+					addGui( 'scale', scale.number, function( val ) {
+
+						scale.number = val;
+
+					}, false, 0, 3 );
+
+					addGui( 'worldScale', worldScale.number, function( val ) {
+
+						worldScale.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'colorA', colorA.value.getHex(), function( val ) {
+
+						colorA.value.setHex( val );
+
+					}, true );
+
+					addGui( 'colorB', colorB.value.getHex(), function( val ) {
+
+						colorB.value.setHex( val );
+
+					}, true );
+
+					addGui( 'useNormals', false, function( val ) {
+
+						blend.b = val ? displace : displaceY;
+
+						mtl.build();
+
+					} );
+
+					break;
+
+				case 'rim':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var intensity = 1.3;
+					var power = new THREE.FloatNode( 3 );
+					var color = new THREE.ColorNode( 0xFFFFFF );
+
+					var viewZ = new THREE.Math2Node(
+						new THREE.NormalNode( THREE.NormalNode.VIEW ),
+						new THREE.Vector3Node( 0, 0, - intensity ),
+						THREE.Math2Node.DOT
+					);
+
+					var rim = new THREE.OperatorNode(
+						viewZ,
+						new THREE.FloatNode( intensity ),
+						THREE.OperatorNode.ADD
+					);
+
+					var rimPower = new THREE.Math2Node(
+						rim,
+						power,
+						THREE.Math2Node.POW
+					);
+
+					var rimColor = new THREE.OperatorNode(
+						rimPower,
+						color,
+						THREE.OperatorNode.MUL
+					);
+
+					mtl.color = new THREE.ColorNode( 0x111111 );
+					mtl.emissive = rimColor;
+
+					// GUI
+
+					addGui( 'color', color.value.getHex(), function( val ) {
+
+						color.value.setHex( val );
+
+					}, true );
+
+					addGui( 'intensity', intensity, function( val ) {
+
+						intensity = val;
+
+						viewZ.b.z = - intensity;
+						rim.b.number = intensity;
+
+
+					}, false, 0, 3 );
+
+					addGui( 'power', power.number, function( val ) {
+
+						power.number = val;
+
+					}, false, 0, 6 );
+
+					addGui( 'xray', false, function( val ) {
+
+						if ( val ) {
+
+							mtl.emissive = color;
+							mtl.alpha = rimPower;
+							mtl.blending = THREE.AdditiveBlending;
+							mtl.depthWrite = false;
+
+						}
+						else {
+
+							mtl.emissive = rimColor;
+							mtl.alpha = null;
+							mtl.blending = THREE.NormalBlending;
+							mtl.depthWrite = true;
+
+						}
+
+						mtl.build();
+
+					} );
+
+					break;
+
+				case 'fresnel':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var reflectance = new THREE.FloatNode( 1.3 );
+					var power = new THREE.FloatNode( 1 );
+					var color = new THREE.CubeTextureNode( cubemap );
+
+					var viewZ = new THREE.Math2Node(
+						new THREE.NormalNode( THREE.NormalNode.VIEW ),
+						new THREE.Vector3Node( 0, 0, - 1 ),
+						THREE.Math2Node.DOT
+					);
+
+					var theta = new THREE.OperatorNode(
+						viewZ,
+						new THREE.FloatNode( 1 ),
+						THREE.OperatorNode.ADD
+					);
+
+					var thetaPower = new THREE.Math2Node(
+						theta,
+						power,
+						THREE.Math2Node.POW
+					);
+
+					var fresnel = new THREE.OperatorNode(
+						reflectance,
+						thetaPower,
+						THREE.OperatorNode.MUL
+					);
+
+					mtl.color = new THREE.ColorNode( 0x3399FF );
+					mtl.environment = color;
+					mtl.reflectivity = new THREE.Math1Node( fresnel, THREE.Math1Node.SAT );
+
+					// GUI
+
+					addGui( 'reflectance', reflectance.number, function( val ) {
+
+						reflectance.number = val;
+
+					}, false, 0, 3 );
+
+					addGui( 'power', power.number, function( val ) {
+
+						power.number = val;
+
+					}, false, 0, 5 );
+
+					break;
+
+				case 'layers':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var tex1 = new THREE.TextureNode( grass );
+					var tex2 = new THREE.TextureNode( brick );
+
+					var offset = new THREE.FloatNode( 0 );
+					var scale = new THREE.FloatNode( 1 );
+					var uv = new THREE.UVNode();
+
+					var uvOffset = new THREE.OperatorNode(
+						offset,
+						uv,
+						THREE.OperatorNode.ADD
+					);
+
+					var uvScale = new THREE.OperatorNode(
+						uvOffset,
+						scale,
+						THREE.OperatorNode.MUL
+					);
+
+					var mask = new THREE.TextureNode( decalDiffuse, uvScale );
+					var maskAlphaChannel = new THREE.SwitchNode( mask, 'w' );
+
+					var blend = new THREE.Math3Node(
+						tex1,
+						tex2,
+						maskAlphaChannel,
+						THREE.Math3Node.MIX
+					);
+
+					mtl.color = blend;
+
+					// GUI
+
+					addGui( 'offset', offset.number, function( val ) {
+
+						offset.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'scale', scale.number, function( val ) {
+
+						scale.number = val;
+
+					}, false, 0, 10 );
+
+					break;
+
+				case 'saturation':
+
+					// MATERIAL
+
+					mtl = new THREE.StandardNodeMaterial();
+
+					var tex = new THREE.TextureNode( 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);",
+					"}"
+					].join( "\n" ) );
+
+					var saturation = new THREE.FunctionCallNode( satrgb );
+					saturation.input.rgb = tex;
+					saturation.input.adjustment = sat;
+
+					// or try
+
+					//saturation.input[0] = tex;
+					//saturation.input[1] = sat;
+
+					mtl.color = saturation;
+					mtl.environment = new THREE.CubeTextureNode( cubemap ); // optional
+
+					// GUI
+
+					addGui( 'saturation', sat.number, function( val ) {
+
+						sat.number = val;
+
+					}, false, 0, 2 );
+
+					break;
+
+				case 'top-bottom':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var top = new THREE.TextureNode( grass );
+					var bottom = new THREE.TextureNode( brick );
+
+					var normal = new THREE.NormalNode( THREE.NormalNode.WORLD );
+					var normalY = new THREE.SwitchNode( normal, 'y' );
+
+					var hard = new THREE.FloatNode( 9 );
+					var offset = new THREE.FloatNode( - 2.5 );
+
+					var hardClamp = new THREE.OperatorNode(
+						normalY,
+						hard,
+						THREE.OperatorNode.MUL
+					);
+
+					var offsetClamp = new THREE.OperatorNode(
+						hardClamp,
+						offset,
+						THREE.OperatorNode.ADD
+					);
+
+					var clamp0at1 = new THREE.Math1Node( offsetClamp, THREE.Math1Node.SAT );
+
+					var blend = new THREE.Math3Node( top, bottom, clamp0at1, THREE.Math3Node.MIX );
+
+					mtl.color = blend;
+
+					// GUI
+
+					addGui( 'hard', hard.number, function( val ) {
+
+						hard.number = val;
+
+					}, false, 0, 20 );
+
+					addGui( 'offset', offset.number, function( val ) {
+
+						offset.number = val;
+
+					}, false, - 10, 10 );
+
+					break;
+
+				case 'displace':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var time = new THREE.TimeNode();
+					var scale = new THREE.FloatNode( 2 );
+					var speed = new THREE.FloatNode( .2 );
+					var colorA = new THREE.ColorNode( 0xFFFFFF );
+					var colorB = new THREE.ColorNode( 0x0054df );
+
+					var uv = new THREE.UVNode();
+
+					var timeScl = new THREE.OperatorNode(
+						time,
+						speed,
+						THREE.OperatorNode.MUL
+					);
+
+					var displaceOffset = new THREE.OperatorNode(
+						timeScl,
+						uv,
+						THREE.OperatorNode.ADD
+					);
+
+					var tex = new THREE.TextureNode( cloud, displaceOffset );
+					var texArea = new THREE.SwitchNode( tex, 'w' );
+
+					var displace = new THREE.OperatorNode(
+						new THREE.NormalNode(),
+						texArea,
+						THREE.OperatorNode.MUL
+					);
+
+					var displaceScale = new THREE.OperatorNode(
+						displace,
+						scale,
+						THREE.OperatorNode.MUL
+					);
+
+					var blend = new THREE.OperatorNode(
+						new THREE.PositionNode(),
+						displaceScale,
+						THREE.OperatorNode.ADD
+					);
+
+					var color = new THREE.Math3Node(
+						colorB,
+						colorA,
+						texArea,
+						THREE.Math3Node.MIX
+					);
+
+					mtl.color = mtl.specular = new THREE.ColorNode( 0 );
+					mtl.emissive = color;
+					mtl.transform = blend;
+
+					// GUI
+
+					addGui( 'speed', speed.number, function( val ) {
+
+						speed.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'scale', scale.number, function( val ) {
+
+						scale.number = val;
+
+					}, false, 0, 10 );
+
+					addGui( 'colorA', colorA.value.getHex(), function( val ) {
+
+						colorA.value.setHex( val );
+
+					}, true );
+
+					addGui( 'colorB', colorB.value.getHex(), function( val ) {
+
+						colorB.value.setHex( val );
+
+					}, true );
+
+					break;
+
+				case 'smoke':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var time = new THREE.TimeNode();
+					var uv = new THREE.UVNode();
+
+					var timeSpeedA = new THREE.OperatorNode(
+						time,
+						new THREE.Vector2Node( 0.3, 0.1 ),
+						THREE.OperatorNode.MUL
+					);
+
+					var timeSpeedB = new THREE.OperatorNode(
+						time,
+						new THREE.Vector2Node( 0.15, 0.4 ),
+						THREE.OperatorNode.MUL
+					);
+
+					var uvOffsetA = new THREE.OperatorNode(
+						timeSpeedA,
+						uv,
+						THREE.OperatorNode.ADD
+					);
+
+					var uvOffsetB = new THREE.OperatorNode(
+						timeSpeedB,
+						uv,
+						THREE.OperatorNode.ADD
+					);
+
+					var cloudA = new THREE.TextureNode( cloud, uvOffsetA );
+					var cloudB = new THREE.TextureNode( cloud, uvOffsetB );
+
+					var clouds = new THREE.OperatorNode(
+						cloudA,
+						cloudB,
+						THREE.OperatorNode.ADD
+					);
+
+					mtl.environment = new THREE.ColorNode( 0xFFFFFF );
+					mtl.alpha = clouds;
+
+					// GUI
+
+					addGui( 'color', mtl.environment.value.getHex(), function( val ) {
+
+						mtl.environment.value.setHex( val );
+
+					}, true );
+
+					break;
+
+				case 'camera-depth':
+
+					// MATERIAL
+
+					var colorA = new THREE.ColorNode( 0xFFFFFF );
+					var colorB = new THREE.ColorNode( 0x0054df );
+
+					var depth = new THREE.CameraNode( THREE.CameraNode.DEPTH );
+					depth.near.number = 1;
+					depth.far.number = 200;
+
+					var colors = new THREE.Math3Node(
+						colorB,
+						colorA,
+						depth,
+						THREE.Math3Node.MIX
+					);
+
+					mtl = new THREE.PhongNodeMaterial();
+					mtl.color = colors;
+
+					// GUI
+
+					addGui( 'near', depth.near.number, function( val ) {
+
+						depth.near.number = val;
+
+					}, false, 1, 1200 );
+
+					addGui( 'far', depth.far.number, function( val ) {
+
+						depth.far.number = val;
+
+					}, false, 1, 1200 );
+
+					addGui( 'nearColor', colorA.value.getHex(), function( val ) {
+
+						colorA.value.setHex( val );
+
+					}, true );
+
+					addGui( 'farColor', colorB.value.getHex(), function( val ) {
+
+						colorB.value.setHex( val );
+
+					}, true );
+
+					break;
+
+				case 'caustic':
+
+					// MATERIAL
+
+					mtl = new THREE.StandardNodeMaterial();
+
+					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);",
+					"}"
+					].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;",
+					"}"
+					].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;",
+					"}"
+					].join( "\n" ), [ voronoi ] ); // define voronoi as dependencies
+
+					var time = new THREE.TimeNode();
+					var timeScale = new THREE.FloatNode( 2 );
+
+					var alpha = new THREE.FloatNode( 1 );
+					var scale = new THREE.FloatNode( .1 );
+					var intensity = new THREE.FloatNode( 1.5 );
+
+					var color = new THREE.ColorNode( 0xFFFFFF );
+					var colorA = new THREE.ColorNode( 0xFFFFFF );
+					var colorB = new THREE.ColorNode( 0x0054df );
+
+					var worldPos = new THREE.PositionNode( THREE.PositionNode.WORLD );
+					var worldPosTop = new THREE.SwitchNode( worldPos, 'xz' );
+
+					var worldNormal = new THREE.NormalNode( THREE.NormalNode.WORLD );
+
+					var mask = new THREE.SwitchNode( worldNormal, 'y' );
+
+					// clamp0at1
+					mask = new THREE.Math1Node( mask, THREE.Math1Node.SAT );
+
+					var timeOffset = new THREE.OperatorNode(
+						time,
+						timeScale,
+						THREE.OperatorNode.MUL
+					);
+
+					var uvPos = new THREE.OperatorNode(
+						worldPosTop,
+						scale,
+						THREE.OperatorNode.MUL
+					);
+
+					var voronoi = new THREE.FunctionCallNode( voronoiLayers );
+					voronoi.input.p = uvPos;
+					voronoi.input.time = timeOffset;
+
+					var maskCaustic = new THREE.OperatorNode(
+						alpha,
+						mask,
+						THREE.OperatorNode.MUL
+					);
+
+					var voronoiIntensity = new THREE.OperatorNode(
+						voronoi,
+						intensity,
+						THREE.OperatorNode.MUL
+					);
+
+					var voronoiColors = new THREE.Math3Node(
+						colorB,
+						colorA,
+						new THREE.Math1Node( voronoiIntensity, THREE.Math1Node.SAT ), // mix needs clamp
+						THREE.Math3Node.MIX
+					);
+
+					var caustic = new THREE.Math3Node(
+						color,
+						voronoiColors,
+						maskCaustic,
+						THREE.Math3Node.MIX
+					);
+
+					var causticLights = new THREE.OperatorNode(
+						voronoiIntensity,
+						maskCaustic,
+						THREE.OperatorNode.MUL
+					);
+
+					mtl.color = caustic;
+					mtl.ambient = causticLights;
+
+					// GUI
+
+					addGui( 'timeScale', timeScale.number, function( val ) {
+
+						timeScale.number = val;
+
+					}, false, 0, 5 );
+
+					addGui( 'intensity', intensity.number, function( val ) {
+
+						intensity.number = val;
+
+					}, false, 0, 3 );
+
+					addGui( 'scale', scale.number, function( val ) {
+
+						scale.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'alpha', alpha.number, function( val ) {
+
+						alpha.number = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'color', color.value.getHex(), function( val ) {
+
+						color.value.setHex( val );
+
+					}, true );
+
+					addGui( 'colorA', colorA.value.getHex(), function( val ) {
+
+						colorA.value.setHex( val );
+
+					}, true );
+
+					addGui( 'colorB', colorB.value.getHex(), function( val ) {
+
+						colorB.value.setHex( val );
+
+					}, true );
+
+					break;
+
+				case 'soft-body':
+
+					// MATERIAL
+
+					move = true;
+
+					mtl = new THREE.StandardNodeMaterial();
+
+					var scale = new THREE.FloatNode( 2 );
+					var colorA = new THREE.ColorNode( 0xFF6633 );
+					var colorB = new THREE.ColorNode( 0x3366FF );
+
+					var pos = new THREE.PositionNode();
+					var posNorm = new THREE.Math1Node( pos, THREE.Math1Node.NORMALIZE );
+
+					var mask = new THREE.SwitchNode( posNorm, 'y' );
+
+					var velocity = new THREE.VelocityNode( mesh, {
+						type: 'elastic',
+						spring: .8,
+						friction: .9
+					} );
+
+					var velocityArea = new THREE.OperatorNode(
+						mask,
+						scale,
+						THREE.OperatorNode.MUL
+					);
+
+					var softVelocity = new THREE.OperatorNode(
+						velocity,
+						velocityArea,
+						THREE.OperatorNode.MUL
+					);
+
+					var softPosition = new THREE.OperatorNode(
+						new THREE.PositionNode(),
+						softVelocity,
+						THREE.OperatorNode.ADD
+					);
+
+					var colors = new THREE.Math3Node(
+						colorB,
+						colorA,
+						mask,
+						THREE.Math3Node.MIX
+					);
+
+					mtl.color = colors;
+					mtl.transform = softPosition;
+
+					// GUI
+
+					addGui( 'spring', velocity.params.spring, function( val ) {
+
+						velocity.params.spring = val;
+
+					}, false, 0, .9 );
+
+					addGui( 'friction', velocity.params.friction, function( val ) {
+
+						velocity.params.friction = val;
+
+					}, false, 0, .9 );
+
+					addGui( 'scale', scale.number, function( val ) {
+
+						scale.number = val;
+
+					}, false, 0, 3 );
+
+					addGui( 'softBody', colorA.value.getHex(), function( val ) {
+
+						colorA.value.setHex( val );
+
+					}, true );
+
+					addGui( 'hardBody', colorB.value.getHex(), function( val ) {
+
+						colorB.value.setHex( val );
+
+					}, true );
+
+					break;
+
+				case 'firefly':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
+					var time = new THREE.TimeNode();
+					var speed = new THREE.FloatNode( .5 );
+
+					var color = new THREE.ColorNode( 0x98ff00 );
+
+					var timeSpeed = new THREE.OperatorNode(
+						time,
+						speed,
+						THREE.OperatorNode.MUL
+					);
+
+					var sinCycleInSecs = new THREE.OperatorNode(
+						timeSpeed,
+						new THREE.ConstNode( THREE.ConstNode.PI2 ),
+						THREE.OperatorNode.MUL
+					);
+
+					var cycle = new THREE.Math1Node( sinCycleInSecs, THREE.Math1Node.SIN );
+
+					var cycleColor = new THREE.OperatorNode(
+						cycle,
+						color,
+						THREE.OperatorNode.MUL
+					);
+
+					var cos = new THREE.Math1Node( cycleColor, THREE.Math1Node.SIN );
+
+					mtl.color = new THREE.ColorNode( 0 );
+					mtl.emissive = cos;
+
+					// GUI
+
+					addGui( 'speed', speed.number, function( val ) {
+
+						speed.number = val;
+
+					}, false, 0, 3 );
+
+					break;
+			}
+
+			// build shader
+			mtl.build();
+
+			// set material
+			mesh.material = mtl;
+
+		}
+
+		function onWindowResize() {
+
+			camera.aspect = window.innerWidth / window.innerHeight;
+			camera.updateProjectionMatrix();
+
+			renderer.setSize( window.innerWidth, window.innerHeight );
+
+		}
+
+		function animate() {
+
+			var delta = clock.getDelta();
+
+			if ( move ) {
+
+				var time = Date.now() * 0.005;
+
+				mesh.position.z = Math.cos( time ) * 10;
+				mesh.position.y = Math.sin( time ) * 10;
+
+			}
+			else {
+
+				mesh.position.z = mesh.position.y = 0;
+
+			}
+
+			//mesh.rotation.z += .01;
+
+			// update material animation and/or gpu calcs (pre-renderer)
+			mesh.material.updateAnimation( delta );
+
+			renderer.render( scene, camera );
+
+			requestAnimationFrame( animate );
+
+		}
+
+		</script>
+
+	</body>
+</html>

+ 528 - 0
examples/webgl_postprocessing_nodes.html

@@ -0,0 +1,528 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - postprocessing</title>
+		<meta charset="utf-8">
+		<style>
+			body {
+				margin: 0px;
+				background-color: #000;
+				overflow: hidden;
+				font-family:Monospace;
+				font-size:13px;
+				margin: 0px;
+				text-align:center;
+				overflow: hidden;
+			}
+			
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				display:block;
+			}
+		</style>
+	</head>
+	<body>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank">three.js</a> - Node-Based Post-Processing
+		</div>
+	
+		<script src="../build/three.min.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<script src="js/shaders/CopyShader.js"></script>
+
+		<script src="js/postprocessing/EffectComposer.js"></script>
+		<script src="js/postprocessing/RenderPass.js"></script>
+		<script src="js/postprocessing/MaskPass.js"></script>
+		<script src="js/postprocessing/ShaderPass.js"></script>
+
+		<!-- NodeLibrary -->
+		<script src="js/nodes/GLNode.js"></script>
+		<script src="js/nodes/RawNode.js"></script>
+		<script src="js/nodes/TempNode.js"></script>
+		<script src="js/nodes/InputNode.js"></script>
+		<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/LibNode.js"></script>
+		<script src="js/nodes/NodeMaterial.js"></script>
+
+		<!-- Accessors -->
+		<script src="js/nodes/accessors/PositionNode.js"></script>
+		<script src="js/nodes/accessors/NormalNode.js"></script>
+		<script src="js/nodes/accessors/UVNode.js"></script>
+		<script src="js/nodes/accessors/ColorsNode.js"></script>
+
+		<!-- Inputs -->
+		<script src="js/nodes/inputs/IntNode.js"></script>
+		<script src="js/nodes/inputs/FloatNode.js"></script>
+		<script src="js/nodes/inputs/ColorNode.js"></script>
+		<script src="js/nodes/inputs/Vector2Node.js"></script>
+		<script src="js/nodes/inputs/Vector3Node.js"></script>
+		<script src="js/nodes/inputs/Vector4Node.js"></script>
+		<script src="js/nodes/inputs/TextureNode.js"></script>
+		<script src="js/nodes/inputs/CubeTextureNode.js"></script>
+		<script src="js/nodes/inputs/ScreenNode.js"></script>
+
+		<!-- Math -->
+		<script src="js/nodes/math/Math1Node.js"></script>
+		<script src="js/nodes/math/Math2Node.js"></script>
+		<script src="js/nodes/math/Math3Node.js"></script>
+		<script src="js/nodes/math/OperatorNode.js"></script>
+
+		<!-- Utils -->
+		<script src="js/nodes/utils/SwitchNode.js"></script>
+		<script src="js/nodes/utils/JoinNode.js"></script>
+		<script src="js/nodes/utils/TimeNode.js"></script>
+		<script src="js/nodes/utils/NormalMapNode.js"></script>
+
+		<!-- Post-Processing -->
+		<script src="js/nodes/postprocessing/NodePass.js"></script>
+
+		<script>
+
+			var camera, scene, renderer, composer;
+			var object, light, nodepass;
+			var gui, guiElements = [];
+
+			var param = { example: 'brightness' };
+
+			var lensflare2 = new THREE.TextureLoader().load( 'textures/lensflare2.jpg' );
+			lensflare2.wrapS = lensflare2.wrapT = THREE.RepeatWrapping;
+
+			var decalNormal = new THREE.TextureLoader().load( 'textures/decal/decal-normal.jpg' );
+			decalNormal.wrapS = decalNormal.wrapT = THREE.RepeatWrapping;
+
+			init();
+			animate();
+
+			function clearGui() {
+
+				if ( gui ) gui.destroy();
+
+				gui = new dat.GUI();
+
+				var example = gui.add( param, 'example', {
+					'basic / brightness': 'brightness',
+					'basic / blends': 'blends',
+					'basic / fade': 'fade',
+					'basic / invert': 'invert',
+					'adv / saturation': 'saturation',
+					'adv / refraction': 'refraction',
+					'adv / mosaic': 'mosaic'
+				} ).onFinishChange( function() {
+
+					updateMaterial();
+
+				} );
+
+				gui.open();
+
+			}
+
+			function addGui( name, value, callback, isColor, min, max ) {
+
+				var node;
+
+				param[ name ] = value;
+
+				if ( isColor ) {
+
+					node = gui.addColor( param, name ).onChange( function() {
+
+						callback( param[ name ] );
+
+					} );
+
+				}
+				else if ( typeof value == 'object' ) {
+
+					node = gui.add( param, name, value ).onChange( function() {
+
+						callback( param[ name ] );
+
+					} );
+
+				}
+				else {
+
+					node = gui.add( param, name, min, max ).onChange( function() {
+
+						callback( param[ name ] );
+
+					} );
+
+				}
+
+				return node;
+
+			}
+
+			function updateMaterial() {
+
+				var name = param.example;
+
+				clearGui();
+
+				switch ( name ) {
+
+					case 'brightness':
+
+						var screen = new THREE.ScreenNode();
+
+						var brightness = new THREE.FloatNode( - 1 );
+						var contrast = new THREE.FloatNode( 3 );
+
+						var contrastResult = new THREE.OperatorNode(
+							screen,
+							contrast,
+							THREE.OperatorNode.MUL
+						);
+
+						var brightnessResult = new THREE.OperatorNode(
+							contrastResult,
+							brightness,
+							THREE.OperatorNode.ADD
+						);
+
+						nodepass.value = brightnessResult;
+
+						// GUI
+
+						addGui( 'brightness', brightness.number, function( val ) {
+
+							brightness.number = val;
+
+						}, false, - 1, 2 );
+
+						addGui( 'contrast', contrast.number, function( val ) {
+
+							contrast.number = val;
+
+						}, false, 0, 4 );
+
+					break;
+
+					case 'fade':
+
+						// PASS
+
+						var color = new THREE.ColorNode( 0xFFFFFF );
+						var percent = new THREE.FloatNode( .5 );
+
+						var fade = new THREE.Math3Node(
+							new THREE.ScreenNode(),
+							color,
+							percent,
+							THREE.Math3Node.MIX
+						);
+
+						nodepass.value = fade;
+
+						// GUI
+
+						addGui( 'color', color.value.getHex(), function( val ) {
+
+							color.value.setHex( val );
+
+						}, true );
+
+						addGui( 'fade', percent.number, function( val ) {
+
+							percent.number = val;
+
+						}, false, 0, 1 );
+
+					break;
+
+					case 'invert':
+
+						// PASS
+
+						var alpha = new THREE.FloatNode( 1 );
+
+						var screen = new THREE.ScreenNode();
+						var inverted = new THREE.Math1Node( screen, THREE.Math1Node.INVERT );
+
+						var fade = new THREE.Math3Node(
+							screen,
+							inverted,
+							alpha,
+							THREE.Math3Node.MIX
+						);
+
+						nodepass.value = fade;
+
+						// GUI
+
+						addGui( 'alpha', alpha.number, function( val ) {
+
+							alpha.number = val;
+
+						}, false, 0, 1 );
+
+					break;
+
+					case 'blends':
+
+						// PASS
+
+						var multiply = new THREE.OperatorNode(
+							new THREE.ScreenNode(),
+							new THREE.TextureNode( lensflare2 ),
+							THREE.OperatorNode.ADD
+						);
+
+						nodepass.value = multiply;
+
+						// GUI
+
+						addGui( 'blend', {
+							'addition' : THREE.OperatorNode.ADD,
+							'subtract' : THREE.OperatorNode.SUB,
+							'multiply' : THREE.OperatorNode.MUL,
+							'division' : THREE.OperatorNode.DIV
+						}, function( val ) {
+
+							multiply.op = val;
+
+							nodepass.build();
+
+						} );
+
+					break;
+
+					case 'saturation':
+
+						// PASS
+
+						var screen = new THREE.ScreenNode();
+						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);",
+						"}"
+						].join( "\n" ) );
+
+						var saturation = new THREE.FunctionCallNode( satrgb );
+						saturation.input.rgb = screen;
+						saturation.input.adjustment = sat;
+
+						nodepass.value = saturation;
+
+						// GUI
+
+						addGui( 'saturation', sat.number, function( val ) {
+
+							sat.number = val;
+
+						}, false, 0, 2 );
+
+					break;
+
+					case 'refraction':
+
+						// PASS
+
+						var normal = new THREE.TextureNode( decalNormal );
+						var normalY = new THREE.SwitchNode( normal, 'y' );
+
+						var offsetNormal = new THREE.OperatorNode(
+							normalY,
+							new THREE.FloatNode( .5 ),
+							THREE.OperatorNode.ADD
+						);
+
+						var scale = new THREE.FloatNode( .5 );
+
+						var scaleNormal = new THREE.Math3Node(
+							new THREE.FloatNode( 1 ),
+							offsetNormal,
+							scale,
+							THREE.Math3Node.MIX
+						);
+
+						var offsetCoord = new THREE.OperatorNode(
+							new THREE.UVNode(),
+							scaleNormal,
+							THREE.OperatorNode.MUL
+						);
+
+						var screen = new THREE.ScreenNode( offsetCoord );
+
+						nodepass.value = screen;
+
+						// GUI
+
+						addGui( 'scale', scale.number, function( val ) {
+
+							scale.number = val;
+
+						}, false, 0, 1 );
+
+					break;
+
+					case 'mosaic':
+
+						// PASS
+
+						var scale = new THREE.FloatNode( 128 );
+						var fade = new THREE.FloatNode( 1 );
+						var uv = new THREE.UVNode();
+						var tex = new THREE.TextureNode( lensflare2 );
+
+						var mask = new THREE.Math1Node( new THREE.SwitchNode( tex, 'x' ), THREE.Math1Node.INVERT );
+
+						var blocks = new THREE.OperatorNode(
+							uv,
+							scale,
+							THREE.OperatorNode.MUL
+						);
+
+						var blocksSize = new THREE.Math1Node(
+							blocks,
+							THREE.Math1Node.FLOOR
+						);
+
+						var coord = new THREE.OperatorNode(
+							blocksSize,
+							scale,
+							THREE.OperatorNode.DIV
+						);
+
+						var maskAlpha = new THREE.OperatorNode(
+							mask,
+							fade,
+							THREE.OperatorNode.MUL
+						);
+
+						var fadeCoord = new THREE.Math3Node(
+							uv,
+							coord,
+							maskAlpha,
+							THREE.Math3Node.MIX
+						);
+
+						var screen = new THREE.ScreenNode( fadeCoord );
+
+						nodepass.value = screen;
+
+						// GUI
+
+						addGui( 'scale', scale.number, function( val ) {
+
+							scale.number = val;
+
+						}, false, 16, 1024 );
+
+						addGui( 'fade', fade.number, function( val ) {
+
+							fade.number = val;
+
+						}, false, 0, 1 );
+
+						addGui( 'mask', true, function( val ) {
+
+							fadeCoord.c = val ? maskAlpha : fade;
+
+							nodepass.build();
+
+						}, false, 0, 1 );
+
+					break;
+
+				}
+
+				nodepass.build();
+
+			}
+
+			function init() {
+
+				renderer = new THREE.WebGLRenderer();
+				renderer.setPixelRatio( window.devicePixelRatio );
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				document.body.appendChild( renderer.domElement );
+
+				//
+
+				camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 1000 );
+				camera.position.z = 400;
+
+				scene = new THREE.Scene();
+				scene.fog = new THREE.Fog( 0x0066FF, 1, 1000 );
+
+				object = new THREE.Object3D();
+				scene.add( object );
+
+				var geometry = new THREE.SphereGeometry( 1, 4, 4 );
+
+				for ( var i = 0; i < 100; i ++ ) {
+
+					var material = new THREE.MeshPhongMaterial( { color: 0x888888 + ( Math.random() * 0x888888 ), shading: THREE.FlatShading } );
+					var mesh = new THREE.Mesh( geometry, material );
+					mesh.position.set( Math.random() - 0.5, Math.random() - 0.5, Math.random() - 0.5 ).normalize();
+					mesh.position.multiplyScalar( Math.random() * 400 );
+					mesh.rotation.set( Math.random() * 2, Math.random() * 2, Math.random() * 2 );
+					mesh.scale.x = mesh.scale.y = mesh.scale.z = 10 + ( Math.random() * 40 );
+					object.add( mesh );
+
+				}
+
+				scene.add( new THREE.AmbientLight( 0x999999 ) );
+
+				light = new THREE.DirectionalLight( 0xffffff );
+				light.position.set( 1, 1, 1 );
+				scene.add( light );
+
+				// postprocessing
+
+				composer = new THREE.EffectComposer( renderer );
+				composer.addPass( new THREE.RenderPass( scene, camera ) );
+
+				nodepass = new THREE.NodePass();
+				nodepass.renderToScreen = true;
+
+				composer.addPass( nodepass );
+
+				//
+
+				updateMaterial();
+
+				window.addEventListener( 'resize', onWindowResize, false );
+
+			}
+
+			function onWindowResize() {
+
+				camera.aspect = window.innerWidth / window.innerHeight;
+				camera.updateProjectionMatrix();
+
+				renderer.setSize( window.innerWidth, window.innerHeight );
+				composer.setSize( window.innerWidth, window.innerHeight );
+
+			}
+
+			function animate() {
+
+				requestAnimationFrame( animate );
+
+				object.rotation.x += 0.005;
+				object.rotation.y += 0.01;
+
+				composer.render();
+
+			}
+
+		</script>
+
+	</body>
+</html>