Browse Source

NodeMaterial rev 3 (final to PR)

SUNAG 9 years ago
parent
commit
d25cf4ad31
76 changed files with 7123 additions and 0 deletions
  1. 1 0
      examples/index.html
  2. 146 0
      examples/js/materials/nodes/NodeBuilder.js
  3. 52 0
      examples/js/materials/nodes/NodeConst.js
  4. 157 0
      examples/js/materials/nodes/NodeFunction.js
  5. 60 0
      examples/js/materials/nodes/NodeFunctionCall.js
  6. 94 0
      examples/js/materials/nodes/NodeGL.js
  7. 44 0
      examples/js/materials/nodes/NodeInput.js
  8. 87 0
      examples/js/materials/nodes/NodeLib.js
  9. 467 0
      examples/js/materials/nodes/NodeMaterial.js
  10. 37 0
      examples/js/materials/nodes/NodeRaw.js
  11. 116 0
      examples/js/materials/nodes/NodeTemp.js
  12. 106 0
      examples/js/materials/nodes/accessors/NodeCamera.js
  13. 31 0
      examples/js/materials/nodes/accessors/NodeColors.js
  14. 66 0
      examples/js/materials/nodes/accessors/NodeNormal.js
  15. 87 0
      examples/js/materials/nodes/accessors/NodePosition.js
  16. 39 0
      examples/js/materials/nodes/accessors/NodeReflect.js
  17. 31 0
      examples/js/materials/nodes/accessors/NodeUV.js
  18. 59 0
      examples/js/materials/nodes/extras/NodeVelocity.js
  19. 16 0
      examples/js/materials/nodes/inputs/NodeColor.js
  20. 37 0
      examples/js/materials/nodes/inputs/NodeCubeTexture.js
  21. 21 0
      examples/js/materials/nodes/inputs/NodeFloat.js
  22. 21 0
      examples/js/materials/nodes/inputs/NodeInt.js
  23. 31 0
      examples/js/materials/nodes/inputs/NodeTexture.js
  24. 16 0
      examples/js/materials/nodes/inputs/NodeVector2.js
  25. 17 0
      examples/js/materials/nodes/inputs/NodeVector3.js
  26. 16 0
      examples/js/materials/nodes/inputs/NodeVector4.js
  27. 257 0
      examples/js/materials/nodes/interfaces/NodePhong.js
  28. 17 0
      examples/js/materials/nodes/interfaces/NodePhongMaterial.js
  29. 268 0
      examples/js/materials/nodes/interfaces/NodeStandard.js
  30. 17 0
      examples/js/materials/nodes/interfaces/NodeStandardMaterial.js
  31. 78 0
      examples/js/materials/nodes/math/NodeMath1.js
  32. 93 0
      examples/js/materials/nodes/math/NodeMath2.js
  33. 80 0
      examples/js/materials/nodes/math/NodeMath3.js
  34. 45 0
      examples/js/materials/nodes/math/NodeOperator.js
  35. 65 0
      examples/js/materials/nodes/utils/NodeJoin.js
  36. 44 0
      examples/js/materials/nodes/utils/NodeRoughnessToBlinnExponent.js
  37. 66 0
      examples/js/materials/nodes/utils/NodeSwitch.js
  38. 20 0
      examples/js/materials/nodes/utils/NodeTime.js
  39. 146 0
      examples/materials/nodes/NodeBuilder.js
  40. 52 0
      examples/materials/nodes/NodeConst.js
  41. 157 0
      examples/materials/nodes/NodeFunction.js
  42. 60 0
      examples/materials/nodes/NodeFunctionCall.js
  43. 94 0
      examples/materials/nodes/NodeGL.js
  44. 44 0
      examples/materials/nodes/NodeInput.js
  45. 87 0
      examples/materials/nodes/NodeLib.js
  46. 467 0
      examples/materials/nodes/NodeMaterial.js
  47. 37 0
      examples/materials/nodes/NodeRaw.js
  48. 116 0
      examples/materials/nodes/NodeTemp.js
  49. 106 0
      examples/materials/nodes/accessors/NodeCamera.js
  50. 31 0
      examples/materials/nodes/accessors/NodeColors.js
  51. 66 0
      examples/materials/nodes/accessors/NodeNormal.js
  52. 87 0
      examples/materials/nodes/accessors/NodePosition.js
  53. 39 0
      examples/materials/nodes/accessors/NodeReflect.js
  54. 31 0
      examples/materials/nodes/accessors/NodeUV.js
  55. 59 0
      examples/materials/nodes/extras/NodeVelocity.js
  56. 16 0
      examples/materials/nodes/inputs/NodeColor.js
  57. 37 0
      examples/materials/nodes/inputs/NodeCubeTexture.js
  58. 21 0
      examples/materials/nodes/inputs/NodeFloat.js
  59. 21 0
      examples/materials/nodes/inputs/NodeInt.js
  60. 31 0
      examples/materials/nodes/inputs/NodeTexture.js
  61. 16 0
      examples/materials/nodes/inputs/NodeVector2.js
  62. 17 0
      examples/materials/nodes/inputs/NodeVector3.js
  63. 16 0
      examples/materials/nodes/inputs/NodeVector4.js
  64. 257 0
      examples/materials/nodes/interfaces/NodePhong.js
  65. 17 0
      examples/materials/nodes/interfaces/NodePhongMaterial.js
  66. 268 0
      examples/materials/nodes/interfaces/NodeStandard.js
  67. 17 0
      examples/materials/nodes/interfaces/NodeStandardMaterial.js
  68. 78 0
      examples/materials/nodes/math/NodeMath1.js
  69. 93 0
      examples/materials/nodes/math/NodeMath2.js
  70. 80 0
      examples/materials/nodes/math/NodeMath3.js
  71. 45 0
      examples/materials/nodes/math/NodeOperator.js
  72. 65 0
      examples/materials/nodes/utils/NodeJoin.js
  73. 44 0
      examples/materials/nodes/utils/NodeRoughnessToBlinnExponent.js
  74. 66 0
      examples/materials/nodes/utils/NodeSwitch.js
  75. 20 0
      examples/materials/nodes/utils/NodeTime.js
  76. 1314 0
      examples/webgl_materials_nodes.html

+ 1 - 0
examples/index.html

@@ -307,6 +307,7 @@
 				"webgl_materials_envmaps",
 				"webgl_materials_grass",
 				"webgl_materials_lightmap",
+				"webgl_materials_nodes",
 				"webgl_materials_normalmap",
 				"webgl_materials_parallaxmap",
 				"webgl_materials_shaders_fresnel",

+ 146 - 0
examples/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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/js/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;
+	
+};

+ 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;
+	
+};

+ 1314 - 0
examples/webgl_materials_nodes.html

@@ -0,0 +1,1314 @@
+<!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>
+		
+		<!-- NodeMaterial Base -->
+		<script src="js/materials/nodes/NodeGL.js"></script>
+		<script src="js/materials/nodes/NodeBuilder.js"></script>
+		<script src="js/materials/nodes/NodeRaw.js"></script>
+		<script src="js/materials/nodes/NodeTemp.js"></script>
+		<script src="js/materials/nodes/NodeInput.js"></script>
+		<script src="js/materials/nodes/NodeMaterial.js"></script>
+		<script src="js/materials/nodes/NodeConst.js"></script>
+		<script src="js/materials/nodes/NodeFunction.js"></script>
+		<script src="js/materials/nodes/NodeFunctionCall.js"></script>
+		<script src="js/materials/nodes/NodeLib.js"></script>
+		
+		<!-- Accessors -->
+		<script src="js/materials/nodes/accessors/NodeColors.js"></script>
+		<script src="js/materials/nodes/accessors/NodeCamera.js"></script>
+		<script src="js/materials/nodes/accessors/NodeNormal.js"></script>
+		<script src="js/materials/nodes/accessors/NodePosition.js"></script>
+		<script src="js/materials/nodes/accessors/NodeReflect.js"></script>
+		<script src="js/materials/nodes/accessors/NodeUV.js"></script>
+		
+		<!-- Inputs -->
+		<script src="js/materials/nodes/inputs/NodeColor.js"></script>
+		<script src="js/materials/nodes/inputs/NodeFloat.js"></script>
+		<script src="js/materials/nodes/inputs/NodeInt.js"></script>
+		<script src="js/materials/nodes/inputs/NodeVector2.js"></script>
+		<script src="js/materials/nodes/inputs/NodeVector3.js"></script>
+		<script src="js/materials/nodes/inputs/NodeVector4.js"></script>
+		<script src="js/materials/nodes/inputs/NodeTexture.js"></script>
+		<script src="js/materials/nodes/inputs/NodeCubeTexture.js"></script>
+		
+		<!-- Math -->
+		<script src="js/materials/nodes/math/NodeMath1.js"></script>
+		<script src="js/materials/nodes/math/NodeMath2.js"></script>
+		<script src="js/materials/nodes/math/NodeMath3.js"></script>
+		<script src="js/materials/nodes/math/NodeOperator.js"></script>
+		
+		<!-- Utils -->
+		<script src="js/materials/nodes/utils/NodeJoin.js"></script>
+		<script src="js/materials/nodes/utils/NodeSwitch.js"></script>
+		<script src="js/materials/nodes/utils/NodeTime.js"></script>
+		<script src="js/materials/nodes/utils/NodeRoughnessToBlinnExponent.js"></script>
+		
+		<!-- Interfaces -->
+		<script src="js/materials/nodes/interfaces/NodePhong.js"></script>
+		<script src="js/materials/nodes/interfaces/NodePhongMaterial.js"></script>
+		<script src="js/materials/nodes/interfaces/NodeStandard.js"></script>
+		<script src="js/materials/nodes/interfaces/NodeStandardMaterial.js"></script>
+		
+		<!-- Extras -->
+		<script src="js/materials/nodes/extras/NodeVelocity.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 decalNormal = new THREE.TextureLoader().load( 'textures/decal/decal-normal.jpg' );
+		
+		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 {
+			
+				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.NodePhongMaterial();
+					
+					//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.NodeSwitch(new THREE.NodeTexture(decalDiffuse), 'w');
+					
+					mtl.color = new THREE.NodeTexture(grass);
+					mtl.specular = new THREE.NodeFloat(.5);
+					mtl.shininess = new THREE.NodeFloat(15);
+					mtl.environment = new THREE.NodeCubeTexture(cubemap);
+					mtl.reflectivity = mask;
+					mtl.normal = new THREE.NodeTexture(grassNormal);
+					mtl.normalScale = new THREE.NodeMath1( mask, THREE.NodeMath1.INVERT );					
+					
+					break;
+				
+				case 'standard':
+					
+					//	MATERIAL
+					
+					mtl = new THREE.NodeStandardMaterial();
+					
+					//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.NodeSwitch(new THREE.NodeTexture(decalDiffuse), 'w');
+					
+					var normalScale = new THREE.NodeFloat(.3);
+					
+					var roughnessA = new THREE.NodeFloat(.5);
+					var metalnessA = new THREE.NodeFloat(.5);
+					
+					var roughnessB = new THREE.NodeFloat(0);
+					var metalnessB = new THREE.NodeFloat(1);
+
+					var roughness = new THREE.NodeMath3(
+						roughnessA,
+						roughnessB,
+						mask,
+						THREE.NodeMath3.MIX
+					);					
+					
+					var metalness = new THREE.NodeMath3(
+						metalnessA,
+						metalnessB,
+						mask,
+						THREE.NodeMath3.MIX
+					);
+					
+					var normalMask = new THREE.NodeOperator(
+						new THREE.NodeMath1( mask, THREE.NodeMath1.INVERT ),
+						normalScale,
+						THREE.NodeOperator.MUL
+					);
+					
+					mtl.color = new THREE.NodeColor(0xFFFFFF);
+					mtl.roughness = roughness;
+					mtl.metalness = metalness;
+					mtl.environment = new THREE.NodeCubeTexture(cubemap);
+					mtl.normal = new THREE.NodeTexture(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.NodePhongMaterial();
+					
+					var time = new THREE.NodeTime();
+					var speed = new THREE.NodeFloat(5);
+					var scale = new THREE.NodeFloat(1);
+					var worldScale = new THREE.NodeFloat(.4);
+					var colorA = new THREE.NodeColor(0xFFFFFF);
+					var colorB = new THREE.NodeColor(0x0054df);
+					
+					var uv = new THREE.NodeUV();
+					
+					var timeScale = new THREE.NodeOperator(
+						time,
+						speed,
+						THREE.NodeOperator.MUL
+					);
+					
+					var worldScl = new THREE.NodeOperator(
+						new THREE.NodePosition(),
+						worldScale,
+						THREE.NodeOperator.MUL
+					);
+					
+					var posContinuous = new THREE.NodeOperator(
+						worldScl,
+						timeScale,
+						THREE.NodeOperator.ADD
+					);
+					
+					var wave = new THREE.NodeMath1(posContinuous, THREE.NodeMath1.SIN);
+					wave = new THREE.NodeSwitch(wave, 'x');
+					
+					var waveScale = new THREE.NodeOperator(
+						wave,
+						scale,
+						THREE.NodeOperator.MUL
+					);
+					
+					var displaceY = new THREE.NodeJoin(
+						new THREE.NodeFloat(),
+						waveScale,
+						new THREE.NodeFloat()
+					);
+					
+					var displace = new THREE.NodeOperator(
+						new THREE.NodeNormal(),
+						displaceY,
+						THREE.NodeOperator.MUL
+					);
+					
+					var blend = new THREE.NodeOperator(
+						new THREE.NodePosition(),
+						displaceY,
+						THREE.NodeOperator.ADD
+					);
+					
+					var color = new THREE.NodeMath3(
+						colorB,
+						colorA,
+						wave,
+						THREE.NodeMath3.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.NodePhongMaterial();
+					
+					var intensity = 1.3;
+					var power = new THREE.NodeFloat(3);
+					var color = new THREE.NodeColor(0xFFFFFF);
+						
+					var viewZ = new THREE.NodeMath2(
+						new THREE.NodeNormal( THREE.NodeNormal.VIEW ), 
+						new THREE.NodeVector3(0, 0, -intensity), 
+						THREE.NodeMath2.DOT
+					);
+					
+					var rim = new THREE.NodeOperator(
+						viewZ,
+						new THREE.NodeFloat(intensity),
+						THREE.NodeOperator.ADD
+					);
+					
+					var rimPower = new THREE.NodeMath2(
+						rim, 
+						power, 
+						THREE.NodeMath2.POW
+					);
+					
+					var rimColor = new THREE.NodeOperator(
+						rimPower,
+						color,
+						THREE.NodeOperator.MUL
+					);
+					
+					mtl.color = new THREE.NodeColor(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.NodePhongMaterial();
+					
+					var reflectance = new THREE.NodeFloat(1.3);
+					var power = new THREE.NodeFloat(1);
+					var color = new THREE.NodeCubeTexture(cubemap);
+						
+					var viewZ = new THREE.NodeMath2(
+						new THREE.NodeNormal( THREE.NodeNormal.VIEW ), 
+						new THREE.NodeVector3(0, 0, -1), 
+						THREE.NodeMath2.DOT
+					);
+					
+					var theta = new THREE.NodeOperator(
+						viewZ,
+						new THREE.NodeFloat(1),
+						THREE.NodeOperator.ADD
+					);
+					
+					var thetaPower = new THREE.NodeMath2(
+						theta, 
+						power, 
+						THREE.NodeMath2.POW
+					);
+					
+					var fresnel = new THREE.NodeOperator(
+						reflectance,
+						thetaPower,
+						THREE.NodeOperator.MUL
+					);
+					
+					mtl.color = new THREE.NodeColor(0x3399FF);
+					mtl.environment = color;
+					mtl.reflectivity = new THREE.NodeMath1( fresnel, THREE.NodeMath1.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.NodePhongMaterial();
+					
+					var tex1 = new THREE.NodeTexture(grass);
+					var tex2 = new THREE.NodeTexture(brick);
+					
+					var offset = new THREE.NodeFloat(0);
+					var scale = new THREE.NodeFloat(1);
+					var uv = new THREE.NodeUV();
+					
+					var uvOffset = new THREE.NodeOperator(
+						offset,
+						uv,
+						THREE.NodeOperator.ADD
+					);
+					
+					var uvScale = new THREE.NodeOperator(
+						uvOffset,
+						scale,
+						THREE.NodeOperator.MUL
+					);
+					
+					var mask = new THREE.NodeTexture(decalDiffuse, uvScale);
+					var maskAlphaChannel = new THREE.NodeSwitch(mask, 'w');
+					
+					var blend = new THREE.NodeMath3(
+						tex1,
+						tex2,
+						maskAlphaChannel,
+						THREE.NodeMath3.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.NodeStandardMaterial();
+					
+					var tex = new THREE.NodeTexture(brick);
+					var sat = new THREE.NodeFloat(0);
+					
+					var satrgb = new THREE.NodeFunction([
+					"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.NodeFunctionCall(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.NodeCubeTexture(cubemap); // optional
+			
+					//	GUI
+			
+					addGui( 'saturation', sat.number, function( val ) {
+						
+						sat.number = val;
+
+					}, false, 0, 2 );
+			
+					break;
+					
+				case 'top-bottom':
+					
+					//	MATERIAL
+					
+					mtl = new THREE.NodePhongMaterial();
+					
+					var top = new THREE.NodeTexture(grass);
+					var bottom = new THREE.NodeTexture(brick);
+					
+					var normal = new THREE.NodeNormal( THREE.NodeNormal.WORLD );
+					var normalY = new THREE.NodeSwitch(normal, 'y');
+			
+					var hard = new THREE.NodeFloat(9);
+					var offset = new THREE.NodeFloat(-2.5);
+			
+					var hardClamp = new THREE.NodeOperator(
+						normalY,
+						hard,
+						THREE.NodeOperator.MUL
+					);
+					
+					var offsetClamp = new THREE.NodeOperator(
+						hardClamp,
+						offset,
+						THREE.NodeOperator.ADD
+					);
+					
+					var clamp0at1 = new THREE.NodeMath1( offsetClamp, THREE.NodeMath1.SAT );
+					
+					var blend = new THREE.NodeMath3(top,bottom,clamp0at1,THREE.NodeMath3.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.NodePhongMaterial();
+					
+					var time = new THREE.NodeTime();
+					var scale = new THREE.NodeFloat(2);
+					var speed = new THREE.NodeFloat(.2);
+					var colorA = new THREE.NodeColor(0xFFFFFF);
+					var colorB = new THREE.NodeColor(0x0054df);
+					
+					var uv = new THREE.NodeUV();
+					
+					var timeScl = new THREE.NodeOperator(
+						time,
+						speed,
+						THREE.NodeOperator.MUL
+					);
+					
+					var displaceOffset = new THREE.NodeOperator(
+						timeScl,
+						uv,
+						THREE.NodeOperator.ADD
+					);
+					
+					var tex = new THREE.NodeTexture(cloud, displaceOffset);
+					var texArea = new THREE.NodeSwitch(tex, 'w');
+					
+					var displace = new THREE.NodeOperator(
+						new THREE.NodeNormal(),
+						texArea,
+						THREE.NodeOperator.MUL
+					);
+					
+					var displaceScale = new THREE.NodeOperator(
+						displace,
+						scale,
+						THREE.NodeOperator.MUL
+					);
+					
+					var blend = new THREE.NodeOperator(
+						new THREE.NodePosition(),
+						displaceScale,
+						THREE.NodeOperator.ADD
+					);
+					
+					var color = new THREE.NodeMath3(
+						colorB,
+						colorA,
+						texArea,
+						THREE.NodeMath3.MIX
+					);
+					
+					mtl.color = mtl.specular = new THREE.NodeColor(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.NodePhongMaterial();
+					
+					var time = new THREE.NodeTime();
+					var uv = new THREE.NodeUV();
+					
+					var timeSpeedA = new THREE.NodeOperator(
+						time,
+						new THREE.NodeVector2(0.3, 0.1),
+						THREE.NodeOperator.MUL
+					);
+					
+					var timeSpeedB = new THREE.NodeOperator(
+						time,
+						new THREE.NodeVector2(0.15, 0.4),
+						THREE.NodeOperator.MUL
+					);
+					
+					var uvOffsetA = new THREE.NodeOperator(
+						timeSpeedA,
+						uv,
+						THREE.NodeOperator.ADD
+					);
+					
+					var uvOffsetB = new THREE.NodeOperator(
+						timeSpeedB,
+						uv,
+						THREE.NodeOperator.ADD
+					);
+					
+					var cloudA = new THREE.NodeTexture(cloud, uvOffsetA);
+					var cloudB = new THREE.NodeTexture(cloud, uvOffsetB);
+					
+					var clouds = new THREE.NodeOperator(
+						cloudA,
+						cloudB,
+						THREE.NodeOperator.ADD
+					);
+					
+					mtl.environment = new THREE.NodeColor(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.NodeColor(0xFFFFFF);
+					var colorB = new THREE.NodeColor(0x0054df);
+					
+					var depth = new THREE.NodeCamera( THREE.NodeCamera.DEPTH );
+					depth.near.number = 1;
+					depth.far.number = 200;
+					
+					var colors = new THREE.NodeMath3(
+						colorB,
+						colorA,
+						depth,
+						THREE.NodeMath3.MIX
+					);
+					
+					mtl = new THREE.NodePhongMaterial();
+					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.NodeStandardMaterial();
+					
+					var hash2 = new THREE.NodeFunction([
+					"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.NodeFunction([
+					// 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.NodeFunction([
+					// 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.NodeTime();
+					var timeScale = new THREE.NodeFloat(2);
+					
+					var alpha = new THREE.NodeFloat(1);
+					var scale = new THREE.NodeFloat(.1);
+					var intensity = new THREE.NodeFloat(1.5);
+					
+					var color = new THREE.NodeColor(0xFFFFFF);
+					var colorA = new THREE.NodeColor(0xFFFFFF);
+					var colorB = new THREE.NodeColor(0x0054df);
+					
+					var worldPos = new THREE.NodePosition( THREE.NodePosition.WORLD );
+					var worldPosTop = new THREE.NodeSwitch(worldPos, 'xz');
+					
+					var pos = new THREE.NodePosition( THREE.NodePosition.WORLD );
+					var posNorm = new THREE.NodeMath1(pos, THREE.NodeMath1.NORMALIZE);
+					
+					var mask = new THREE.NodeSwitch(posNorm, 'y');
+					
+					// clamp0at1
+					mask = new THREE.NodeMath1( mask, THREE.NodeMath1.SAT );
+					
+					var timeOffset = new THREE.NodeOperator(
+						time,
+						timeScale,
+						THREE.NodeOperator.MUL
+					);					
+					
+					var uvPos = new THREE.NodeOperator(
+						worldPosTop,
+						scale,
+						THREE.NodeOperator.MUL
+					);	
+					
+					var voronoi = new THREE.NodeFunctionCall(voronoiLayers);
+					voronoi.input.p = uvPos;
+					voronoi.input.time = timeOffset;
+					
+					var maskCaustic = new THREE.NodeOperator(
+						alpha,
+						mask,
+						THREE.NodeOperator.MUL
+					);	
+					
+					var voronoiIntensity = new THREE.NodeOperator(
+						voronoi,
+						intensity,
+						THREE.NodeOperator.MUL
+					);	
+					
+					var voronoiColors = new THREE.NodeMath3(
+						colorB,
+						colorA,
+						new THREE.NodeMath1( voronoiIntensity, THREE.NodeMath1.SAT ), // mix needs clamp
+						THREE.NodeMath3.MIX
+					);
+					
+					var caustic = new THREE.NodeMath3(
+						color,
+						voronoiColors,
+						maskCaustic,
+						THREE.NodeMath3.MIX
+					);
+					
+					var causticLights = new THREE.NodeOperator(
+						voronoiIntensity,
+						maskCaustic,
+						THREE.NodeOperator.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.NodeStandardMaterial();
+					
+					var scale = new THREE.NodeFloat(2);
+					var colorA = new THREE.NodeColor(0xFF6633);
+					var colorB = new THREE.NodeColor(0x3366FF);
+					
+					var pos = new THREE.NodePosition();
+					var posNorm = new THREE.NodeMath1(pos, THREE.NodeMath1.NORMALIZE);					
+					
+					var mask = new THREE.NodeSwitch(posNorm, 'y');
+					
+					var velocity = new THREE.NodeVelocity( mesh, {
+						type:'elastic', 
+						spring:.8, 
+						friction:.9
+					} );
+					
+					var velocityArea = new THREE.NodeOperator(
+						mask,
+						scale,
+						THREE.NodeOperator.MUL
+					);
+					
+					var softVelocity = new THREE.NodeOperator(
+						velocity,
+						velocityArea,
+						THREE.NodeOperator.MUL
+					);
+					
+					var softPosition = new THREE.NodeOperator(
+						new THREE.NodePosition(),
+						softVelocity,
+						THREE.NodeOperator.ADD
+					);
+					
+					var colors = new THREE.NodeMath3(
+						colorB,
+						colorA,
+						mask,
+						THREE.NodeMath3.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.NodePhongMaterial();
+					
+					var time = new THREE.NodeTime();
+					var speed = new THREE.NodeFloat(.5);
+					
+					var color = new THREE.NodeColor(0x98ff00);
+					
+					var timeSpeed = new THREE.NodeOperator(
+						time,
+						speed,
+						THREE.NodeOperator.MUL
+					);
+					
+					var sinCycleInSecs = new THREE.NodeOperator(
+						timeSpeed, 
+						new THREE.NodeConst( THREE.NodeConst.PI2 ),
+						THREE.NodeOperator.MUL
+					);
+					
+					var cycle = new THREE.NodeMath1(sinCycleInSecs, THREE.NodeMath1.SIN);
+					
+					var cycleColor = new THREE.NodeOperator(
+						cycle,
+						color,
+						THREE.NodeOperator.MUL
+					);
+					
+					var cos = new THREE.NodeMath1(cycleColor, THREE.NodeMath1.SIN);
+					
+					mtl.color = new THREE.NodeColor(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>