فهرست منبع

nodematerial - r7 dev

sunag 7 سال پیش
والد
کامیت
085bd767e4
74فایلهای تغییر یافته به همراه2710 افزوده شده و 1256 حذف شده
  1. 74 405
      examples/js/loaders/NodeMaterialLoader.js
  2. 0 259
      examples/js/nodes/NodeBuilder.js
  3. 0 216
      examples/js/nodes/NodeLib.js
  4. 97 0
      examples/js/nodes/NodeMaterialSource.js
  5. 25 0
      examples/js/nodes/accessors/CameraNode.js
  6. 8 0
      examples/js/nodes/accessors/ColorsNode.js
  7. 8 0
      examples/js/nodes/accessors/LightNode.js
  8. 8 0
      examples/js/nodes/accessors/NormalNode.js
  9. 8 0
      examples/js/nodes/accessors/PositionNode.js
  10. 3 2
      examples/js/nodes/accessors/ReflectNode.js
  11. 8 0
      examples/js/nodes/accessors/ScreenUVNode.js
  12. 8 0
      examples/js/nodes/accessors/UVNode.js
  13. 9 1
      examples/js/nodes/core/AttributeNode.js
  14. 71 0
      examples/js/nodes/core/BypassNode.js
  15. 19 8
      examples/js/nodes/core/ConstNode.js
  16. 16 2
      examples/js/nodes/core/FunctionCallNode.js
  17. 30 14
      examples/js/nodes/core/FunctionNode.js
  18. 9 8
      examples/js/nodes/core/GLNode.js
  19. 19 0
      examples/js/nodes/core/InputNode.js
  20. 420 0
      examples/js/nodes/core/NodeBuilder.js
  21. 0 0
      examples/js/nodes/core/NodeFrame.js
  22. 66 0
      examples/js/nodes/core/NodeLib.js
  23. 124 48
      examples/js/nodes/core/NodeMaterial.js
  24. 0 0
      examples/js/nodes/core/NodeUniform.js
  25. 9 1
      examples/js/nodes/core/RawNode.js
  26. 102 0
      examples/js/nodes/core/StructNode.js
  27. 2 2
      examples/js/nodes/core/TempNode.js
  28. 21 2
      examples/js/nodes/core/VarNode.js
  29. 10 2
      examples/js/nodes/inputs/ColorNode.js
  30. 15 15
      examples/js/nodes/inputs/CubeTextureNode.js
  31. 8 2
      examples/js/nodes/inputs/FloatNode.js
  32. 8 0
      examples/js/nodes/inputs/IntNode.js
  33. 29 2
      examples/js/nodes/inputs/Matrix3Node.js
  34. 28 2
      examples/js/nodes/inputs/Matrix4Node.js
  35. 8 0
      examples/js/nodes/inputs/ReflectorNode.js
  36. 15 14
      examples/js/nodes/inputs/TextureNode.js
  37. 9 1
      examples/js/nodes/inputs/Vector2Node.js
  38. 9 2
      examples/js/nodes/inputs/Vector3Node.js
  39. 9 1
      examples/js/nodes/inputs/Vector4Node.js
  40. 156 0
      examples/js/nodes/libs/common.js
  41. 53 0
      examples/js/nodes/libs/keywords.js
  42. 77 0
      examples/js/nodes/materials/MeshStandardNode.js
  43. 41 0
      examples/js/nodes/materials/MeshStandardNodeMaterial.js
  44. 48 14
      examples/js/nodes/materials/PhongNode.js
  45. 17 4
      examples/js/nodes/materials/PhongNodeMaterial.js
  46. 20 1
      examples/js/nodes/materials/SpriteNode.js
  47. 8 4
      examples/js/nodes/materials/SpriteNodeMaterial.js
  48. 68 36
      examples/js/nodes/materials/StandardNode.js
  49. 17 2
      examples/js/nodes/materials/StandardNodeMaterial.js
  50. 12 3
      examples/js/nodes/math/Math1Node.js
  51. 11 1
      examples/js/nodes/math/Math2Node.js
  52. 12 1
      examples/js/nodes/math/Math3Node.js
  53. 11 1
      examples/js/nodes/math/OperatorNode.js
  54. 6 0
      examples/js/nodes/postprocessing/NodePass.js
  55. 45 0
      examples/js/nodes/utils/BlinnExponentToRoughnessNode.js
  56. 27 0
      examples/js/nodes/utils/BlinnShininessExponentNode.js
  57. 20 4
      examples/js/nodes/utils/BlurNode.js
  58. 95 0
      examples/js/nodes/utils/BumpMapNode.js
  59. 0 68
      examples/js/nodes/utils/BumpNode.js
  60. 10 0
      examples/js/nodes/utils/ColorAdjustmentNode.js
  61. 12 0
      examples/js/nodes/utils/JoinNode.js
  62. 8 0
      examples/js/nodes/utils/LuminanceNode.js
  63. 53 0
      examples/js/nodes/utils/MaxMIPLevelNode.js
  64. 8 0
      examples/js/nodes/utils/NoiseNode.js
  65. 23 16
      examples/js/nodes/utils/NormalMapNode.js
  66. 8 0
      examples/js/nodes/utils/ResolutionNode.js
  67. 33 19
      examples/js/nodes/utils/RoughnessToBlinnExponentNode.js
  68. 9 0
      examples/js/nodes/utils/SwitchNode.js
  69. 61 0
      examples/js/nodes/utils/TextureCubeNode.js
  70. 192 0
      examples/js/nodes/utils/TextureCubeUVNode.js
  71. 15 3
      examples/js/nodes/utils/TimerNode.js
  72. 12 3
      examples/js/nodes/utils/UVTransformNode.js
  73. 10 0
      examples/js/nodes/utils/VelocityNode.js
  74. 200 67
      examples/webgl_materials_nodes.html

+ 74 - 405
examples/js/loaders/NodeMaterialLoader.js

@@ -91,7 +91,11 @@ Object.assign( THREE.NodeMaterialLoader.prototype, {
 
 	getObjectById: function ( uuid ) {
 
-		return this.library[ uuid ] || this.nodes[ uuid ] || this.names[ uuid ];
+		return this.library[ uuid ] || 
+			this.nodes[ uuid ] || 
+			this.materials[ uuid ] ||
+			this.passes[ uuid ] || 
+			this.names[ uuid ];
 
 	},
 
@@ -109,15 +113,62 @@ Object.assign( THREE.NodeMaterialLoader.prototype, {
 
 	},
 
-	parse: function ( json ) {
-
-		var uuid, node, object, prop, i;
+	resolve: function( json ) {
+		
+		switch( typeof json ) {
+			
+			case "boolean":
+			case "number":
+			
+				return json;
+			
+			case "string":
+			
+				if (/^\w{8}-\w{4}-\w{4}-\w{4}-\w{12}$/s.test(json)) {
+					
+					return this.getNode( json );
+					
+				}
+				
+				return json;
+
+			default:
+			
+				if ( Array.isArray( json ) ) {
+			
+					for(var i = 0; i < json.length; i++) {
+						
+						json[i] = this.resolve( json[i] );
+						
+					}
+					
+				} else {
+					
+					for ( var prop in json ) {
+						
+						if (prop === "uuid") continue;
+						
+						json[ prop ] = this.resolve( json[ prop ] );
+						
+					}
+					
+				}
+				
+		}
+		
+		return json;
+		
+	},
+	
+	declare: function( json ) {
+		
+		var uuid, node, object;
 
 		for ( uuid in json.nodes ) {
 
 			node = json.nodes[ uuid ];
 
-			object = new THREE[ node.type ]();
+			object = new THREE[ node.nodeType + "Node" ]();
 
 			if ( node.name ) {
 
@@ -125,15 +176,8 @@ Object.assign( THREE.NodeMaterialLoader.prototype, {
 
 				this.names[ object.name ] = object;
 
-			} else {
-
-				// ignore "uniform" shader input ( for optimization )
-				object.readonly = true;
-
 			}
 
-			if ( node.readonly !== undefined ) object.readonly = node.readonly;
-
 			this.nodes[ uuid ] = object;
 
 		}
@@ -174,410 +218,35 @@ Object.assign( THREE.NodeMaterialLoader.prototype, {
 
 		}
 
-		if ( json.material ) this.material = this.materials[ uuid ];
-		if ( json.pass ) this.pass = this.passes[ uuid ];
+		if ( json.material ) this.material = this.materials[ json.material ];
+		
+		if ( json.pass ) this.pass = this.passes[ json.pass ];
+		
+		return json;
+		
+	},
+	
+	parse: function ( json ) {
 
+		var uuid;
+	
+		json = this.resolve( this.declare( json ) );
+		
 		for ( uuid in json.nodes ) {
 
-			node = json.nodes[ uuid ];
-			object = this.nodes[ uuid ];
-
-			switch ( node.type ) {
-
-				case "IntNode":
-				case "FloatNode":
-
-					object.value = node.value;
-
-					break;
-
-				case "ColorNode":
-
-					object.value.copy( node );
-
-					break;
-
-				case "Vector2Node":
-
-					object.x = node.x;
-					object.y = node.y;
-
-					break;
-
-
-				case "Vector3Node":
-
-					object.x = node.x;
-					object.y = node.y;
-					object.z = node.z;
-
-					break;
-
-				case "Vector4Node":
-
-					object.x = node.x;
-					object.y = node.y;
-					object.z = node.z;
-					object.w = node.w;
-
-					break;
-
-				case "Matrix3Node":
-				case "Matrix4Node":
-
-					object.value.fromArray( node.elements );
-
-					break;
-
-				case "OperatorNode":
-
-					object.a = this.getNode( node.a );
-					object.b = this.getNode( node.b );
-					object.op = node.op;
-
-					break;
-
-				case "Math1Node":
-
-					object.a = this.getNode( node.a );
-					object.method = node.method;
-
-					break;
-
-				case "Math2Node":
-
-					object.a = this.getNode( node.a );
-					object.b = this.getNode( node.b );
-					object.method = node.method;
-
-					break;
-
-				case "Math3Node":
-
-					object.a = this.getNode( node.a );
-					object.b = this.getNode( node.b );
-					object.c = this.getNode( node.c );
-					object.method = node.method;
-
-					break;
-
-				case "UVNode":
-				case "ColorsNode":
-
-					object.index = node.index;
-
-					break;
-
-
-				case "LuminanceNode":
-
-					object.rgb = this.getNode( node.rgb );
-
-					break;
-
-				case "PositionNode":
-				case "NormalNode":
-				case "ReflectNode":
-				case "LightNode":
-
-					object.scope = node.scope;
-
-					break;
-
-				case "SwitchNode":
-
-					object.node = this.getNode( node.node );
-					object.components = node.components;
-
-					break;
-
-				case "JoinNode":
-
-					for ( prop in node.inputs ) {
-
-						object[ prop ] = this.getNode( node.inputs[ prop ] );
-
-					}
-
-					break;
-
-				case "CameraNode":
-
-					object.setScope( node.scope );
-
-					if ( node.camera ) object.setCamera( this.getNode( node.camera ) );
-
-					switch ( node.scope ) {
-
-						case THREE.CameraNode.DEPTH:
-
-							object.near.number = node.near;
-							object.far.number = node.far;
-
-							break;
-
-					}
-
-					break;
-
-				case "ColorAdjustmentNode":
-
-					object.rgb = this.getNode( node.rgb );
-					object.adjustment = this.getNode( node.adjustment );
-					object.method = node.method;
-
-					break;
-
-				case "UVTransformNode":
-
-					object.uv = this.getNode( node.uv );
-					object.transform = this.getNode( node.transform );
-
-					break;
-
-				case "BumpNode":
-
-					object.value = this.getNode( node.value );
-					object.coord = this.getNode( node.coord );
-					object.scale = this.getNode( node.scale );
-
-					break;
-
-				case "BlurNode":
-
-					object.value = this.getNode( node.value );
-					object.coord = this.getNode( node.coord );
-					object.scale = this.getNode( node.scale );
-
-					object.value = this.getNode( node.value );
-					object.coord = this.getNode( node.coord );
-					object.radius = this.getNode( node.radius );
-
-					if ( node.size !== undefined ) object.size = new THREE.Vector2( node.size.x, node.size.y );
-
-					object.blurX = node.blurX;
-					object.blurY = node.blurY;
-
-					break;
-
-				case "ResolutionNode":
-
-					object.renderer = this.getNode( node.renderer );
-
-					break;
-
-				case "ScreenUVNode":
-
-					object.resolution = this.getNode( node.resolution );
-
-					break;
-
-				case "VelocityNode":
-
-					if ( node.target ) object.setTarget( this.getNode( node.target ) );
-					object.setParams( node.params );
-
-					break;
-
-				case "TimerNode":
-
-					object.scope = node.scope;
-					object.scale = node.scale;
-
-					break;
-
-				case "ConstNode":
-
-					object.name = node.name;
-					object.type = node.out;
-					object.value = node.value;
-					object.useDefine = node.useDefine === true;
-
-					break;
-
-				case "AttributeNode":
-				case "VarNode":
-
-					object.type = node.out;
-
-					break;
-
-
-				case "ReflectorNode":
-
-					object.setMirror( this.getNode( node.mirror ) );
-
-					if ( node.offset ) object.offset = this.getNode( node.offset );
-
-					break;
-
-				case "NoiseNode":
-
-					object.coord = this.getNode( node.coord );
-
-					break;
-
-				case "FunctionNode":
-
-					object.isMethod = node.isMethod;
-					object.useKeywords = node.useKeywords;
-
-					object.extensions = node.extensions;
-					object.keywords = {};
-
-					for ( prop in node.keywords ) {
-
-						object.keywords[ prop ] = this.getNode( node.keywords[ prop ] );
-
-					}
-
-					if ( node.includes ) {
-
-						for ( i = 0; i < node.includes.length; i ++ ) {
-
-							object.includes.push( this.getNode( node.includes[ i ] ) );
-
-						}
-
-					}
-
-					object.eval( node.src, object.includes, object.extensions, object.keywords );
-
-					if ( ! object.isMethod ) object.type = node.out;
-
-					break;
-
-				case "FunctionCallNode":
-
-					for ( prop in node.inputs ) {
-
-						object.inputs[ prop ] = this.getNode( node.inputs[ prop ] );
-
-					}
-
-					object.value = this.getNode( node.value );
-
-					break;
-
-				case "TextureNode":
-				case "CubeTextureNode":
-				case "ScreenNode":
-
-					if ( node.value ) object.value = this.getNode( node.value );
-
-					object.coord = this.getNode( node.coord );
-
-					if ( node.bias ) object.bias = this.getNode( node.bias );
-					if ( object.project !== undefined ) object.project = node.project;
-
-					break;
-
-				case "RoughnessToBlinnExponentNode":
-					break;
-
-				case "RawNode":
-
-					object.value = this.getNode( node.value );
-
-					break;
-
-				case "StandardNode":
-				case "PhongNode":
-				case "SpriteNode":
-
-					object.color = this.getNode( node.color );
-
-					if ( node.alpha ) object.alpha = this.getNode( node.alpha );
-
-					if ( node.specular ) object.specular = this.getNode( node.specular );
-					if ( node.shininess ) object.shininess = this.getNode( node.shininess );
-
-					if ( node.roughness ) object.roughness = this.getNode( node.roughness );
-					if ( node.metalness ) object.metalness = this.getNode( node.metalness );
-
-					if ( node.reflectivity ) object.reflectivity = this.getNode( node.reflectivity );
-
-					if ( node.clearCoat ) object.clearCoat = this.getNode( node.clearCoat );
-					if ( node.clearCoatRoughness ) object.clearCoatRoughness = this.getNode( node.clearCoatRoughness );
-
-					if ( node.normal ) object.normal = this.getNode( node.normal );
-					if ( node.normalScale ) object.normalScale = this.getNode( node.normalScale );
-
-					if ( node.emissive ) object.emissive = this.getNode( node.emissive );
-					if ( node.ambient ) object.ambient = this.getNode( node.ambient );
-
-					if ( node.shadow ) object.shadow = this.getNode( node.shadow );
-					if ( node.light ) object.light = this.getNode( node.light );
-
-					if ( node.ao ) object.ao = this.getNode( node.ao );
-
-					if ( node.environment ) object.environment = this.getNode( node.environment );
-					if ( node.environmentAlpha ) object.environmentAlpha = this.getNode( node.environmentAlpha );
-
-					if ( node.transform ) object.transform = this.getNode( node.transform );
-
-					if ( node.spherical === false ) object.spherical = false;
-
-					break;
-
-				default:
-
-					console.warn( node.type, "not supported." );
-
-			}
+			this.nodes[ uuid ].copy( json.nodes[ uuid ] );
 
 		}
-
+		
 		for ( uuid in json.materials ) {
 
-			node = json.materials[ uuid ];
-			object = this.materials[ uuid ];
-
-			if ( node.name !== undefined ) object.name = node.name;
-
-			if ( node.blending !== undefined ) object.blending = node.blending;
-			if ( node.flatShading !== undefined ) object.flatShading = node.flatShading;
-			if ( node.side !== undefined ) object.side = node.side;
-
-			object.depthFunc = node.depthFunc;
-			object.depthTest = node.depthTest;
-			object.depthWrite = node.depthWrite;
-
-			if ( node.wireframe !== undefined ) object.wireframe = node.wireframe;
-			if ( node.wireframeLinewidth !== undefined ) object.wireframeLinewidth = node.wireframeLinewidth;
-			if ( node.wireframeLinecap !== undefined ) object.wireframeLinecap = node.wireframeLinecap;
-			if ( node.wireframeLinejoin !== undefined ) object.wireframeLinejoin = node.wireframeLinejoin;
-
-			if ( node.skinning !== undefined ) object.skinning = node.skinning;
-			if ( node.morphTargets !== undefined ) object.morphTargets = node.morphTargets;
-
-			if ( node.visible !== undefined ) object.visible = node.visible;
-			if ( node.userData !== undefined ) object.userData = node.userData;
-
-			object.vertex = this.getNode( node.vertex );
-			object.fragment = this.getNode( node.fragment );
-
-			if ( object.vertex === object.fragment ) {
-
-				// replace main node
-
-				object.node = object.vertex;
-
-			}
-
-			if ( node.fog !== undefined ) object.fog = node.fog;
-			if ( node.lights !== undefined ) object.lights = node.lights;
-
-			if ( node.transparent !== undefined ) object.transparent = node.transparent;
+			this.materials[ uuid ].copy( json.materials[ uuid ] );
 
 		}
-
+		
 		for ( uuid in json.passes ) {
 
-			node = json.passes[ uuid ];
-			object = this.passes[ uuid ];
-
-			object.value = this.getNode( node.value );
+			this.passes[ uuid ].copy( json.passes[ uuid ] );
 
 		}
 

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

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

+ 0 - 216
examples/js/nodes/NodeLib.js

@@ -1,216 +0,0 @@
-/**
- * @author sunag / http://www.sunag.com.br/
- */
-
-THREE.NodeLib = {
-
-	nodes: {},
-	keywords: {},
-
-	add: function ( node ) {
-
-		this.nodes[ node.name ] = node;
-
-	},
-
-	addKeyword: function ( name, callback, cache ) {
-
-		cache = cache !== undefined ? cache : true;
-
-		this.keywords[ name ] = { callback: callback, cache: cache };
-
-	},
-
-	remove: function ( node ) {
-
-		delete this.nodes[ node.name ];
-
-	},
-
-	removeKeyword: function ( name ) {
-
-		delete this.keywords[ name ];
-
-	},
-
-	get: function ( name ) {
-
-		return this.nodes[ name ];
-
-	},
-
-	getKeyword: function ( name, material ) {
-
-		return this.keywords[ name ].callback.call( this, material );
-
-	},
-
-	getKeywordData: function ( name ) {
-
-		return this.keywords[ name ];
-
-	},
-
-	contains: function ( name ) {
-
-		return this.nodes[ name ] != undefined;
-
-	},
-
-	containsKeyword: function ( name ) {
-
-		return this.keywords[ name ] != undefined;
-
-	}
-
-};
-
-//
-//	Keywords
-//
-
-THREE.NodeLib.addKeyword( 'uv', function () {
-
-	return new THREE.UVNode();
-
-} );
-
-THREE.NodeLib.addKeyword( 'uv2', function () {
-
-	return new THREE.UVNode( 1 );
-
-} );
-
-THREE.NodeLib.addKeyword( 'position', function () {
-
-	return new THREE.PositionNode();
-
-} );
-
-THREE.NodeLib.addKeyword( 'worldPosition', function () {
-
-	return new THREE.PositionNode( THREE.PositionNode.WORLD );
-
-} );
-
-THREE.NodeLib.addKeyword( 'normal', function () {
-
-	return new THREE.NormalNode();
-
-} );
-
-THREE.NodeLib.addKeyword( 'worldNormal', function () {
-
-	return new THREE.NormalNode( THREE.NormalNode.WORLD );
-
-} );
-
-THREE.NodeLib.addKeyword( 'viewPosition', function () {
-
-	return new THREE.PositionNode( THREE.NormalNode.VIEW );
-
-} );
-
-THREE.NodeLib.addKeyword( 'viewNormal', function () {
-
-	return new THREE.NormalNode( THREE.NormalNode.VIEW );
-
-} );
-
-THREE.NodeLib.addKeyword( 'time', function () {
-
-	return new THREE.TimerNode();
-
-} );
-
-//
-//	Luma
-//
-
-THREE.NodeLib.add( new THREE.ConstNode( "vec3 LUMA vec3(0.2125, 0.7154, 0.0721)" ) );
-
-//
-//	NormalMap
-//
-
-THREE.NodeLib.add( new THREE.FunctionNode( [
-// Per-Pixel Tangent Space Normal Mapping
-// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
-	"vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 map, vec2 mUv, vec2 scale ) {",
-	"	vec3 q0 = dFdx( eye_pos );",
-	"	vec3 q1 = dFdy( eye_pos );",
-	"	vec2 st0 = dFdx( mUv.st );",
-	"	vec2 st1 = dFdy( mUv.st );",
-	"	float factor = sign( st1.t * st0.s - st0.t * st1.s );",
-	"	factor *= float( gl_FrontFacing ) * 2.0 - 1.0;",
-	"	vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * factor );",
-	"	vec3 T = normalize( ( -q0 * st1.s + q1 * st0.s ) * factor );",
-	"	vec3 N = normalize( surf_norm );",
-	"	vec3 mapN = map * 2.0 - 1.0;",
-	"	mapN.xy = scale * mapN.xy;",
-	"	mat3 tsn = mat3( S, T, N );",
-	"	return normalize( tsn * mapN );",
-	"}"
-].join( "\n" ), null, { derivatives: true } ) );
-
-//
-//	Noise
-//
-
-THREE.NodeLib.add( new THREE.FunctionNode( [
-	"float snoise(vec2 co) {",
-	"	return fract( sin( dot(co.xy, vec2(12.9898,78.233) ) ) * 43758.5453 );",
-	"}"
-].join( "\n" ) ) );
-
-//
-//	Hue
-//
-
-THREE.NodeLib.add( new THREE.FunctionNode( [
-	"vec3 hue_rgb(vec3 rgb, float adjustment) {",
-	"	const mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);",
-	"	const mat3 YIQtoRGB = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.107, 1.7046);",
-	"	vec3 yiq = RGBtoYIQ * rgb;",
-	"	float hue = atan(yiq.z, yiq.y) + adjustment;",
-	"	float chroma = sqrt(yiq.z * yiq.z + yiq.y * yiq.y);",
-	"	return YIQtoRGB * vec3(yiq.x, chroma * cos(hue), chroma * sin(hue));",
-	"}"
-].join( "\n" ) ) );
-
-//
-//	Saturation
-//
-
-THREE.NodeLib.add( new THREE.FunctionNode( [
-// Algorithm from Chapter 16 of OpenGL Shading Language
-	"vec3 saturation_rgb(vec3 rgb, float adjustment) {",
-	"	vec3 intensity = vec3(dot(rgb, LUMA));",
-	"	return mix(intensity, rgb, adjustment);",
-	"}"
-].join( "\n" ) ) );
-
-//
-//	Luminance
-//
-
-THREE.NodeLib.add( new THREE.FunctionNode( [
-// Algorithm from Chapter 10 of Graphics Shaders
-	"float luminance_rgb(vec3 rgb) {",
-	"	return dot(rgb, LUMA);",
-	"}"
-].join( "\n" ) ) );
-
-//
-//	Vibrance
-//
-
-THREE.NodeLib.add( new THREE.FunctionNode( [
-// Shader by Evan Wallace adapted by @lo-th
-	"vec3 vibrance_rgb(vec3 rgb, float adjustment) {",
-	"	float average = (rgb.r + rgb.g + rgb.b) / 3.0;",
-	"	float mx = max(rgb.r, max(rgb.g, rgb.b));",
-	"	float amt = (mx - average) * (-3.0 * adjustment);",
-	"	return mix(rgb.rgb, vec3(mx), amt);",
-	"}"
-].join( "\n" ) ) );

+ 97 - 0
examples/js/nodes/NodeMaterialSource.js

@@ -0,0 +1,97 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+( function () {
+	
+	function loadScript( path ) {
+
+		var js = document.createElement("script");
+		js.type = 'text/javascript';
+		js.src = path;
+
+		document.body.appendChild( js );
+
+	}
+	
+	function loadScripts( path, scripts ) {
+
+		for(var i = 0; i < scripts.length; i++) {
+			
+			loadScript( path + scripts[i] );
+			
+		}
+
+	}
+	
+	loadScripts( "js/nodes/", [
+		// NodeLibrary
+		"core/GLNode.js",
+		"core/RawNode.js",
+		"core/BypassNode.js",
+		"core/TempNode.js",
+		"core/InputNode.js",
+		"core/ConstNode.js",
+		"core/VarNode.js",
+		"core/StructNode.js",
+		"core/FunctionNode.js",
+		"core/FunctionCallNode.js",
+		"core/AttributeNode.js",
+		"core/NodeUniform.js",
+		"core/NodeBuilder.js",
+		"core/NodeLib.js",
+		"core/NodeFrame.js",
+		"core/NodeMaterial.js",
+		// Library
+		"libs/common.js",
+		"libs/keywords.js",
+		// Accessors
+		"accessors/PositionNode.js",
+		"accessors/NormalNode.js",
+		"accessors/UVNode.js",
+		"accessors/ScreenUVNode.js",
+		"accessors/ColorsNode.js",
+		"accessors/CameraNode.js",
+		"accessors/ReflectNode.js",
+		"accessors/LightNode.js",
+		// Inputs
+		"inputs/IntNode.js",
+		"inputs/FloatNode.js",
+		"inputs/ColorNode.js",
+		"inputs/Vector2Node.js",
+		"inputs/Vector3Node.js",
+		"inputs/Vector4Node.js",
+		"inputs/TextureNode.js",
+		"inputs/Matrix3Node.js",
+		"inputs/Matrix4Node.js",
+		"inputs/CubeTextureNode.js",
+		// Math
+		"math/Math1Node.js",
+		"math/Math2Node.js",
+		"math/Math3Node.js",
+		"math/OperatorNode.js",
+		// Utils
+		"utils/SwitchNode.js",
+		"utils/JoinNode.js",
+		"utils/TimerNode.js",
+		"utils/RoughnessToBlinnExponentNode.js",
+		"utils/BlinnShininessExponentNode.js",
+		"utils/VelocityNode.js",
+		"utils/LuminanceNode.js",
+		"utils/ColorAdjustmentNode.js",
+		"utils/NoiseNode.js",
+		"utils/ResolutionNode.js",
+		"utils/BumpMapNode.js",
+		"utils/BlurNode.js",
+		"utils/UVTransformNode.js",
+		"utils/MaxMIPLevelNode.js",
+		"utils/NormalMapNode.js",
+		// Phong Material
+		"materials/PhongNode.js",
+		"materials/PhongNodeMaterial.js",
+		// Standard Material
+		"materials/StandardNode.js",
+		"materials/StandardNodeMaterial.js"
+	]);
+	
+}() );

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

@@ -159,6 +159,31 @@ THREE.CameraNode.prototype.onUpdateFrame = function ( frame ) {
 
 };
 
+THREE.CameraNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.setScope( source.scope );
+
+	if ( source.camera ) {
+		
+		this.setCamera( source.camera );
+		
+	}
+
+	switch ( source.scope ) {
+
+		case THREE.CameraNode.DEPTH:
+
+			this.near.number = source.near;
+			this.far.number = source.far;
+
+			break;
+
+	}
+	
+};
+
 THREE.CameraNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -30,6 +30,14 @@ THREE.ColorsNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.ColorsNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.index = source.index;
+	
+};
+
 THREE.ColorsNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 8 - 0
examples/js/nodes/accessors/LightNode.js

@@ -32,6 +32,14 @@ THREE.LightNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.LightNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.scope = source.scope;
+	
+};
+
 THREE.LightNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -68,6 +68,14 @@ THREE.NormalNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.NormalNode.prototype.copy = function ( source ) {
+	
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.scope = source.scope;
+	
+};
+
 THREE.NormalNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -91,6 +91,14 @@ THREE.PositionNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.PositionNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.scope = source.scope;
+	
+};
+
 THREE.PositionNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -38,7 +38,8 @@ THREE.ReflectNode.prototype.generate = function ( builder, output ) {
 	switch ( this.scope ) {
 
 		case THREE.ReflectNode.VECTOR:
-
+//vec3 reflectVec = reflect( -geometry.viewDir, geometry.normal )
+			//builder.material.addFragmentNode( 'vec3 reflectVec = inverseTransformDirection( reflect( -geometry.viewDir, geometry.normal ), viewMatrix );' );
 			builder.material.addFragmentNode( 'vec3 reflectVec = inverseTransformDirection( reflect( -normalize( vViewPosition ), normal ), viewMatrix );' );
 
 			result = 'reflectVec';
@@ -48,7 +49,7 @@ THREE.ReflectNode.prototype.generate = function ( builder, output ) {
 		case THREE.ReflectNode.CUBE:
 
 			var reflectVec = new THREE.ReflectNode( THREE.ReflectNode.VECTOR ).build( builder, 'v3' );
-
+//flipEnvMap
 			builder.material.addFragmentNode( 'vec3 reflectCubeVec = vec3( -1.0 * ' + reflectVec + '.x, ' + reflectVec + '.yz );' );
 
 			result = 'reflectCubeVec';

+ 8 - 0
examples/js/nodes/accessors/ScreenUVNode.js

@@ -35,6 +35,14 @@ THREE.ScreenUVNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.ScreenUVNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.resolution = source.resolution;
+	
+};
+
 THREE.ScreenUVNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -31,6 +31,14 @@ THREE.UVNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.UVNode.prototype.copy = function ( source ) {
+		
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.index = source.index;
+	
+};
+
 THREE.UVNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 9 - 1
examples/js/nodes/AttributeNode.js → examples/js/nodes/core/AttributeNode.js

@@ -38,6 +38,14 @@ THREE.AttributeNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.AttributeNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.type = source.type;
+	
+};
+
 THREE.AttributeNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -46,7 +54,7 @@ THREE.AttributeNode.prototype.toJSON = function ( meta ) {
 
 		data = this.createJSONNode( meta );
 
-		data.out = this.type;
+		data.type = this.type;
 
 	}
 

+ 71 - 0
examples/js/nodes/core/BypassNode.js

@@ -0,0 +1,71 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.BypassNode = function ( code, value ) {
+
+	THREE.GLNode.call( this );
+
+	this.code = code;
+	this.value = value;
+
+};
+
+THREE.BypassNode.prototype = Object.create( THREE.GLNode.prototype );
+THREE.BypassNode.prototype.constructor = THREE.BypassNode;
+THREE.BypassNode.prototype.nodeType = "Bypass";
+
+THREE.BypassNode.prototype.getType = function ( builder ) {
+
+	return this.value ? this.value.getType( builder ) : 'void';
+
+};
+
+THREE.BypassNode.prototype.generate = function ( builder, output ) {
+
+	var code = this.code.build( builder, output ) + ';';
+
+	if ( builder.isShader( 'fragment' ) ) {
+		
+		builder.material.addFragmentNode( code );
+		
+	} else {
+		
+		builder.material.addVertexNode( code );
+
+	}
+
+	if (this.value) {
+		
+		return this.value.build( builder, output );
+		
+	}
+
+};
+
+THREE.BypassNode.prototype.copy = function ( source ) {
+	
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.code = source.code;
+	this.value = source.value;
+	
+};
+
+THREE.BypassNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.code = this.code.toJSON( meta ).uuid;
+
+		if (this.value) data.value = this.value.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 19 - 8
examples/js/nodes/ConstNode.js → examples/js/nodes/core/ConstNode.js

@@ -17,6 +17,8 @@ THREE.ConstNode.RECIPROCAL_PI2 = 'RECIPROCAL_PI2';
 THREE.ConstNode.LOG2 = 'LOG2';
 THREE.ConstNode.EPSILON = 'EPSILON';
 
+THREE.ConstNode.rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=?\s?(.*?)(\;|$)/i;
+
 THREE.ConstNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.ConstNode.prototype.constructor = THREE.ConstNode;
 THREE.ConstNode.prototype.nodeType = "Const";
@@ -29,14 +31,13 @@ THREE.ConstNode.prototype.getType = function ( builder ) {
 
 THREE.ConstNode.prototype.eval = function ( src, useDefine ) {
 
-	src = ( src || '' ).trim();
+	this.src = src || '';
 
 	var name, type, value = "";
 
-	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=?\s?(.*?)(\;|$)/i;
-	var match = src.match( rDeclaration );
+	var match = this.src.match( THREE.ConstNode.rDeclaration );
 
-	this.useDefine = useDefine;
+	this.useDefine = useDefine || this.src.charAt(0) === '#';
 
 	if ( match && match.length > 1 ) {
 
@@ -46,7 +47,7 @@ THREE.ConstNode.prototype.eval = function ( src, useDefine ) {
 
 	} else {
 
-		name = src;
+		name = this.src;
 		type = 'fv1';
 
 	}
@@ -71,6 +72,10 @@ THREE.ConstNode.prototype.build = function ( builder, output ) {
 
 			return 'const ' + this.type + ' ' + this.name + ' = ' + this.value + ';';
 
+		} else if (this.useDefine) {
+		
+			return this.src;
+			
 		}
 
 	} else {
@@ -89,6 +94,14 @@ THREE.ConstNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.ConstNode.prototype.copy = function ( source ) {
+	
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.eval( source.src, source.useDefine );	
+	
+};
+
 THREE.ConstNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -97,10 +110,8 @@ THREE.ConstNode.prototype.toJSON = function ( meta ) {
 
 		data = this.createJSONNode( meta );
 
-		data.name = this.name;
-		data.out = this.type;
+		data.src = this.src;
 
-		if ( this.value ) data.value = this.value;
 		if ( data.useDefine === true ) data.useDefine = true;
 
 	}

+ 16 - 2
examples/js/nodes/FunctionCallNode.js → examples/js/nodes/core/FunctionCallNode.js

@@ -40,7 +40,7 @@ THREE.FunctionCallNode.prototype.generate = function ( builder, output ) {
 	var type = this.getType( builder );
 	var func = this.value;
 
-	var code = func.build( builder, output ) + '(';
+	var code = func.build( builder, output ) + '( ';
 	var params = [];
 
 	for ( var i = 0; i < func.inputs.length; i ++ ) {
@@ -52,12 +52,26 @@ THREE.FunctionCallNode.prototype.generate = function ( builder, output ) {
 
 	}
 
-	code += params.join( ',' ) + ')';
+	code += params.join( ', ' ) + ' )';
 
 	return builder.format( code, type, output );
 
 };
 
+THREE.FunctionCallNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	for ( var prop in source.inputs ) {
+
+		this.inputs[ prop ] = source.inputs[ prop ];
+
+	}
+
+	this.value = source.value;
+	
+};
+
 THREE.FunctionCallNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 30 - 14
examples/js/nodes/FunctionNode.js → examples/js/nodes/core/FunctionNode.js

@@ -3,21 +3,26 @@
  * @thanks bhouston / https://clara.io/
  */
 
-THREE.FunctionNode = function ( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions ) {
-
-	src = src || '';
+THREE.FunctionNode = function ( src, includesOrType, extensionsOrKeywords, keywordsOrExtensions, includes ) {
 
 	this.isMethod = typeof includesOrType !== "string";
 	this.useKeywords = true;
 
 	THREE.TempNode.call( this, this.isMethod ? null : includesOrType );
 
-	if ( this.isMethod ) this.eval( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions );
-	else this.eval( src, extensionsOrIncludes, keywordsOrExtensions );
+	if ( this.isMethod ) {
+		
+		this.eval( src, includesOrType, extensionsOrKeywords, keywordsOrExtensions );
+		
+	} else {
+		
+		this.eval( src, includes, keywordsOrExtensions, extensionsOrKeywords );
+
+	}
 
 };
 
-THREE.FunctionNode.rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\((.*?)\)/i;
+THREE.FunctionNode.rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s*\((.*?)\)/i;
 THREE.FunctionNode.rProperties = /[a-z_0-9]+/ig;
 
 THREE.FunctionNode.prototype = Object.create( THREE.TempNode.prototype );
@@ -64,7 +69,7 @@ THREE.FunctionNode.prototype.getIncludeByName = function ( name ) {
 
 THREE.FunctionNode.prototype.generate = function ( builder, output ) {
 
-	var match, offset = 0, src = this.value;
+	var match, offset = 0, src = this.src;
 
 	for ( var i = 0; i < this.includes.length; i ++ ) {
 
@@ -78,7 +83,7 @@ THREE.FunctionNode.prototype.generate = function ( builder, output ) {
 
 	}
 
-	while ( match = THREE.FunctionNode.rProperties.exec( this.value ) ) {
+	while ( match = THREE.FunctionNode.rProperties.exec( this.src ) ) {
 
 		var prop = match[ 0 ], isGlobal = this.isMethod ? ! this.getInputByName( prop ) : true;
 		var reference = prop;
@@ -131,7 +136,7 @@ THREE.FunctionNode.prototype.generate = function ( builder, output ) {
 
 	} else {
 
-		return builder.format( "(" + src + ")", this.getType( builder ), output );
+		return builder.format( '( ' + src + ' )', this.getType( builder ), output );
 
 	}
 
@@ -139,7 +144,7 @@ THREE.FunctionNode.prototype.generate = function ( builder, output ) {
 
 THREE.FunctionNode.prototype.eval = function ( src, includes, extensions, keywords ) {
 
-	src = ( src || '' ).trim();
+	this.src = src || '';
 
 	this.includes = includes || [];
 	this.extensions = extensions || {};
@@ -147,7 +152,7 @@ THREE.FunctionNode.prototype.eval = function ( src, includes, extensions, keywor
 
 	if ( this.isMethod ) {
 
-		var match = src.match( THREE.FunctionNode.rDeclaration );
+		var match = this.src.match( THREE.FunctionNode.rDeclaration );
 
 		this.inputs = [];
 
@@ -199,8 +204,19 @@ THREE.FunctionNode.prototype.eval = function ( src, includes, extensions, keywor
 
 	}
 
-	this.value = src;
+};
 
+THREE.FunctionNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.isMethod = source.isMethod;
+	this.useKeywords = source.useKeywords;
+	
+	this.eval( source.src, source.includes, source.extensions, source.keywords );
+
+	if ( source.type !== undefined ) this.type = source.type;
+	
 };
 
 THREE.FunctionNode.prototype.toJSON = function ( meta ) {
@@ -211,11 +227,11 @@ THREE.FunctionNode.prototype.toJSON = function ( meta ) {
 
 		data = this.createJSONNode( meta );
 
-		data.src = this.value;
+		data.src = this.src;
 		data.isMethod = this.isMethod;
 		data.useKeywords = this.useKeywords;
 
-		if ( ! this.isMethod ) data.out = this.type;
+		if ( ! this.isMethod ) data.type = this.type;
 
 		data.extensions = JSON.parse( JSON.stringify( this.extensions ) );
 		data.keywords = {};

+ 9 - 8
examples/js/nodes/GLNode.js → examples/js/nodes/core/GLNode.js

@@ -7,7 +7,6 @@ THREE.GLNode = function ( type ) {
 	this.uuid = THREE.Math.generateUUID();
 
 	this.name = "";
-	this.allows = {};
 
 	this.type = type;
 
@@ -71,12 +70,6 @@ THREE.GLNode.prototype.build = function ( builder, output, uuid ) {
 
 	if ( builder.parsing ) this.appendDepsNode( builder, data, output );
 
-	if ( this.allows[ builder.shader ] === false ) {
-
-		throw new Error( 'Shader ' + shader + ' is not compatible with this node.' );
-
-	}
-
 	if ( material.nodes.indexOf( this ) === - 1 ) {
 
 		material.nodes.push( this );
@@ -126,6 +119,14 @@ THREE.GLNode.prototype.getJSONNode = function ( meta ) {
 
 };
 
+THREE.GLNode.prototype.copy = function ( source ) {
+
+	if ( source.name !== undefined ) this.name = source.name;
+	
+	if ( source.userData !== undefined ) this.userData = JSON.parse( JSON.stringify( source.userData ) );
+
+};
+
 THREE.GLNode.prototype.createJSONNode = function ( meta ) {
 
 	var isRootObject = ( meta === undefined || typeof meta === 'string' );
@@ -135,7 +136,7 @@ THREE.GLNode.prototype.createJSONNode = function ( meta ) {
 	if ( typeof this.nodeType !== "string" ) throw new Error( "Node does not allow serialization." );
 
 	data.uuid = this.uuid;
-	data.type = this.nodeType + "Node";
+	data.nodeType = this.nodeType;
 
 	if ( this.name !== "" ) data.name = this.name;
 

+ 19 - 0
examples/js/nodes/InputNode.js → examples/js/nodes/core/InputNode.js

@@ -22,6 +22,25 @@ THREE.InputNode.prototype.isReadonly = function ( builder ) {
 
 };
 
+
+THREE.InputNode.prototype.copy = function ( source ) {
+	
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	if ( source.readonly !== undefined ) this.readonly = source.readonly;
+	
+};
+
+THREE.InputNode.prototype.createJSONNode = function ( meta ) {
+
+	var data = THREE.GLNode.prototype.createJSONNode.call( this, meta );
+	
+	if ( this.readonly === true ) data.readonly = this.readonly;
+
+	return data;
+
+};
+
 THREE.InputNode.prototype.generate = function ( builder, output, uuid, type, ns, needsUpdate ) {
 
 	var material = builder.material;

+ 420 - 0
examples/js/nodes/core/NodeBuilder.js

@@ -0,0 +1,420 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeBuilder = function ( material, renderer ) {
+
+	this.material = material;
+	this.renderer = renderer;
+
+	this.caches = [];
+	this.slots = [];
+
+	this.keywords = {};
+
+	this.parsing = false;
+	this.optimize = true;
+
+	this.update();
+
+};
+
+THREE.NodeBuilder.type = {
+	float: 'fv1',
+	vec2: 'v2',
+	vec3: 'v3',
+	vec4: 'v4',
+	mat4: 'v4',
+	int: 'iv1'
+};
+
+THREE.NodeBuilder.constructors = [
+	'float',
+	'vec2',
+	'vec3',
+	'vec4'
+];
+
+THREE.NodeBuilder.elements = [
+	'x',
+	'y',
+	'z',
+	'w'
+];
+
+THREE.NodeBuilder.prototype = {
+
+	constructor: THREE.NodeBuilder,
+
+	addCache: function ( name, requires ) {
+
+		this.caches.push( {
+			name: name || '',
+			requires: requires || {}
+		} );
+
+		return this.update();
+
+	},
+
+	removeCache: function () {
+
+		this.caches.pop();
+
+		return this.update();
+
+	},
+
+	addSlot: function ( name ) {
+
+		this.slots.push( {
+			name: name || ''
+		} );
+
+		return this.update();
+
+	},
+
+	removeSlot: function () {
+
+		this.slots.pop();
+
+		return this.update();
+
+	},
+
+	isCache: function ( name ) {
+
+		var i = this.caches.length;
+
+		while ( i -- ) {
+
+			if ( this.caches[ i ].name == name ) return true;
+
+		}
+
+		return false;
+
+	},
+
+	isSlot: function ( name ) {
+
+		var i = this.slots.length;
+
+		while ( i -- ) {
+
+			if ( this.slots[ i ].name == name ) return true;
+
+		}
+
+		return false;
+
+	},
+
+	update: function () {
+
+		var cache = this.caches[ this.caches.length - 1 ];
+		var slot = this.slots[ this.slots.length - 1 ];
+
+		this.slot = slot ? slot.name : '';
+		this.cache = cache ? cache.name : '';
+		this.requires = cache ? cache.requires : {};
+
+		return this;
+
+	},
+
+	require: function ( name, node ) {
+
+		this.requires[ name ] = node;
+
+		return this;
+
+	},
+
+	include: function ( node, parent, source ) {
+
+		this.material.include( this, node, parent, source );
+
+		return this;
+
+	},
+
+	colorToVector: function ( color ) {
+
+		return color.replace( 'r', 'x' ).replace( 'g', 'y' ).replace( 'b', 'z' ).replace( 'a', 'w' );
+
+	},
+
+	getConstructorFromLength: function ( len ) {
+
+		return THREE.NodeBuilder.constructors[ len - 1 ];
+
+	},
+
+	getFormatName: function ( format ) {
+
+		return format.replace( /c/g, 'v3' ).replace( /fv1/g, 'v1' ).replace( /iv1/g, 'i' );
+
+	},
+
+	isFormatMatrix: function ( format ) {
+
+		return /^m/.test( format );
+
+	},
+
+	getFormatLength: function ( format ) {
+
+		return parseInt( this.getFormatName( format ).substr( 1 ) );
+
+	},
+
+	getFormatFromLength: function ( len ) {
+
+		if ( len == 1 ) return 'fv1';
+
+		return 'v' + len;
+
+	},
+	
+	resolve: function() {
+		
+		for(var i = 0; i < arguments.length; i++) {
+			
+			var nodeCandidate = arguments[i];
+			
+			if (nodeCandidate !== undefined) {
+				
+				if (nodeCandidate.isNode) {
+				
+					return nodeCandidate;
+					
+				} else if (nodeCandidate.isTexture) {
+					
+					switch( nodeCandidate.mapping ) {
+					
+						case THREE.CubeReflectionMapping:
+						case THREE.CubeRefractionMapping:
+
+							return new THREE.CubeTextureNode( nodeCandidate );
+
+							break;
+						
+						case THREE.CubeUVReflectionMapping:
+						case THREE.CubeUVRefractionMapping:
+
+							return new THREE.TextureCubeNode( new THREE.TextureNode( nodeCandidate ) );
+
+							break;
+							
+						default:
+						
+							return new THREE.TextureNode( nodeCandidate );
+						
+					}
+					
+				} else if (nodeCandidate.isVector2) {
+					
+					return new THREE.Vector2Node( nodeCandidate );
+					
+				} else if (nodeCandidate.isVector3) {
+					
+					return new THREE.Vector3Node( nodeCandidate );
+					
+				} else if (nodeCandidate.isVector4) {
+					
+					return new THREE.Vector4Node( nodeCandidate );
+					
+				}
+				
+			}
+			
+		}
+		
+	},
+
+	format: function ( code, from, to ) {
+
+		var format = this.getFormatName( to + ' = ' + from );
+
+		switch ( format ) {
+
+			case 'v1 = v2': return code + '.x';
+			case 'v1 = v3': return code + '.x';
+			case 'v1 = v4': return code + '.x';
+			case 'v1 = i': return 'float( ' + code + ' )';
+
+			case 'v2 = v1': return 'vec2( ' + code + ' )';
+			case 'v2 = v3': return code + '.xy';
+			case 'v2 = v4': return code + '.xy';
+			case 'v2 = i': return 'vec2( float( ' + code + ' ) )';
+
+			case 'v3 = v1': return 'vec3( ' + code + ' )';
+			case 'v3 = v2': return 'vec3( ' + code + ', 0.0 )';
+			case 'v3 = v4': return code + '.xyz';
+			case 'v3 = i': return 'vec2( float( ' + code + ' ) )';
+
+			case 'v4 = v1': return 'vec4( ' + code + ' )';
+			case 'v4 = v2': return 'vec4( ' + code + ', 0.0, 1.0 )';
+			case 'v4 = v3': return 'vec4( ' + code + ', 1.0 )';
+			case 'v4 = i': return 'vec4( float( ' + code + ' ) )';
+
+			case 'i = v1': return 'int( ' + code + ' )';
+			case 'i = v2': return 'int( ' + code + '.x )';
+			case 'i = v3': return 'int( ' + code + '.x )';
+			case 'i = v4': return 'int( ' + code + '.x )';
+
+		}
+
+		return code;
+
+	},
+
+	getTypeByFormat: function ( format ) {
+
+		return THREE.NodeBuilder.type[ format ] || format;
+
+	},
+
+	getUuid: function ( uuid, useCache ) {
+
+		useCache = useCache !== undefined ? useCache : true;
+
+		if ( useCache && this.cache ) uuid = this.cache + '-' + uuid;
+
+		return uuid;
+
+	},
+
+	getElementByIndex: function ( index ) {
+
+		return THREE.NodeBuilder.elements[ index ];
+
+	},
+
+	getIndexByElement: function ( elm ) {
+
+		return THREE.NodeBuilder.elements.indexOf( elm );
+
+	},
+
+	isShader: function ( shader ) {
+
+		return this.shader === shader;
+
+	},
+
+	setShader: function ( shader ) {
+
+		this.shader = shader;
+
+		return this;
+
+	},
+
+	getTexelDecodingFunctionFromTexture: function( code, texture ) {
+
+		var gammaOverrideLinear = this.getTextureEncodingFromMap( texture, this.requires.gamma && ( this.renderer ? this.renderer.gammaInput : false ) )
+
+		return this.getTexelDecodingFunction( code, gammaOverrideLinear );
+
+	},
+
+	getTexelDecodingFunction: function( value, encoding ) {
+
+		var components = this.getEncodingComponents( encoding );
+
+		return components[ 0 ] + 'ToLinear' + components[ 1 ].replace( 'value', value );
+
+	},
+
+	getTexelEncodingFunction: function( value, encoding ) {
+
+		var components = this.getEncodingComponents( encoding );
+
+		return 'LinearTo' + components[ 0 ] + components[ 1 ].replace( 'value', value );
+
+	},
+
+	getEncodingComponents: function( encoding ) {
+
+		switch ( encoding ) {
+
+			case THREE.LinearEncoding:
+				return [ 'Linear', '( value )' ];
+			case THREE.sRGBEncoding:
+				return [ 'sRGB', '( value )' ];
+			case THREE.RGBEEncoding:
+				return [ 'RGBE', '( value )' ];
+			case THREE.RGBM7Encoding:
+				return [ 'RGBM', '( value, 7.0 )' ];
+			case THREE.RGBM16Encoding:
+				return [ 'RGBM', '( value, 16.0 )' ];
+			case THREE.RGBDEncoding:
+				return [ 'RGBD', '( value, 256.0 )' ];
+			case THREE.GammaEncoding:
+				return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ];
+			default:
+				throw new Error( 'unsupported encoding: ' + encoding );
+
+		}
+
+	},
+
+	getEncodingComponents: function( encoding ) {
+
+		switch ( encoding ) {
+
+			case THREE.LinearEncoding:
+				return [ 'Linear', '( value )' ];
+			case THREE.sRGBEncoding:
+				return [ 'sRGB', '( value )' ];
+			case THREE.RGBEEncoding:
+				return [ 'RGBE', '( value )' ];
+			case THREE.RGBM7Encoding:
+				return [ 'RGBM', '( value, 7.0 )' ];
+			case THREE.RGBM16Encoding:
+				return [ 'RGBM', '( value, 16.0 )' ];
+			case THREE.RGBDEncoding:
+				return [ 'RGBD', '( value, 256.0 )' ];
+			case THREE.GammaEncoding:
+				return [ 'Gamma', '( value, float( GAMMA_FACTOR ) )' ];
+			default:
+				throw new Error( 'unsupported encoding: ' + encoding );
+
+		}
+
+	},
+	
+	getTextureEncodingFromMap: function ( map, gammaOverrideLinear ) {
+
+		var encoding;
+
+		if ( ! map ) {
+
+			encoding = THREE.LinearEncoding;
+
+		} else if ( map.isTexture ) {
+
+			encoding = map.encoding;
+
+		} else if ( map.isWebGLRenderTarget ) {
+
+			console.warn( "THREE.WebGLPrograms.getTextureEncodingFromMap: don't use render targets as textures. Use their .texture property instead." );
+			encoding = map.texture.encoding;
+
+		}
+
+		// add backwards compatibility for WebGLRenderer.gammaInput/gammaOutput parameter, should probably be removed at some point.
+		if ( encoding === THREE.LinearEncoding && gammaOverrideLinear ) {
+
+			encoding = THREE.GammaEncoding;
+
+		}
+
+		return encoding;
+
+	}
+
+};

+ 0 - 0
examples/js/nodes/NodeFrame.js → examples/js/nodes/core/NodeFrame.js


+ 66 - 0
examples/js/nodes/core/NodeLib.js

@@ -0,0 +1,66 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeLib = {
+
+	nodes: {},
+	keywords: {},
+
+	add: function ( node ) {
+
+		this.nodes[ node.name ] = node;
+
+	},
+
+	addKeyword: function ( name, callback, cache ) {
+
+		cache = cache !== undefined ? cache : true;
+
+		this.keywords[ name ] = { callback: callback, cache: cache };
+
+	},
+
+	remove: function ( node ) {
+
+		delete this.nodes[ node.name ];
+
+	},
+
+	removeKeyword: function ( name ) {
+
+		delete this.keywords[ name ];
+
+	},
+
+	get: function ( name ) {
+
+		return this.nodes[ name ];
+
+	},
+
+	getKeyword: function ( name, material ) {
+
+		return this.keywords[ name ].callback.call( this, material );
+
+	},
+
+	getKeywordData: function ( name ) {
+
+		return this.keywords[ name ];
+
+	},
+
+	contains: function ( name ) {
+
+		return this.nodes[ name ] != undefined;
+
+	},
+
+	containsKeyword: function ( name ) {
+
+		return this.keywords[ name ] != undefined;
+
+	}
+
+};

+ 124 - 48
examples/js/nodes/NodeMaterial.js → examples/js/nodes/core/NodeMaterial.js

@@ -29,47 +29,76 @@ THREE.NodeMaterial.types = {
 	m4: 'mat4'
 };
 
-THREE.NodeMaterial.addShortcuts = function ( proto, prop, list ) {
+THREE.NodeMaterial.addShortcuts = function () {
 
-	function applyShortcut( prop, name ) {
+	function applyShortcut( proxy, property, subProperty ) {
 
-		return {
-			get: function () {
+		if ( subProperty ) {
 
-				return this[ prop ][ name ];
+			return {
+				
+				get: function () {
 
-			},
-			set: function ( val ) {
+					return this[ proxy ][ property ][ subProperty ];
 
-				this[ prop ][ name ] = val;
+				},
+				
+				set: function ( val ) {
 
-			}
-		};
+					this[ proxy ][ property ][ subProperty ] = val;
+
+				}
+				
+			};
+
+		} else {
+
+			return {
+				
+				get: function () {
+
+					return this[ proxy ][ property ];
+
+				},
+				
+				set: function ( val ) {
+
+					this[ proxy ][ property ] = val;
+
+				}
+				
+			};
+
+		}
 
 	}
 
-	return ( function () {
+	return function addShortcuts( proto, proxy, list ) {
 
 		var shortcuts = {};
 
 		for ( var i = 0; i < list.length; ++ i ) {
 
-			var name = list[ i ];
+			var data = list[ i ].split( "." ),
+				property = data[0],
+				subProperty = data[1];
 
-			shortcuts[ name ] = applyShortcut( prop, name );
+			shortcuts[ property ] = applyShortcut( proxy, property, subProperty );
 
 		}
 
 		Object.defineProperties( proto, shortcuts );
 
-	} )();
+	};
 
-};
+}();
 
 THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
 THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
 THREE.NodeMaterial.prototype.type = "NodeMaterial";
 
+THREE.NodeMaterial.prototype.isNodeMaterial = true;
+
 THREE.NodeMaterial.prototype.updateFrame = function ( frame ) {
 
 	for ( var i = 0; i < this.updaters.length; ++ i ) {
@@ -122,6 +151,7 @@ THREE.NodeMaterial.prototype.build = function ( params ) {
 
 	this.consts = [];
 	this.functions = [];
+	this.structs = [];
 
 	this.updaters = [];
 
@@ -251,8 +281,9 @@ THREE.NodeMaterial.prototype.build = function ( params ) {
 		this.vertexPars,
 		this.getCodePars( this.vertexUniform, 'uniform' ),
 		this.getIncludes( this.consts[ 'vertex' ] ),
+		this.getIncludes( this.structs[ 'vertex' ] ),
 		this.getIncludes( this.functions[ 'vertex' ] ),
-		'void main(){',
+		'void main() {',
 		this.getCodePars( this.vertexTemps ),
 		vertex,
 		this.vertexCode,
@@ -264,14 +295,15 @@ THREE.NodeMaterial.prototype.build = function ( params ) {
 		this.fragmentPars,
 		this.getCodePars( this.fragmentUniform, 'uniform' ),
 		this.getIncludes( this.consts[ 'fragment' ] ),
+		this.getIncludes( this.structs[ 'fragment' ] ),
 		this.getIncludes( this.functions[ 'fragment' ] ),
-		'void main(){',
+		'void main() {',
 		this.getCodePars( this.fragmentTemps ),
 		this.fragmentCode,
 		fragment,
 		'}'
 	].join( "\n" );
-
+ 
 	if ( params.dispose ) {
 
 		// force update
@@ -497,11 +529,11 @@ THREE.NodeMaterial.prototype.getCodePars = function ( pars, prefix ) {
 		var parsName = pars[ i ].name;
 		var parsValue = pars[ i ].value;
 
-		if ( parsType == 't' && parsValue instanceof THREE.CubeTexture ) parsType = 'tc';
+		if ( parsType === 't' && parsValue instanceof THREE.CubeTexture ) parsType = 'tc';
 
-		var type = THREE.NodeMaterial.types[ parsType ];
+		var type = THREE.NodeMaterial.types[ parsType ] || parsType;
 
-		if ( type == undefined ) throw new Error( "Node pars " + parsType + " not found." );
+		if ( type === undefined ) throw new Error( "Node pars " + parsType + " not found." );
 
 		code += prefix + ' ' + type + ' ' + parsName + ';\n';
 
@@ -557,49 +589,81 @@ THREE.NodeMaterial.prototype.include = function ( builder, node, parent, source
 
 		includes = this.consts[ builder.shader ] = this.consts[ builder.shader ] || [];
 
+	} else if ( node instanceof THREE.StructNode ) {
+
+		includes = this.structs[ builder.shader ] = this.structs[ builder.shader ] || [];
+
 	}
 
-	var included = includes[ node.name ];
+	if (node) {
+	
+		var included = includes[ node.name ];
 
-	if ( ! included ) {
+		if ( ! included ) {
 
-		included = includes[ node.name ] = {
-			node: node,
-			deps: []
-		};
+			included = includes[ node.name ] = {
+				node: node,
+				deps: []
+			};
 
-		includes.push( included );
+			includes.push( included );
 
-		included.src = node.build( builder, 'source' );
+			included.src = node.build( builder, 'source' );
 
-	}
+		}
 
-	if ( node instanceof THREE.FunctionNode && parent && includes[ parent.name ] && includes[ parent.name ].deps.indexOf( node ) == - 1 ) {
+		if ( node instanceof THREE.FunctionNode && parent && includes[ parent.name ] && includes[ parent.name ].deps.indexOf( node ) == - 1 ) {
 
-		includes[ parent.name ].deps.push( node );
+			includes[ parent.name ].deps.push( node );
 
-		if ( node.includes && node.includes.length ) {
+			if ( node.includes && node.includes.length ) {
 
-			var i = 0;
+				var i = 0;
 
-			do {
+				do {
 
-				this.include( builder, node.includes[ i ++ ], parent );
+					this.include( builder, node.includes[ i ++ ], parent );
 
-			} while ( i < node.includes.length );
+				} while ( i < node.includes.length );
 
-		}
+			}
 
-	}
+		}
 
-	if ( source ) {
+		if ( source ) {
 
-		included.src = source;
+			included.src = source;
 
+		}
+		
+	} else {
+		
+		throw new Error("Include not found.");
+		
 	}
 
 };
 
+THREE.NodeMaterial.prototype.copy = function ( source ) {
+	
+	var uuid = this.uuid;
+	
+	for (var name in source) {
+		
+		this[name] = source[name];
+		
+	}
+	
+	this.uuid = uuid;
+	
+	if ( source.userData !== undefined) {
+		
+		this.userData = JSON.parse( JSON.stringify( source.userData ) );
+		
+	}
+	
+};
+
 THREE.NodeMaterial.prototype.toJSON = function ( meta ) {
 
 	var isRootObject = ( meta === undefined || typeof meta === 'string' );
@@ -625,30 +689,42 @@ THREE.NodeMaterial.prototype.toJSON = function ( meta ) {
 
 		if ( this.name !== "" ) data.name = this.name;
 
+		if ( this.size !== undefined ) data.size = this.size;
+		if ( this.sizeAttenuation !== undefined ) data.sizeAttenuation = this.sizeAttenuation;
+
 		if ( this.blending !== THREE.NormalBlending ) data.blending = this.blending;
 		if ( this.flatShading === true ) data.flatShading = this.flatShading;
 		if ( this.side !== THREE.FrontSide ) data.side = this.side;
+		if ( this.vertexColors !== THREE.NoColors ) data.vertexColors = this.vertexColors;
 
-		if ( this.transparent === true ) data.transparent = this.transparent;
+		if ( this.depthFunc !== THREE.LessEqualDepth ) data.depthFunc = this.depthFunc;
+		if ( this.depthTest === false ) data.depthTest = this.depthTest;
+		if ( this.depthWrite === false ) data.depthWrite = this.depthWrite;
 
-		data.depthFunc = this.depthFunc;
-		data.depthTest = this.depthTest;
-		data.depthWrite = this.depthWrite;
+		if ( this.linewidth !== 1 ) data.linewidth = this.linewidth;
+		if ( this.dashSize !== undefined ) data.dashSize = this.dashSize;
+		if ( this.gapSize !== undefined ) data.gapSize = this.gapSize;
+		if ( this.scale !== undefined ) data.scale = this.scale;
 
+		if ( this.dithering === true ) data.dithering = true;
+		
 		if ( this.wireframe === true ) data.wireframe = this.wireframe;
 		if ( this.wireframeLinewidth > 1 ) data.wireframeLinewidth = this.wireframeLinewidth;
 		if ( this.wireframeLinecap !== 'round' ) data.wireframeLinecap = this.wireframeLinecap;
 		if ( this.wireframeLinejoin !== 'round' ) data.wireframeLinejoin = this.wireframeLinejoin;
 
+		if ( this.alphaTest > 0 ) data.alphaTest = this.alphaTest;
+		if ( this.premultipliedAlpha === true ) data.premultipliedAlpha = this.premultipliedAlpha;
+		
 		if ( this.morphTargets === true ) data.morphTargets = true;
 		if ( this.skinning === true ) data.skinning = true;
 
-		data.fog = this.fog;
-		data.lights = this.lights;
-
 		if ( this.visible === false ) data.visible = false;
 		if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
 
+		data.fog = this.fog;
+		data.lights = this.lights;
+		
 		data.vertex = this.vertex.toJSON( meta ).uuid;
 		data.fragment = this.fragment.toJSON( meta ).uuid;
 

+ 0 - 0
examples/js/nodes/NodeUniform.js → examples/js/nodes/core/NodeUniform.js


+ 9 - 1
examples/js/nodes/RawNode.js → examples/js/nodes/core/RawNode.js

@@ -22,7 +22,7 @@ THREE.RawNode.prototype.generate = function ( builder ) {
 
 	var code = data.code + '\n';
 
-	if ( builder.shader == 'vertex' ) {
+	if ( builder.isShader( 'vertex' ) ) {
 
 		code += 'gl_Position = ' + data.result + ';';
 
@@ -36,6 +36,14 @@ THREE.RawNode.prototype.generate = function ( builder ) {
 
 };
 
+THREE.RawNode.prototype.copy = function ( source ) {
+	
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.value = source.value;
+	
+};
+
 THREE.RawNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 102 - 0
examples/js/nodes/core/StructNode.js

@@ -0,0 +1,102 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.StructNode = function ( src ) {
+
+	THREE.TempNode.call( this);
+
+	this.eval( src );
+
+};
+
+THREE.StructNode.rDeclaration = /^struct\s*([a-z_0-9]+)\s*{\s*((.|\n)*?)}/img;
+THREE.StructNode.rProperties = /\s*(\w*?)\s*(\w*?)(\=|\;)/img;
+
+THREE.StructNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.StructNode.prototype.constructor = THREE.StructNode;
+THREE.StructNode.prototype.nodeType = "Struct";
+
+THREE.StructNode.prototype.getType = function ( builder ) {
+
+	return builder.getTypeByFormat( this.name );
+
+};
+
+THREE.StructNode.prototype.getInputByName = function ( name ) {
+
+	var i = this.inputs.length;
+
+	while ( i -- ) {
+
+		if ( this.inputs[ i ].name === name )
+			return this.inputs[ i ];
+
+	}
+
+};
+
+THREE.StructNode.prototype.generate = function ( builder, output ) {
+
+	if ( output === 'source' ) {
+
+		return this.src + ';';
+
+	} else {
+
+		return builder.format( "(" + src + ")", this.getType( builder ), output );
+
+	}
+
+};
+
+THREE.StructNode.prototype.eval = function ( src ) {
+
+	this.src = src || '';
+	
+	this.inputs = [];
+	
+	var declaration = THREE.StructNode.rDeclaration.exec( this.src );
+	
+	if (declaration) {
+		
+		var properties = declaration[2], matchType, matchName;
+		
+		while ( matchType = THREE.FunctionNode.rProperties.exec( properties ) ) {
+			
+			matchName = THREE.FunctionNode.rProperties.exec( properties )[0];
+			
+			this.inputs.push( {
+				name: matchName,
+				type: matchType
+			} );
+			
+		}
+		
+		this.name = declaration[1];
+
+	} else {
+		
+		this.name = '';
+		
+	}
+	
+	this.type = this.name;
+
+};
+
+THREE.StructNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.src = this.src;
+
+	}
+
+	return data;
+
+};

+ 2 - 2
examples/js/nodes/TempNode.js → examples/js/nodes/core/TempNode.js

@@ -76,8 +76,8 @@ THREE.TempNode.prototype.build = function ( builder, output, uuid, ns ) {
 
 			var code = this.generate( builder, type, uuid );
 
-			if ( builder.isShader( 'vertex' ) ) material.addVertexNode( name + '=' + code + ';' );
-			else material.addFragmentNode( name + '=' + code + ';' );
+			if ( builder.isShader( 'vertex' ) ) material.addVertexNode( name + ' = ' + code + ';' );
+			else material.addFragmentNode( name + ' = ' + code + ';' );
 
 			return builder.format( name, type, output );
 

+ 21 - 2
examples/js/nodes/VarNode.js → examples/js/nodes/core/VarNode.js

@@ -2,9 +2,11 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.VarNode = function ( type ) {
+THREE.VarNode = function ( type, value ) {
 
 	THREE.GLNode.call( this, type );
+	
+	this.value = value;
 
 };
 
@@ -22,10 +24,25 @@ THREE.VarNode.prototype.generate = function ( builder, output ) {
 
 	var varying = builder.material.getVar( this.uuid, this.type );
 
+	if ( this.value && builder.isShader( 'vertex' ) ) {
+
+		builder.material.addVertexNode( varying.name + ' = ' + this.value.build( builder, this.getType( builder ) ) + ';' );
+
+	}
+	
 	return builder.format( varying.name, this.getType( builder ), output );
 
 };
 
+THREE.VarNode.prototype.copy = function ( source ) {
+	
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.type = source.type;
+	this.value = source.value;
+	
+};
+
 THREE.VarNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -34,7 +51,9 @@ THREE.VarNode.prototype.toJSON = function ( meta ) {
 
 		data = this.createJSONNode( meta );
 
-		data.out = this.type;
+		data.type = this.type;
+
+		if ( this.value ) data.value = this.value.toJSON( meta ).uuid;
 
 	}
 

+ 10 - 2
examples/js/nodes/inputs/ColorNode.js

@@ -2,11 +2,11 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ColorNode = function ( color ) {
+THREE.ColorNode = function ( color, g, b ) {
 
 	THREE.InputNode.call( this, 'c' );
 
-	this.value = new THREE.Color( color || 0 );
+	this.value = color instanceof THREE.Color ? color : new THREE.Color( color || 0, g, b );
 
 };
 
@@ -22,6 +22,14 @@ THREE.ColorNode.prototype.generateReadonly = function ( builder, output, uuid, t
 
 };
 
+THREE.ColorNode.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.value.copy( source );
+	
+};
+
 THREE.ColorNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 15 - 15
examples/js/nodes/inputs/CubeTextureNode.js

@@ -34,33 +34,33 @@ THREE.CubeTextureNode.prototype.generate = function ( builder, output ) {
 	var coord = this.coord.build( builder, 'v3' );
 	var bias = this.bias ? this.bias.build( builder, 'fv1' ) : undefined;
 
-	if ( bias == undefined && builder.requires.bias ) {
+	if ( bias === undefined && builder.requires.bias ) {
 
-		bias = builder.requires.bias.build( builder, 'fv1' );
+		bias = new builder.requires.bias( this ).build( builder, 'fv1' );
 
 	}
 
 	var code;
 
-	if ( bias ) code = 'texCubeBias(' + cubetex + ',' + coord + ',' + bias + ')';
-	else code = 'texCube(' + cubetex + ',' + coord + ')';
+	if ( bias ) code = 'texCubeBias( ' + cubetex + ', ' + coord + ', ' + bias + ' )';
+	else code = 'texCube( ' + cubetex + ', ' + coord + ' )';
 
-	if ( builder.isSlot( 'color' ) ) {
+	code = builder.getTexelDecodingFunctionFromTexture( code, this.value );
 
-		code = 'mapTexelToLinear(' + code + ')';
-
-	} else if ( builder.isSlot( 'emissive' ) ) {
-
-		code = 'emissiveMapTexelToLinear(' + code + ')';
-
-	} else if ( builder.isSlot( 'environment' ) ) {
+	return builder.format( code, this.type, output );
 
-		code = 'envMapTexelToLinear(' + code + ')';
+};
 
-	}
+THREE.CubeTextureNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	if ( source.value ) this.value = source.value;
 
-	return builder.format( code, this.type, output );
+	this.coord = source.coord;
 
+	if ( source.bias ) this.bias = source.bias;
+	
 };
 
 THREE.CubeTextureNode.prototype.toJSON = function ( meta ) {

+ 8 - 2
examples/js/nodes/inputs/FloatNode.js

@@ -16,10 +16,16 @@ THREE.FloatNode.prototype.nodeType = "Float";
 
 THREE.FloatNode.prototype.generateReadonly = function ( builder, output, uuid, type, ns, needsUpdate ) {
 
-	var val = this.value;
+	return builder.format( this.value + ( this.value % 1 ? '' : '.0' ), type, output );
 
-	return builder.format( Math.floor( val ) !== val ? val : val + ".0", type, output );
+};
 
+THREE.FloatNode.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.value = source.value;
+	
 };
 
 THREE.FloatNode.prototype.toJSON = function ( meta ) {

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

@@ -20,6 +20,14 @@ THREE.IntNode.prototype.generateReadonly = function ( builder, output, uuid, typ
 
 };
 
+THREE.IntNode.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.value = source.value;
+	
+};
+
 THREE.IntNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 29 - 2
examples/js/nodes/inputs/Matrix3Node.js

@@ -14,12 +14,41 @@ THREE.Matrix3Node.prototype = Object.create( THREE.InputNode.prototype );
 THREE.Matrix3Node.prototype.constructor = THREE.Matrix3Node;
 THREE.Matrix3Node.prototype.nodeType = "Matrix3";
 
+Object.defineProperties( THREE.Matrix3Node.prototype, {
+
+	elements: {
+		
+		set: function (val) {
+
+			this.value.elements = val;
+
+		},
+		
+		get: function () {
+
+			return this.value.elements;
+
+		}
+		
+	}
+
+} );
+
 THREE.Matrix3Node.prototype.generateReadonly = function ( builder, output, uuid, type, ns, needsUpdate ) {
 
 	return builder.format( "mat3( " + this.value.elements.join( ", " ) + " )", type, output );
 
 };
 
+
+THREE.Matrix3Node.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.value.fromArray( source.elements );
+	
+};
+
 THREE.Matrix3Node.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -30,8 +59,6 @@ THREE.Matrix3Node.prototype.toJSON = function ( meta ) {
 
 		data.elements = this.value.elements.concat();
 
-		if ( this.readonly === true ) data.readonly = true;
-
 	}
 
 	return data;

+ 28 - 2
examples/js/nodes/inputs/Matrix4Node.js

@@ -14,12 +14,40 @@ THREE.Matrix4Node.prototype = Object.create( THREE.InputNode.prototype );
 THREE.Matrix4Node.prototype.constructor = THREE.Matrix4Node;
 THREE.Matrix4Node.prototype.nodeType = "Matrix4";
 
+Object.defineProperties( THREE.Matrix4Node.prototype, {
+
+	elements: {
+		
+		set: function (val) {
+
+			this.value.elements = val;
+
+		},
+		
+		get: function () {
+
+			return this.value.elements;
+
+		}
+		
+	}
+
+} );
+
 THREE.Matrix4Node.prototype.generateReadonly = function ( builder, output, uuid, type, ns, needsUpdate ) {
 
 	return builder.format( "mat4( " + this.value.elements.join( ", " ) + " )", type, output );
 
 };
 
+THREE.Matrix4Node.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.scope.value.fromArray( source.elements );
+	
+};
+
 THREE.Matrix4Node.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -30,8 +58,6 @@ THREE.Matrix4Node.prototype.toJSON = function ( meta ) {
 
 		data.elements = this.value.elements.concat();
 
-		if ( this.readonly === true ) data.readonly = true;
-
 	}
 
 	return data;

+ 8 - 0
examples/js/nodes/inputs/ReflectorNode.js

@@ -52,6 +52,14 @@ THREE.ReflectorNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.ReflectorNode.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.scope.mirror = source.mirror;
+
+};
+
 THREE.ReflectorNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 15 - 14
examples/js/nodes/inputs/TextureNode.js

@@ -37,7 +37,7 @@ THREE.TextureNode.prototype.generate = function ( builder, output ) {
 
 	if ( bias == undefined && builder.requires.bias ) {
 
-		bias = builder.requires.bias.build( builder, 'fv1' );
+		bias = new builder.requires.bias( this ).build( builder, 'fv1' );
 
 	}
 
@@ -46,25 +46,26 @@ THREE.TextureNode.prototype.generate = function ( builder, output ) {
 	if ( this.project ) method = 'texture2DProj';
 	else method = bias ? 'tex2DBias' : 'tex2D';
 
-	if ( bias ) code = method + '(' + tex + ',' + coord + ',' + bias + ')';
-	else code = method + '(' + tex + ',' + coord + ')';
+	if ( bias ) code = method + '( ' + tex + ', ' + coord + ', ' + bias + ' )';
+	else code = method + '( ' + tex + ', ' + coord + ' )';
 
-	if ( builder.isSlot( 'color' ) ) {
+	code = builder.getTexelDecodingFunctionFromTexture( code, this.value );
 
-		code = 'mapTexelToLinear(' + code + ')';
-
-	} else if ( builder.isSlot( 'emissive' ) ) {
-
-		code = 'emissiveMapTexelToLinear(' + code + ')';
-
-	} else if ( builder.isSlot( 'environment' ) ) {
+	return builder.format( code, this.type, output );
 
-		code = 'envMapTexelToLinear(' + code + ')';
+};
 
-	}
+THREE.TextureNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	if ( source.value ) this.value = source.value;
 
-	return builder.format( code, this.type, output );
+	this.coord = source.coord;
 
+	if ( source.bias ) this.bias = source.bias;
+	if ( source.project !== undefined ) this.project = source.project;
+	
 };
 
 THREE.TextureNode.prototype.toJSON = function ( meta ) {

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

@@ -6,7 +6,7 @@ THREE.Vector2Node = function ( x, y ) {
 
 	THREE.InputNode.call( this, 'v2' );
 
-	this.value = new THREE.Vector2( x, y );
+	this.value = x instanceof THREE.Vector2 ? x : new THREE.Vector2( x, y );
 
 };
 
@@ -22,6 +22,14 @@ THREE.Vector2Node.prototype.generateReadonly = function ( builder, output, uuid,
 
 };
 
+THREE.Vector2Node.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.value.copy( source );
+
+};
+
 THREE.Vector2Node.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 9 - 2
examples/js/nodes/inputs/Vector3Node.js

@@ -6,8 +6,7 @@ THREE.Vector3Node = function ( x, y, z ) {
 
 	THREE.InputNode.call( this, 'v3' );
 
-	this.type = 'v3';
-	this.value = new THREE.Vector3( x, y, z );
+	this.value = x instanceof THREE.Vector3 ? x : new THREE.Vector3( x, y, z );
 
 };
 
@@ -23,6 +22,14 @@ THREE.Vector3Node.prototype.generateReadonly = function ( builder, output, uuid,
 
 };
 
+THREE.Vector3Node.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.value.copy( source );
+	
+};
+
 THREE.Vector3Node.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -6,7 +6,7 @@ THREE.Vector4Node = function ( x, y, z, w ) {
 
 	THREE.InputNode.call( this, 'v4' );
 
-	this.value = new THREE.Vector4( x, y, z, w );
+	this.value = x instanceof THREE.Vector4 ? x : new THREE.Vector4( x, y, z, w );
 
 };
 
@@ -22,6 +22,14 @@ THREE.Vector4Node.prototype.generateReadonly = function ( builder, output, uuid,
 
 };
 
+THREE.Vector4Node.prototype.copy = function ( source ) {
+			
+	THREE.InputNode.prototype.copy.call( this, source );
+	
+	this.value.copy( source );
+
+};
+
 THREE.Vector4Node.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 156 - 0
examples/js/nodes/libs/common.js

@@ -0,0 +1,156 @@
+//
+//	Luma
+//
+
+THREE.NodeLib.add( new THREE.ConstNode( "vec3 LUMA vec3(0.2125, 0.7154, 0.0721)" ) );
+
+//
+//	NormalMap
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+
+	// Per-Pixel Tangent Space Normal Mapping
+	// http://hacksoflife.blogspot.ch/2009/11/per-pixel-tangent-space-normal-mapping.html
+
+	"vec3 perturbNormal2Arb( vec3 eye_pos, vec3 surf_norm, vec3 map, vec2 mUv, vec2 normalScale ) {",
+
+	// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
+	
+	"	vec3 q0 = vec3( dFdx( eye_pos.x ), dFdx( eye_pos.y ), dFdx( eye_pos.z ) );",
+	"	vec3 q1 = vec3( dFdy( eye_pos.x ), dFdy( eye_pos.y ), dFdy( eye_pos.z ) );",
+	"	vec2 st0 = dFdx( mUv.st );",
+	"	vec2 st1 = dFdy( mUv.st );",
+	
+	"	float scale = sign( st1.t * st0.s - st0.t * st1.s );", // we do not care about the magnitude
+	
+	"	vec3 S = normalize( ( q0 * st1.t - q1 * st0.t ) * scale );",
+	"	vec3 T = normalize( ( - q0 * st1.s + q1 * st0.s ) * scale );",
+	"	vec3 N = normalize( surf_norm );",
+	"	mat3 tsn = mat3( S, T, N );",
+	
+	"	vec3 mapN = map * 2.0 - 1.0;",
+	
+	"	mapN.xy *= normalScale;",
+	"	mapN.xy *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );",
+	
+	"	return normalize( tsn * mapN );",
+
+	"}"
+
+].join( "\n" ), null, { derivatives: true } ) );
+
+//
+//	BumpMap
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+
+	// Bump Mapping Unparametrized Surfaces on the GPU by Morten S. Mikkelsen
+	// http://api.unrealengine.com/attachments/Engine/Rendering/LightingAndShadows/BumpMappingWithoutTangentSpace/mm_sfgrad_bump.pdf
+	
+	// Evaluate the derivative of the height w.r.t. screen-space using forward differencing (listing 2)
+
+	"vec2 dHdxy_fwd( sampler2D bumpMap, vec2 vUv, float bumpScale ) {",
+
+	// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
+	
+	"	vec2 dSTdx = dFdx( vUv );",
+	"	vec2 dSTdy = dFdy( vUv );",
+	
+	"	float Hll = bumpScale * texture2D( bumpMap, vUv ).x;",
+	"	float dBx = bumpScale * texture2D( bumpMap, vUv + dSTdx ).x - Hll;",
+	"	float dBy = bumpScale * texture2D( bumpMap, vUv + dSTdy ).x - Hll;",
+	
+	"	return vec2( dBx, dBy );",
+	
+	"}"
+
+].join( "\n" ), null, { derivatives: true } ) );
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+
+	"vec3 perturbNormalArb( vec3 surf_pos, vec3 surf_norm, vec2 dHdxy ) {",
+
+	// Workaround for Adreno 3XX dFd*( vec3 ) bug. See #9988
+	
+	"	vec3 vSigmaX = vec3( dFdx( surf_pos.x ), dFdx( surf_pos.y ), dFdx( surf_pos.z ) );",
+	"	vec3 vSigmaY = vec3( dFdy( surf_pos.x ), dFdy( surf_pos.y ), dFdy( surf_pos.z ) );",
+	"	vec3 vN = surf_norm;", // normalized
+	
+	"	vec3 R1 = cross( vSigmaY, vN );",
+	"	vec3 R2 = cross( vN, vSigmaX );",
+
+	"	float fDet = dot( vSigmaX, R1 );",
+	
+	"	fDet *= ( float( gl_FrontFacing ) * 2.0 - 1.0 );",
+	
+	"	vec3 vGrad = sign( fDet ) * ( dHdxy.x * R1 + dHdxy.y * R2 );",
+	
+	"	return normalize( abs( fDet ) * surf_norm - vGrad );",
+
+	"}"
+
+].join( "\n" ), null, { derivatives: true } ) );
+
+//
+//	Noise
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+	"float snoise(vec2 co) {",
+	"	return fract( sin( dot(co.xy, vec2(12.9898,78.233) ) ) * 43758.5453 );",
+	"}"
+].join( "\n" ) ) );
+
+//
+//	Hue
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+	"vec3 hue_rgb(vec3 rgb, float adjustment) {",
+	"	const mat3 RGBtoYIQ = mat3(0.299, 0.587, 0.114, 0.595716, -0.274453, -0.321263, 0.211456, -0.522591, 0.311135);",
+	"	const mat3 YIQtoRGB = mat3(1.0, 0.9563, 0.6210, 1.0, -0.2721, -0.6474, 1.0, -1.107, 1.7046);",
+	"	vec3 yiq = RGBtoYIQ * rgb;",
+	"	float hue = atan(yiq.z, yiq.y) + adjustment;",
+	"	float chroma = sqrt(yiq.z * yiq.z + yiq.y * yiq.y);",
+	"	return YIQtoRGB * vec3(yiq.x, chroma * cos(hue), chroma * sin(hue));",
+	"}"
+].join( "\n" ) ) );
+
+//
+//	Saturation
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+// Algorithm from Chapter 16 of OpenGL Shading Language
+	"vec3 saturation_rgb(vec3 rgb, float adjustment) {",
+	"	vec3 intensity = vec3(dot(rgb, LUMA));",
+	"	return mix(intensity, rgb, adjustment);",
+	"}"
+].join( "\n" ) ) );
+
+//
+//	Luminance
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+// Algorithm from Chapter 10 of Graphics Shaders
+	"float luminance_rgb(vec3 rgb) {",
+	"	return dot(rgb, LUMA);",
+	"}"
+].join( "\n" ) ) );
+
+//
+//	Vibrance
+//
+
+THREE.NodeLib.add( new THREE.FunctionNode( [
+// Shader by Evan Wallace adapted by @lo-th
+	"vec3 vibrance_rgb(vec3 rgb, float adjustment) {",
+	"	float average = (rgb.r + rgb.g + rgb.b) / 3.0;",
+	"	float mx = max(rgb.r, max(rgb.g, rgb.b));",
+	"	float amt = (mx - average) * (-3.0 * adjustment);",
+	"	return mix(rgb.rgb, vec3(mx), amt);",
+	"}"
+].join( "\n" ) ) );

+ 53 - 0
examples/js/nodes/libs/keywords.js

@@ -0,0 +1,53 @@
+THREE.NodeLib.addKeyword( 'uv', function () {
+
+	return new THREE.UVNode();
+
+} );
+
+THREE.NodeLib.addKeyword( 'uv2', function () {
+
+	return new THREE.UVNode( 1 );
+
+} );
+
+THREE.NodeLib.addKeyword( 'position', function () {
+
+	return new THREE.PositionNode();
+
+} );
+
+THREE.NodeLib.addKeyword( 'worldPosition', function () {
+
+	return new THREE.PositionNode( THREE.PositionNode.WORLD );
+
+} );
+
+THREE.NodeLib.addKeyword( 'normal', function () {
+
+	return new THREE.NormalNode();
+
+} );
+
+THREE.NodeLib.addKeyword( 'worldNormal', function () {
+
+	return new THREE.NormalNode( THREE.NormalNode.WORLD );
+
+} );
+
+THREE.NodeLib.addKeyword( 'viewPosition', function () {
+
+	return new THREE.PositionNode( THREE.NormalNode.VIEW );
+
+} );
+
+THREE.NodeLib.addKeyword( 'viewNormal', function () {
+
+	return new THREE.NormalNode( THREE.NormalNode.VIEW );
+
+} );
+
+THREE.NodeLib.addKeyword( 'time', function () {
+
+	return new THREE.TimerNode();
+
+} );

+ 77 - 0
examples/js/nodes/materials/MeshStandardNode.js

@@ -0,0 +1,77 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.MeshStandardNode = function () {
+
+	THREE.StandardNode.call( this );
+
+	this.properties = {
+		color: new THREE.ColorNode( 0xffffff ),
+		roughness: new THREE.FloatNode( 0.5 ),
+		metalness: new THREE.FloatNode( 0.5 ),
+		normalScale: new THREE.Vector2Node( 1, 1 )
+	};
+
+};
+
+THREE.MeshStandardNode.prototype = Object.create( THREE.StandardNode.prototype );
+THREE.MeshStandardNode.prototype.constructor = THREE.MeshStandardNode;
+
+THREE.MeshStandardNode.prototype.build = function ( builder ) {
+
+	var material = builder.material,
+		props = this.properties;
+
+	if ( builder.isShader('fragment') ) {
+		
+		// slots
+		// * color
+		// * map
+		
+		var color = builder.resolve( props.color.value, props.color ),
+			map = builder.resolve( props.map );
+		
+		this.color = map ? new THREE.OperatorNode( color, map, THREE.OperatorNode.MUL ) : color;
+		
+		// slots
+		// * roughness
+		// * roughnessMap
+		
+		var roughness = builder.resolve( props.roughness.value, props.roughness ),
+			roughnessMap = builder.resolve( props.roughnessMap );
+		
+		this.roughness = roughnessMap ? new THREE.OperatorNode( roughness, new THREE.SwitchNode( roughnessMap, "g" ), THREE.OperatorNode.MUL ) : roughness;
+		
+		// slots
+		// * metalness
+		// * metalnessMap
+		
+		var metalness = builder.resolve( props.metalness.value, props.metalness ),
+			metalnessMap = builder.resolve( props.metalnessMap );
+		
+		this.metalness = metalnessMap ? new THREE.OperatorNode( metalness, new THREE.SwitchNode( metalnessMap, "b" ), THREE.OperatorNode.MUL ) : metalness;
+
+		// slots
+		// * normalMap
+		// * normalScale
+		
+		if ( props.normalMap ) {
+			
+			this.normal = new THREE.NormalMapNode( builder.resolve( props.normalMap ) );
+			this.normal.scale = builder.resolve( props.normalScale );
+
+		}
+
+		// slots
+		// * envMap
+		
+		this.environment = builder.resolve( props.envMap );
+		
+	}
+	
+	// build code
+
+	return THREE.StandardNode.prototype.build.call( this, builder );
+
+};

+ 41 - 0
examples/js/nodes/materials/MeshStandardNodeMaterial.js

@@ -0,0 +1,41 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.MeshStandardNodeMaterial = function () {
+
+	var node = new THREE.MeshStandardNode();
+
+	THREE.NodeMaterial.call( this, node, node );
+
+	this.type = "MeshStandardNodeMaterial";
+
+};
+
+THREE.MeshStandardNodeMaterial.prototype = Object.create( THREE.NodeMaterial.prototype );
+THREE.MeshStandardNodeMaterial.prototype.constructor = THREE.StandardNodeMaterial;
+
+Object.defineProperties( THREE.MeshStandardNodeMaterial.prototype, {
+
+	properties: {
+		
+		get: function () {
+
+			return this.fragment.properties;
+
+		}
+		
+	}
+
+} );
+
+THREE.NodeMaterial.addShortcuts( THREE.MeshStandardNodeMaterial.prototype, 'properties', [
+	"color.value",
+	"roughness.value",
+	"metalness.value",
+	"map",
+	"normalMap",
+	"metalnessMap",
+	"roughnessMap",
+	"envMap"
+] );

+ 48 - 14
examples/js/nodes/materials/PhongNode.js

@@ -47,6 +47,7 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 			"#endif",
 
 			"#include <common>",
+			"#include <encodings_pars_fragment>", // encoding functions
 			"#include <fog_pars_vertex>",
 			"#include <morphtarget_pars_vertex>",
 			"#include <skinning_pars_vertex>",
@@ -74,7 +75,7 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 
 			output.push(
 				transform.code,
-				"transformed = " + transform.result + ";"
+				transform.result ? "transformed = " + transform.result + ";" : ''
 			);
 
 		}
@@ -167,7 +168,7 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 			"	vec3 specular = " + specular.result + ";",
 
 			shininess.code,
-			"	float shininess = max(0.0001," + shininess.result + ");",
+			"	float shininess = max( 0.0001, " + shininess.result + " );",
 
 			"	float specularStrength = 1.0;" // Ignored in MaterialNode ( replace to specular )
 		];
@@ -183,19 +184,11 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 
 		if ( normal ) {
 
-			builder.include( 'perturbNormal2Arb' );
-
-			output.push( normal.code );
-
-			if ( normalScale ) output.push( normalScale.code );
-
 			output.push(
-				'normal = perturbNormal2Arb(-vViewPosition,normal,' +
-				normal.result + ',' +
-				new THREE.UVNode().build( builder, 'v2' ) + ',' +
-				( normalScale ? normalScale.result : 'vec2( 1.0 )' ) + ');'
+				normal.code,
+				'normal = ' + normal.result + ';'
 			);
-
+		
 		}
 
 		// optimization for now
@@ -286,6 +279,18 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 
 		}
 
+		switch( builder.material.combine ) {
+
+			case THREE.ENVMAP_BLENDING_MULTIPLY:
+				
+				//output.push( "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;" );
+				//outgoingLight = mix( outgoingLight, outgoingLight * envColor.xyz, specularStrength * reflectivity );
+				
+				break;
+			
+			
+		}
+		
 		if ( alpha ) {
 
 			output.push( "gl_FragColor = vec4( outgoingLight, " + alpha.result + " );" );
@@ -311,6 +316,36 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 
 };
 
+THREE.PhongNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	// vertex
+
+	if ( source.transform ) this.transform = source.transform;
+
+	// fragment
+
+	this.color = source.color;
+	this.specular = source.specular;
+	this.shininess = source.shininess;
+
+	if ( source.alpha ) this.alpha = source.alpha;
+
+	if ( source.normal ) this.normal = source.normal;
+
+	if ( source.light ) this.light = source.light;
+	if ( source.shadow ) this.shadow = source.shadow;
+
+	if ( source.ao ) this.ao = source.ao;
+	
+	if ( source.emissive ) this.emissive = source.emissive;
+	if ( source.ambient ) this.ambient = source.ambient;
+
+	if ( source.environment ) this.environment = source.environment;
+	if ( source.environmentAlpha ) this.environmentAlpha = source.environmentAlpha;
+
+};
 
 THREE.PhongNode.prototype.toJSON = function ( meta ) {
 
@@ -333,7 +368,6 @@ THREE.PhongNode.prototype.toJSON = function ( meta ) {
 		if ( this.alpha ) data.alpha = this.alpha.toJSON( meta ).uuid;
 
 		if ( this.normal ) data.normal = this.normal.toJSON( meta ).uuid;
-		if ( this.normalScale ) data.normalScale = this.normalScale.toJSON( meta ).uuid;
 
 		if ( this.light ) data.light = this.light.toJSON( meta ).uuid;
 

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

@@ -4,9 +4,9 @@
 
 THREE.PhongNodeMaterial = function () {
 
-	this.node = new THREE.PhongNode();
+	var node = new THREE.PhongNode();
 
-	THREE.NodeMaterial.call( this, this.node, this.node );
+	THREE.NodeMaterial.call( this, node, node );
 
 	this.type = "PhongNodeMaterial";
 
@@ -15,5 +15,18 @@ THREE.PhongNodeMaterial = function () {
 THREE.PhongNodeMaterial.prototype = Object.create( THREE.NodeMaterial.prototype );
 THREE.PhongNodeMaterial.prototype.constructor = THREE.PhongNodeMaterial;
 
-THREE.NodeMaterial.addShortcuts( THREE.PhongNodeMaterial.prototype, 'node',
-	[ 'color', 'alpha', 'specular', 'shininess', 'normal', 'normalScale', 'emissive', 'ambient', 'light', 'shadow', 'ao', 'environment', 'environmentAlpha', 'transform' ] );
+THREE.NodeMaterial.addShortcuts( THREE.PhongNodeMaterial.prototype, 'fragment', [ 
+	'color', 
+	'alpha', 
+	'specular', 
+	'shininess', 
+	'normal', 
+	'emissive', 
+	'ambient', 
+	'light', 
+	'shadow', 
+	'ao', 
+	'environment', 
+	'environmentAlpha',
+	'transform' 
+] );

+ 20 - 1
examples/js/nodes/materials/SpriteNode.js

@@ -45,7 +45,7 @@ THREE.SpriteNode.prototype.build = function ( builder ) {
 
 			output.push(
 				transform.code,
-				"transformed = " + transform.result + ";"
+				transform.result ? "transformed = " + transform.result + ";" : ''
 			);
 
 		}
@@ -139,6 +139,24 @@ THREE.SpriteNode.prototype.build = function ( builder ) {
 
 };
 
+THREE.SpriteNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	// vertex
+	
+	if ( source.transform ) this.transform = source.transform;
+	
+	// fragment
+	
+	this.color = source.color;
+	
+	if ( source.spherical !== undefined ) this.spherical = source.transform;
+	
+	if ( source.alpha ) this.alpha = source.alpha;
+
+};
+
 THREE.SpriteNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -154,6 +172,7 @@ THREE.SpriteNode.prototype.toJSON = function ( meta ) {
 		// fragment
 
 		data.color = this.color.toJSON( meta ).uuid;
+		
 		if ( this.spherical === false ) data.spherical = false;
 
 		if ( this.alpha ) data.alpha = this.alpha.toJSON( meta ).uuid;

+ 8 - 4
examples/js/nodes/materials/SpriteNodeMaterial.js

@@ -4,9 +4,9 @@
 
 THREE.SpriteNodeMaterial = function () {
 
-	this.node = new THREE.SpriteNode();
+	var node = new THREE.SpriteNode();
 
-	THREE.NodeMaterial.call( this, this.node, this.node );
+	THREE.NodeMaterial.call( this, node, node );
 
 	this.type = "SpriteNodeMaterial";
 
@@ -15,5 +15,9 @@ THREE.SpriteNodeMaterial = function () {
 THREE.SpriteNodeMaterial.prototype = Object.create( THREE.NodeMaterial.prototype );
 THREE.SpriteNodeMaterial.prototype.constructor = THREE.SpriteNodeMaterial;
 
-THREE.NodeMaterial.addShortcuts( THREE.SpriteNodeMaterial.prototype, 'node',
-	[ 'color', 'alpha', 'transform', 'spherical' ] );
+THREE.NodeMaterial.addShortcuts( THREE.SpriteNodeMaterial.prototype, 'fragment', [ 
+	'color', 
+	'alpha', 
+	'transform', 
+	'spherical' 
+] );

+ 68 - 36
examples/js/nodes/materials/StandardNode.js

@@ -21,9 +21,7 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 	var material = builder.material;
 	var code;
 
-	material.define( 'PHYSICAL' );
-
-	if ( ! this.clearCoat && ! this.clearCoatRoughness ) material.define( 'STANDARD' );
+	material.define( this.clearCoat || this.clearCoatRoughness ? 'PHYSICAL' : 'STANDARD' );
 
 	material.define( 'ALPHATEST', '0.0' );
 
@@ -52,6 +50,7 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 			"#endif",
 
 			"#include <common>",
+			"#include <encodings_pars_fragment>", // encoding functions
 			"#include <fog_pars_vertex>",
 			"#include <morphtarget_pars_vertex>",
 			"#include <skinning_pars_vertex>",
@@ -83,7 +82,7 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 
 			output.push(
 				transform.code,
-				"transformed = " + transform.result + ";"
+				transform.result ? "transformed = " + transform.result + ";" : ''
 			);
 
 		}
@@ -107,17 +106,20 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 
 		// blur textures for PBR effect
 
-		var requires = {
-			bias: new THREE.RoughnessToBlinnExponentNode(),
-			offsetU: 0,
-			offsetV: 0
+		var requiresEnvironment = {
+			bias: THREE.RoughnessToBlinnExponentNode,
+			gamma: true
+		};
+
+		var requiresGamma = {
+			gamma: true
 		};
 
 		var useClearCoat = ! material.isDefined( 'STANDARD' );
 
 		// parse all nodes to reuse generate codes
 
-		this.color.parse( builder, { slot: 'color' } );
+		this.color.parse( builder, { slot: 'color', requires: requiresGamma } );
 		this.roughness.parse( builder );
 		this.metalness.parse( builder );
 
@@ -138,11 +140,11 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 		if ( this.shadow ) this.shadow.parse( builder );
 		if ( this.emissive ) this.emissive.parse( builder, { slot: 'emissive' } );
 
-		if ( this.environment ) this.environment.parse( builder, { cache: 'env', requires: requires, slot: 'environment' } ); // isolate environment from others inputs ( see TextureNode, CubeTextureNode )
+		if ( this.environment ) this.environment.parse( builder, { cache: 'env', requires: requiresEnvironment, slot: 'environment' } ); // isolate environment from others inputs ( see TextureNode, CubeTextureNode )
 
 		// build code
 
-		var color = this.color.buildCode( builder, 'c', { slot: 'color' } );
+		var color = this.color.buildCode( builder, 'c', { slot: 'color', requires: requiresGamma } );
 		var roughness = this.roughness.buildCode( builder, 'fv1' );
 		var metalness = this.metalness.buildCode( builder, 'fv1' );
 
@@ -163,9 +165,9 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 		var shadow = this.shadow ? this.shadow.buildCode( builder, 'c' ) : undefined;
 		var emissive = this.emissive ? this.emissive.buildCode( builder, 'c', { slot: 'emissive' } ) : undefined;
 
-		var environment = this.environment ? this.environment.buildCode( builder, 'c', { cache: 'env', requires: requires, slot: 'environment' } ) : undefined;
+		var environment = this.environment ? this.environment.buildCode( builder, 'c', { cache: 'env', requires: requiresEnvironment, slot: 'environment' } ) : undefined;
 
-		var clearCoatEnv = useClearCoat && environment ? this.environment.buildCode( builder, 'c', { cache: 'clearCoat', requires: requires, slot: 'environment' } ) : undefined;
+		var clearCoatEnv = useClearCoat && environment ? this.environment.buildCode( builder, 'c', { cache: 'clearCoat', requires: requiresEnvironment, slot: 'environment' } ) : undefined;
 
 		material.requires.transparent = alpha != undefined;
 
@@ -221,28 +223,18 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 
 		if ( normal ) {
 
-			builder.include( 'perturbNormal2Arb' );
-
-			output.push( normal.code );
-
-			if ( normalScale ) output.push( normalScale.code );
-
 			output.push(
-				'normal = perturbNormal2Arb(-vViewPosition,normal,' +
-				normal.result + ',' +
-				new THREE.UVNode().build( builder, 'v2' ) + ',' +
-				( normalScale ? normalScale.result : 'vec2( 1.0 )' ) + ');'
+				normal.code,
+				'normal = ' + normal.result + ';'
 			);
-
+		
 		}
 
 		// optimization for now
 
-		output.push( 'material.diffuseColor = ' + ( light ? 'vec3( 1.0 )' : 'diffuseColor * (1.0 - metalnessFactor)' ) + ';' );
-
 		output.push(
-			// accumulation
-			'material.specularRoughness = clamp( roughnessFactor, DEFAULT_SPECULAR_COEFFICIENT, 1.0 );' // disney's remapping of [ 0, 1 ] roughness to [ 0.001, 1 ]
+			'material.diffuseColor = ' + ( light ? 'vec3( 1.0 )' : 'diffuseColor * (1.0 - metalnessFactor)' ) + ';',
+			'material.specularRoughness = clamp( roughnessFactor, 0.04, 1.0 );'
 		);
 
 		if ( clearCoat ) {
@@ -262,7 +254,7 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 
 			output.push(
 				clearCoatRoughness.code,
-				'material.clearCoatRoughness = clamp( ' + clearCoatRoughness.result + ', DEFAULT_SPECULAR_COEFFICIENT, 1.0 );'
+				'material.clearCoatRoughness = clamp( ' + clearCoatRoughness.result + ', 0.04, 1.0 );'
 			);
 
 		} else if ( useClearCoat ) {
@@ -287,8 +279,7 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 		}
 
 		output.push(
-			"#include <lights_fragment_begin>",
-			"#include <lights_fragment_end>"
+			"#include <lights_fragment_begin>"
 		);
 
 		if ( light ) {
@@ -361,9 +352,13 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 
 			}
 
-			output.push( "RE_IndirectSpecular(" + environment.result + ", clearCoatRadiance, geometry, material, reflectedLight );" );
+			output.push( "radiance += " + environment.result + ";" );
 
 		}
+		
+		output.push(
+			"#include <lights_fragment_end>"
+		);
 
 		output.push( "vec3 outgoingLight = reflectedLight.directDiffuse + reflectedLight.indirectDiffuse + reflectedLight.directSpecular + reflectedLight.indirectSpecular;" );
 
@@ -381,7 +376,9 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 			"#include <premultiplied_alpha_fragment>",
 			"#include <tonemapping_fragment>",
 			"#include <encodings_fragment>",
-			"#include <fog_fragment>"
+			"#include <fog_fragment>",
+			"#include <premultiplied_alpha_fragment>",
+			"#include <dithering_fragment>"
 		);
 
 		code = output.join( "\n" );
@@ -392,6 +389,41 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 
 };
 
+THREE.StandardNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	// vertex
+
+	if ( source.transform ) this.transform = source.transform;
+
+	// fragment
+
+	this.color = source.color;
+	this.roughness = source.roughness;
+	this.metalness = source.metalness;
+
+	if ( source.alpha ) this.alpha = source.alpha;
+
+	if ( source.normal ) this.normal = source.normal;
+
+	if ( source.clearCoat ) this.clearCoat = source.clearCoat;
+	if ( source.clearCoatRoughness ) this.clearCoatRoughness = source.clearCoatRoughness;
+
+	if ( source.reflectivity ) this.reflectivity = source.reflectivity;
+
+	if ( source.light ) this.light = source.light;
+	if ( source.shadow ) this.shadow = source.shadow;
+
+	if ( source.ao ) this.ao = source.ao;
+	
+	if ( source.emissive ) this.emissive = source.emissive;
+	if ( source.ambient ) this.ambient = source.ambient;
+
+	if ( source.environment ) this.environment = source.environment;
+
+};
+
 THREE.StandardNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -413,7 +445,6 @@ THREE.StandardNode.prototype.toJSON = function ( meta ) {
 		if ( this.alpha ) data.alpha = this.alpha.toJSON( meta ).uuid;
 
 		if ( this.normal ) data.normal = this.normal.toJSON( meta ).uuid;
-		if ( this.normalScale ) data.normalScale = this.normalScale.toJSON( meta ).uuid;
 
 		if ( this.clearCoat ) data.clearCoat = this.clearCoat.toJSON( meta ).uuid;
 		if ( this.clearCoatRoughness ) data.clearCoatRoughness = this.clearCoatRoughness.toJSON( meta ).uuid;
@@ -421,11 +452,12 @@ THREE.StandardNode.prototype.toJSON = function ( meta ) {
 		if ( this.reflectivity ) data.reflectivity = this.reflectivity.toJSON( meta ).uuid;
 
 		if ( this.light ) data.light = this.light.toJSON( meta ).uuid;
+		if ( this.shadow ) data.shadow = this.shadow.toJSON( meta ).uuid;
 
 		if ( this.ao ) data.ao = this.ao.toJSON( meta ).uuid;
-		if ( this.ambient ) data.ambient = this.ambient.toJSON( meta ).uuid;
-		if ( this.shadow ) data.shadow = this.shadow.toJSON( meta ).uuid;
+		
 		if ( this.emissive ) data.emissive = this.emissive.toJSON( meta ).uuid;
+		if ( this.ambient ) data.ambient = this.ambient.toJSON( meta ).uuid;
 
 		if ( this.environment ) data.environment = this.environment.toJSON( meta ).uuid;
 

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

@@ -15,5 +15,20 @@ THREE.StandardNodeMaterial = function () {
 THREE.StandardNodeMaterial.prototype = Object.create( THREE.NodeMaterial.prototype );
 THREE.StandardNodeMaterial.prototype.constructor = THREE.StandardNodeMaterial;
 
-THREE.NodeMaterial.addShortcuts( THREE.StandardNodeMaterial.prototype, 'node',
-	[ 'color', 'alpha', 'roughness', 'metalness', 'reflectivity', 'clearCoat', 'clearCoatRoughness', 'normal', 'normalScale', 'emissive', 'ambient', 'light', 'shadow', 'ao', 'environment', 'transform' ] );
+THREE.NodeMaterial.addShortcuts( THREE.StandardNodeMaterial.prototype, 'node', [ 
+	'color', 
+	'alpha', 
+	'roughness', 
+	'metalness', 
+	'reflectivity', 
+	'clearCoat', 
+	'clearCoatRoughness', 
+	'normal', 
+	'emissive', 
+	'ambient', 
+	'light', 
+	'shadow', 
+	'ao', 
+	'environment', 
+	'transform' 
+] );

+ 12 - 3
examples/js/nodes/math/Math1Node.js

@@ -65,15 +65,15 @@ THREE.Math1Node.prototype.generate = function ( builder, output ) {
 	switch ( this.method ) {
 
 		case THREE.Math1Node.NEGATE:
-			result = '(-' + result + ')';
+			result = '( -' + result + ' )';
 			break;
 
 		case THREE.Math1Node.INVERT:
-			result = '(1.0-' + result + ')';
+			result = '( 1.0 - ' + result + ' )';
 			break;
 
 		default:
-			result = this.method + '(' + result + ')';
+			result = this.method + '( ' + result + ' )';
 			break;
 
 	}
@@ -82,6 +82,15 @@ THREE.Math1Node.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.Math1Node.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.a = source.a;
+	this.method = source.method;
+	
+};
+
 THREE.Math1Node.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 11 - 1
examples/js/nodes/math/Math2Node.js

@@ -95,10 +95,20 @@ THREE.Math2Node.prototype.generate = function ( builder, output ) {
 
 	}
 
-	return builder.format( this.method + '(' + a + ',' + b + ')', this.getType( builder ), output );
+	return builder.format( this.method + '( ' + a + ', ' + b + ' )', this.getType( builder ), output );
 
 };
 
+THREE.Math2Node.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.a = source.a;
+	this.b = source.b;
+	this.method = source.method;
+	
+};
+
 THREE.Math2Node.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -71,10 +71,21 @@ THREE.Math3Node.prototype.generate = function ( builder, output ) {
 
 	}
 
-	return builder.format( this.method + '(' + a + ',' + b + ',' + c + ')', type, output );
+	return builder.format( this.method + '( ' + a + ', ' + b + ', ' + c + ' )', type, output );
 
 };
 
+THREE.Math3Node.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.a = source.a;
+	this.b = source.b;
+	this.c = source.c;
+	this.method = source.method;
+	
+};
+
 THREE.Math3Node.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 11 - 1
examples/js/nodes/math/OperatorNode.js

@@ -52,10 +52,20 @@ THREE.OperatorNode.prototype.generate = function ( builder, output ) {
 	var a = this.a.build( builder, type );
 	var b = this.b.build( builder, type );
 
-	return builder.format( '(' + a + this.op + b + ')', type, output );
+	return builder.format( '( ' + a + ' ' +  this.op + ' '+ b + ' )', type, output );
 
 };
 
+THREE.OperatorNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.a = source.a;
+	this.b = source.b;
+	this.op = source.op;
+	
+};
+
 THREE.OperatorNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -44,6 +44,12 @@ THREE.NodePass.prototype.render = function () {
 
 };
 
+THREE.NodePass.prototype.copy = function ( source ) {
+	
+	this.value = source.value;
+	
+};
+
 THREE.NodePass.prototype.toJSON = function ( meta ) {
 
 	var isRootObject = ( meta === undefined || typeof meta === 'string' );

+ 45 - 0
examples/js/nodes/utils/BlinnExponentToRoughnessNode.js

@@ -0,0 +1,45 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.BlinnExponentToRoughnessNode = function ( blinnExponent ) {
+
+	THREE.TempNode.call( this, 'fv1' );
+
+	this.blinnExponent = blinnExponent || new THREE.BlinnShininessExponentNode();
+
+};
+
+THREE.BlinnExponentToRoughnessNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.BlinnExponentToRoughnessNode.prototype.constructor = THREE.BlinnExponentToRoughnessNode;
+THREE.BlinnExponentToRoughnessNode.prototype.nodeType = "BlinnExponentToRoughness";
+
+THREE.BlinnExponentToRoughnessNode.prototype.generate = function ( builder, output ) {
+
+	return builder.format( 'BlinnExponentToGGXRoughness( ' + this.blinnExponent.build( builder, 'fv1' ) + ' )', this.type, output );
+
+};
+
+THREE.BlinnExponentToRoughnessNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.blinnExponent = source.blinnExponent;
+	
+};
+
+THREE.BlinnExponentToRoughnessNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.blinnExponent = this.blinnExponent;
+
+	}
+
+	return data;
+
+};

+ 27 - 0
examples/js/nodes/utils/BlinnShininessExponentNode.js

@@ -0,0 +1,27 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.BlinnShininessExponentNode = function () {
+
+	THREE.TempNode.call( this, 'fv1' );
+
+};
+
+THREE.BlinnShininessExponentNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.BlinnShininessExponentNode.prototype.constructor = THREE.BlinnShininessExponentNode;
+THREE.BlinnShininessExponentNode.prototype.nodeType = "BlinnShininessExponent";
+
+THREE.BlinnShininessExponentNode.prototype.generate = function ( builder, output ) {
+
+	if ( builder.isCache( 'clearCoat' ) ) {
+
+		return builder.format( 'Material_ClearCoat_BlinnShininessExponent( material )', this.type, output );
+
+	} else {
+
+		return builder.format( 'Material_BlinnShininessExponent( material )', this.type, output );
+
+	}
+
+};

+ 20 - 4
examples/js/nodes/utils/BlurNode.js

@@ -9,6 +9,7 @@ THREE.BlurNode = function ( value, coord, radius, size ) {
 	this.value = value;
 	this.coord = coord || new THREE.UVNode();
 	this.radius = new THREE.Vector2Node( 1, 1 );
+
 	this.size = size;
 
 	this.blurX = true;
@@ -86,18 +87,18 @@ THREE.BlurNode.prototype.generate = function ( builder, output ) {
 
 		if ( this.blurX ) {
 
-			blurCode.push( blurX.name + '(' + this.value.build( builder, 'sampler2D' ) + ',' + this.coord.build( builder, 'v2' ) + ',' + this.horizontal.build( builder, 'fv1' ) + ')' );
+			blurCode.push( blurX.name + '( ' + this.value.build( builder, 'sampler2D' ) + ', ' + this.coord.build( builder, 'v2' ) + ', ' + this.horizontal.build( builder, 'fv1' ) + ' )' );
 
 		}
 
 		if ( this.blurY ) {
 
-			blurCode.push( blurY.name + '(' + this.value.build( builder, 'sampler2D' ) + ',' + this.coord.build( builder, 'v2' ) + ',' + this.vertical.build( builder, 'fv1' ) + ')' );
+			blurCode.push( blurY.name + '( ' + this.value.build( builder, 'sampler2D' ) + ', ' + this.coord.build( builder, 'v2' ) + ', ' + this.vertical.build( builder, 'fv1' ) + ' )' );
 
 		}
 
-		if ( blurCode.length == 2 ) code = '(' + blurCode.join( '+' ) + '/2.0)';
-		else if ( blurCode.length ) code = '(' + blurCode[ 0 ] + ')';
+		if ( blurCode.length == 2 ) code = '( ' + blurCode.join( ' + ' ) + '/ 2.0 )';
+		else if ( blurCode.length ) code = '( ' + blurCode[ 0 ] + ' )';
 		else code = 'vec4( 0.0 )';
 
 		return builder.format( code, this.getType( builder ), output );
@@ -112,6 +113,21 @@ THREE.BlurNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.BlurNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.value = source.value;
+	this.coord = source.coord;
+	this.radius = source.radius;
+
+	if ( source.size !== undefined ) this.size = new THREE.Vector2( source.size.x, source.size.y );
+
+	this.blurX = source.blurX;
+	this.blurY = source.blurY;
+					
+};
+
 THREE.BlurNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 95 - 0
examples/js/nodes/utils/BumpMapNode.js

@@ -0,0 +1,95 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.BumpMapNode = function ( value, scale ) {
+
+	THREE.TempNode.call( this, 'v3' );
+
+	this.value = value;
+	this.scale = scale || new THREE.FloatNode( 1 );
+
+	this.toNormalMap = false;
+	
+};
+
+THREE.BumpMapNode.fBumpToNormal = new THREE.FunctionNode( [
+	"vec3 bumpToNormal( sampler2D bumpMap, vec2 uv, float scale ) {",
+	"	vec2 dSTdx = dFdx( uv );",
+	"	vec2 dSTdy = dFdy( uv );",
+	"	float Hll = texture2D( bumpMap, uv ).x;",
+	"	float dBx = texture2D( bumpMap, uv + dSTdx ).x - Hll;",
+	"	float dBy = texture2D( bumpMap, uv + dSTdy ).x - Hll;",
+	"	return vec3( .5 - ( dBx * scale ), .5 - ( dBy * scale ), 1.0 );",
+	"}"
+].join( "\n" ), null, { derivatives: true } );
+
+THREE.BumpMapNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.BumpMapNode.prototype.constructor = THREE.BumpMapNode;
+THREE.BumpMapNode.prototype.nodeType = "BumpMap";
+
+THREE.BumpMapNode.prototype.generate = function ( builder, output ) {
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		if ( this.toNormalMap ) {
+	
+			builder.include( THREE.BumpMapNode.fBumpToNormal );
+		
+			return builder.format( THREE.BumpMapNode.fBumpToNormal.name + '( ' + this.value.build( builder, 'sampler2D' ) + ', ' +
+				this.value.coord.build( builder, 'v2' ) + ', ' +
+				this.scale.build( builder, 'fv1' ) + ' )', this.getType( builder ), output );
+				
+		} else {
+			
+			builder.include( 'dHdxy_fwd' );
+			builder.include( 'perturbNormalArb' );
+		
+			this.normal = this.normal || new THREE.NormalNode( THREE.NormalNode.VIEW );
+			this.position = this.position || new THREE.PositionNode( THREE.NormalNode.VIEW );
+		
+			var derivativeOfHeightCode = 'dHdxy_fwd( ' + this.value.build( builder, 'sampler2D' ) + ', ' +
+				this.value.coord.build( builder, 'v2' ) + ', ' +
+				this.scale.build( builder, 'fv1' ) + ' )';
+
+			return builder.format( 'perturbNormalArb( -' + this.position.build( builder, 'v3' ) + ', ' +
+				this.normal.build( builder, 'v3' ) + ', ' +
+				derivativeOfHeightCode + ' )', this.getType( builder ), output );
+			
+		}
+
+	} else {
+
+		console.warn( "THREE.BumpMapNode is not compatible with " + builder.shader + " shader." );
+
+		return builder.format( 'vec3( 0.0 )', this.getType( builder ), output );
+
+	}
+
+};
+
+THREE.BumpMapNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.value = source.value;
+	this.scale = source.scale;
+					
+};
+
+THREE.BumpMapNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.value = this.value.toJSON( meta ).uuid;
+		data.scale = this.scale.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

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

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

+ 10 - 0
examples/js/nodes/utils/ColorAdjustmentNode.js

@@ -70,6 +70,16 @@ THREE.ColorAdjustmentNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.ColorAdjustmentNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.rgb = source.rgb;
+	this.adjustment = source.adjustment;
+	this.method = source.method;
+					
+};
+
 THREE.ColorAdjustmentNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -69,6 +69,18 @@ THREE.JoinNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.JoinNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	for ( var prop in source.inputs ) {
+
+		this[ prop ] = source.inputs[ prop ];
+
+	}
+	
+};
+
 THREE.JoinNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 8 - 0
examples/js/nodes/utils/LuminanceNode.js

@@ -22,6 +22,14 @@ THREE.LuminanceNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.LuminanceNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.rgb = source.rgb;
+	
+};
+
 THREE.LuminanceNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 53 - 0
examples/js/nodes/utils/MaxMIPLevelNode.js

@@ -0,0 +1,53 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.MaxMIPLevelNode = function ( texture ) {
+
+	THREE.FloatNode.call( this );
+
+	this.texture = texture;
+
+	this.maxMIPLevel = 0;
+
+};
+
+THREE.MaxMIPLevelNode.prototype = Object.create( THREE.FloatNode.prototype );
+THREE.MaxMIPLevelNode.prototype.constructor = THREE.MaxMIPLevelNode;
+THREE.MaxMIPLevelNode.prototype.nodeType = "MaxMIPLevel";
+
+Object.defineProperties( THREE.MaxMIPLevelNode.prototype, {
+
+	value: {
+		get: function () {
+
+			if ( this.maxMIPLevel === 0 ) {
+
+				var image = this.texture.value.image ? this.texture.value.image[0] : undefined;
+
+				this.maxMIPLevel = image !== undefined ? ( Math.log( Math.max( image.width, image.height ) ) + 1 ) * Math.LOG2E : 0;
+
+			}
+
+			return this.maxMIPLevel;
+
+		}
+	}
+
+} );
+
+THREE.MaxMIPLevelNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.texture = this.texture.uuid;
+
+	}
+
+	return data;
+
+};

+ 8 - 0
examples/js/nodes/utils/NoiseNode.js

@@ -22,6 +22,14 @@ THREE.NoiseNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.NoiseNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.coord = source.coord;
+	
+};
+
 THREE.NoiseNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 23 - 16
examples/js/nodes/utils/NormalMapNode.js

@@ -2,15 +2,12 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.NormalMapNode = function ( value, uv, scale, normal, position ) {
+THREE.NormalMapNode = function ( value, scale ) {
 
 	THREE.TempNode.call( this, 'v3' );
 
 	this.value = value;
-	this.scale = scale || new THREE.FloatNode( 1 );
-
-	this.normal = normal || new THREE.NormalNode( THREE.NormalNode.LOCAL );
-	this.position = position || new THREE.PositionNode( THREE.NormalNode.VIEW );
+	this.scale = scale || new THREE.Vector2Node( 1, 1 );
 
 };
 
@@ -22,15 +19,19 @@ THREE.NormalMapNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
-	builder.include( 'perturbNormal2Arb' );
-
 	if ( builder.isShader( 'fragment' ) ) {
 
-		return builder.format( 'perturbNormal2Arb(-' + this.position.build( builder, 'v3' ) + ',' +
-			this.normal.build( builder, 'v3' ) + ',' +
-			this.value.build( builder, 'v3' ) + ',' +
-			this.value.coord.build( builder, 'v2' ) + ',' +
-			this.scale.build( builder, 'v2' ) + ')', this.getType( builder ), output );
+		builder.include( 'perturbNormal2Arb' );
+
+		this.normal = this.normal || new THREE.NormalNode( THREE.NormalNode.VIEW );
+		this.position = this.position || new THREE.PositionNode( THREE.NormalNode.VIEW );
+		this.uv = this.uv || new THREE.UVNode();
+
+		return builder.format( 'perturbNormal2Arb( -' + this.position.build( builder, 'v3' ) + ', ' +
+			this.normal.build( builder, 'v3' ) + ', ' +
+			this.value.build( builder, 'v3' ) + ', ' +
+			this.uv.build( builder, 'v2' ) + ', ' +
+			this.scale.build( builder, 'v2' ) + ' )', this.getType( builder ), output );
 
 	} else {
 
@@ -42,6 +43,15 @@ THREE.NormalMapNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.NormalMapNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.value = source.value;
+	this.scale = source.scale;
+	
+};
+
 THREE.NormalMapNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -50,12 +60,9 @@ THREE.NormalMapNode.prototype.toJSON = function ( meta ) {
 
 		data = this.createJSONNode( meta );
 
-		data.value = this.value.uuid;
+		data.value = this.value.toJSON( meta ).uuid;
 		data.scale = this.scale.toJSON( meta ).uuid;
 
-		data.normal = this.normal.toJSON( meta ).uuid;
-		data.position = this.position.toJSON( meta ).uuid;
-
 	}
 
 	return data;

+ 8 - 0
examples/js/nodes/utils/ResolutionNode.js

@@ -24,6 +24,14 @@ THREE.ResolutionNode.prototype.updateFrame = function ( frame ) {
 
 };
 
+THREE.ResolutionNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.renderer = source.renderer;
+	
+};
+
 THREE.ResolutionNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 33 - 19
examples/js/nodes/utils/RoughnessToBlinnExponentNode.js

@@ -2,19 +2,24 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.RoughnessToBlinnExponentNode = function () {
+THREE.RoughnessToBlinnExponentNode = function ( texture ) {
 
 	THREE.TempNode.call( this, 'fv1' );
 
+	this.texture = texture;
+
+	this.maxMIPLevel = new THREE.MaxMIPLevelNode( texture );
+	this.blinnShininessExponent = new THREE.BlinnShininessExponentNode();
+
 };
 
 THREE.RoughnessToBlinnExponentNode.getSpecularMIPLevel = new THREE.FunctionNode( [
-// taken from here: http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html
-	"float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {",
+	// taken from here: http://casual-effects.blogspot.ca/2011/08/plausible-environment-lighting-in-two.html
+	"float getSpecularMIPLevel( const in float blinnShininessExponent, const in float maxMIPLevelScalar ) {",
 
 	//	float envMapWidth = pow( 2.0, maxMIPLevelScalar );
 	//	float desiredMIPLevel = log2( envMapWidth * sqrt( 3.0 ) ) - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );
-	"	float maxMIPLevelScalar = float( maxMIPLevel );",
+
 	"	float desiredMIPLevel = maxMIPLevelScalar - 0.79248 - 0.5 * log2( pow2( blinnShininessExponent ) + 1.0 );",
 
 	// clamp to allowable LOD ranges.
@@ -32,34 +37,43 @@ THREE.RoughnessToBlinnExponentNode.prototype.generate = function ( builder, outp
 
 	if ( builder.isShader( 'fragment' ) ) {
 
-		if ( material.isDefined( 'PHYSICAL' ) ) {
-
-			builder.include( THREE.RoughnessToBlinnExponentNode.getSpecularMIPLevel );
+		builder.include( THREE.RoughnessToBlinnExponentNode.getSpecularMIPLevel );
 
-			if ( builder.isCache( 'clearCoat' ) ) {
+		this.maxMIPLevel.texture = this.texture;
+		
+		return builder.format( 'getSpecularMIPLevel( ' + this.blinnShininessExponent.build( builder, 'fv1' ) + ', ' + this.maxMIPLevel.build( builder, 'fv1' ) + ' )', this.type, output );
 
-				return builder.format( 'getSpecularMIPLevel( Material_ClearCoat_BlinnShininessExponent( material ), 8 )', this.type, output );
+	} else {
 
-			} else {
+		console.warn( "THREE.RoughnessToBlinnExponentNode is not compatible with " + builder.shader + " shader." );
 
-				return builder.format( 'getSpecularMIPLevel( Material_BlinnShininessExponent( material ), 8 )', this.type, output );
+		return builder.format( '0.0', this.type, output );
 
-			}
+	}
 
-		} else {
+};
 
-			console.warn( "THREE.RoughnessToBlinnExponentNode is only compatible with PhysicalMaterial." );
+THREE.RoughnessToBlinnExponentNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.texture = source.texture;
+	
+};
 
-			return builder.format( '0.0', this.type, output );
+THREE.RoughnessToBlinnExponentNode.prototype.toJSON = function ( meta ) {
 
-		}
+	var data = this.getJSONNode( meta );
 
-	} else {
+	if ( ! data ) {
 
-		console.warn( "THREE.RoughnessToBlinnExponentNode is not compatible with " + builder.shader + " shader." );
+		data = this.createJSONNode( meta );
 
-		return builder.format( '0.0', this.type, output );
+		data.texture = this.texture;
 
 	}
 
+	return data;
+
 };
+

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

@@ -72,6 +72,15 @@ THREE.SwitchNode.prototype.generate = function ( builder, output ) {
 
 };
 
+THREE.SwitchNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.node = source.node;
+	this.components = source.components;
+	
+};
+
 THREE.SwitchNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 61 - 0
examples/js/nodes/utils/TextureCubeNode.js

@@ -0,0 +1,61 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.TextureCubeNode = function ( value, coord ) {
+
+	THREE.TempNode.call( this, 'v4' );
+
+	this.value = value;
+	this.coord = coord || new THREE.TextureCubeUVNode();
+
+};
+
+THREE.TextureCubeNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.TextureCubeNode.prototype.constructor = THREE.TextureCubeNode;
+THREE.TextureCubeNode.prototype.nodeType = "TextureCube";
+
+THREE.TextureCubeNode.prototype.generate = function ( builder, output ) {
+
+	var material = builder.material;
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		var uv_10 = this.coord.build( builder ) + '.uv_10';
+		var uv_20 = this.coord.build( builder ) + '.uv_20';
+		var t = this.coord.build( builder ) + '.t';
+		
+		var color10 = builder.getTexelDecodingFunctionFromTexture( 'texture2D( ' + this.value.build( builder, 'sampler2D' ) + ', ' + uv_10 + ' )', this.value.value );
+		var color20 = builder.getTexelDecodingFunctionFromTexture( 'texture2D( ' + this.value.build( builder, 'sampler2D' ) + ', ' + uv_20 + ' )', this.value.value );
+
+		return builder.format( 'vec4( mix( ' + color10 + ', ' + color20 + ', ' + t + ' ).rgb, 1.0 )', this.getType( builder ), output );
+			
+	} else {
+
+		console.warn( "THREE.TextureCubeNode is not compatible with " + builder.shader + " shader." );
+
+		return builder.format( 'vec4( 0.0 )', this.getType( builder ), output );
+
+	}
+
+};
+
+THREE.TextureCubeNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.coord = this.coord.toJSON( meta ).uuid;
+		data.textureSize = this.textureSize.toJSON( meta ).uuid;
+		data.blinnExponentToRoughness = this.blinnExponentToRoughness.toJSON( meta ).uuid;
+
+		if ( this.roughness ) data.roughness = this.roughness.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 192 - 0
examples/js/nodes/utils/TextureCubeUVNode.js

@@ -0,0 +1,192 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.TextureCubeUVNode = function ( coord, textureSize, blinnExponentToRoughness ) {
+
+	THREE.TempNode.call( this, THREE.TextureCubeUVNode.fTextureCubeUVOutput.type );
+
+	this.coord = coord || new THREE.ReflectNode( THREE.ReflectNode.VECTOR );
+	this.textureSize = textureSize || new THREE.FloatNode( 1024 );
+	this.blinnExponentToRoughness = this.blinnExponentToRoughness || new THREE.BlinnExponentToRoughnessNode();
+
+};
+
+THREE.TextureCubeUVNode.fTextureCubeUVOutput = new THREE.StructNode([
+"struct TextureCubeUVData {",
+"	vec2 uv_10;",
+"	vec2 uv_20;",
+"	float t;",
+"}"
+].join( "\n" ));
+
+THREE.TextureCubeUVNode.fTextureCubeUV = ( function () {
+
+	var getFaceFromDirection = new THREE.FunctionNode( [
+		"int getFaceFromDirection(vec3 direction) {",
+		"	vec3 absDirection = abs(direction);",
+		"	int face = -1;",
+		"	if( absDirection.x > absDirection.z ) {",
+		"		if(absDirection.x > absDirection.y )",
+		"			face = direction.x > 0.0 ? 0 : 3;",
+		"		else",
+		"			face = direction.y > 0.0 ? 1 : 4;",
+		"	}",
+		"	else {",
+		"		if(absDirection.z > absDirection.y )",
+		"			face = direction.z > 0.0 ? 2 : 5;",
+		"		else",
+		"			face = direction.y > 0.0 ? 1 : 4;",
+		"	}",
+		"	return face;",
+		"}" ].join( "\n" ) );
+
+	var cubeUV_maxLods1 = new THREE.ConstNode( "#define cubeUV_maxLods1 ( log2( cubeUV_textureSize * 0.25 ) - 1.0 )" );
+	var cubeUV_rangeClamp = new THREE.ConstNode( "#define cubeUV_rangeClamp ( exp2( ( 6.0 - 1.0 ) * 2.0 ) )" );
+
+	var MipLevelInfo = new THREE.FunctionNode( [
+		"vec2 MipLevelInfo( vec3 vec, float roughnessLevel, float roughness, in float cubeUV_textureSize ) {",
+		"	float scale = exp2(cubeUV_maxLods1 - roughnessLevel);",
+		"	float dxRoughness = dFdx(roughness);",
+		"	float dyRoughness = dFdy(roughness);",
+		"	vec3 dx = dFdx( vec * scale * dxRoughness );",
+		"	vec3 dy = dFdy( vec * scale * dyRoughness );",
+		"	float d = max( dot( dx, dx ), dot( dy, dy ) );",
+			// Clamp the value to the max mip level counts. hard coded to 6 mips"
+		"	d = clamp(d, 1.0, cubeUV_rangeClamp);",
+		"	float mipLevel = 0.5 * log2(d);",
+		"	return vec2(floor(mipLevel), fract(mipLevel));",
+		"}" ].join( "\n" ), [ cubeUV_maxLods1, cubeUV_rangeClamp ], { derivatives: true } );
+
+	var cubeUV_maxLods2 = new THREE.ConstNode( "#define cubeUV_maxLods2 ( log2( cubeUV_textureSize * 0.25 ) - 2.0 )" );
+	var cubeUV_rcpTextureSize = new THREE.ConstNode( "#define cubeUV_rcpTextureSize ( 1.0 / cubeUV_textureSize )" );
+
+	var getCubeUV = new THREE.FunctionNode( [
+		"vec2 getCubeUV( vec3 direction, float roughnessLevel, float mipLevel, in float cubeUV_textureSize ) {",
+		"	mipLevel = roughnessLevel > cubeUV_maxLods2 - 3.0 ? 0.0 : mipLevel;",
+		"	float a = 16.0 * cubeUV_rcpTextureSize;",
+		"",
+		"	vec2 exp2_packed = exp2( vec2( roughnessLevel, mipLevel ) );",
+		"	vec2 rcp_exp2_packed = vec2( 1.0 ) / exp2_packed;",
+			// float powScale = exp2(roughnessLevel + mipLevel);"
+		"	float powScale = exp2_packed.x * exp2_packed.y;",
+			// float scale =  1.0 / exp2(roughnessLevel + 2.0 + mipLevel);"
+		"	float scale = rcp_exp2_packed.x * rcp_exp2_packed.y * 0.25;",
+			// float mipOffset = 0.75*(1.0 - 1.0/exp2(mipLevel))/exp2(roughnessLevel);"
+		"	float mipOffset = 0.75*(1.0 - rcp_exp2_packed.y) * rcp_exp2_packed.x;",
+		"",
+		"	bool bRes = mipLevel == 0.0;",
+		"	scale =  bRes && (scale < a) ? a : scale;",
+		"",
+		"	vec3 r;",
+		"	vec2 offset;",
+		"	int face = getFaceFromDirection(direction);",
+		"",
+		"	float rcpPowScale = 1.0 / powScale;",
+		"",
+		"	if( face == 0) {",
+		"		r = vec3(direction.x, -direction.z, direction.y);",
+		"		offset = vec2(0.0+mipOffset,0.75 * rcpPowScale);",
+		"		offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;",
+		"	}",
+		"	else if( face == 1) {",
+		"		r = vec3(direction.y, direction.x, direction.z);",
+		"		offset = vec2(scale+mipOffset, 0.75 * rcpPowScale);",
+		"		offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;",
+		"	}",
+		"	else if( face == 2) {",
+		"		r = vec3(direction.z, direction.x, direction.y);",
+		"		offset = vec2(2.0*scale+mipOffset, 0.75 * rcpPowScale);",
+		"		offset.y = bRes && (offset.y < 2.0*a) ? a : offset.y;",
+		"	}",
+		"	else if( face == 3) {",
+		"		r = vec3(direction.x, direction.z, direction.y);",
+		"		offset = vec2(0.0+mipOffset,0.5 * rcpPowScale);",
+		"		offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;",
+		"	}",
+		"	else if( face == 4) {",
+		"		r = vec3(direction.y, direction.x, -direction.z);",
+		"		offset = vec2(scale+mipOffset, 0.5 * rcpPowScale);",
+		"		offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;",
+		"	}",
+		"	else {",
+		"		r = vec3(direction.z, -direction.x, direction.y);",
+		"		offset = vec2(2.0*scale+mipOffset, 0.5 * rcpPowScale);",
+		"		offset.y = bRes && (offset.y < 2.0*a) ? 0.0 : offset.y;",
+		"	}",
+		"	r = normalize(r);",
+		"	float texelOffset = 0.5 * cubeUV_rcpTextureSize;",
+		"	vec2 s = ( r.yz / abs( r.x ) + vec2( 1.0 ) ) * 0.5;",
+		"	vec2 base = offset + vec2( texelOffset );",
+		"	return base + s * ( scale - 2.0 * texelOffset );",
+		"}" ].join( "\n" ), [ cubeUV_maxLods2, cubeUV_rcpTextureSize, getFaceFromDirection ] );
+
+	var cubeUV_maxLods3 = new THREE.ConstNode( "#define cubeUV_maxLods3 ( log2( cubeUV_textureSize * 0.25 ) - 3.0 )" );
+
+	return new THREE.FunctionNode( [
+		"TextureCubeUVData textureCubeUV( vec3 reflectedDirection, float roughness, in float cubeUV_textureSize ) {",
+		"	float roughnessVal = roughness * cubeUV_maxLods3;",
+		"	float r1 = floor(roughnessVal);",
+		"	float r2 = r1 + 1.0;",
+		"	float t = fract(roughnessVal);",
+		"	vec2 mipInfo = MipLevelInfo(reflectedDirection, r1, roughness, cubeUV_textureSize);",
+		"	float s = mipInfo.y;",
+		"	float level0 = mipInfo.x;",
+		"	float level1 = level0 + 1.0;",
+		"	level1 = level1 > 5.0 ? 5.0 : level1;",
+		"",
+			// round to nearest mipmap if we are not interpolating."
+		"	level0 += min( floor( s + 0.5 ), 5.0 );",
+		"",
+			// Tri linear interpolation."
+		"	vec2 uv_10 = getCubeUV(reflectedDirection, r1, level0, cubeUV_textureSize);",
+		"	vec2 uv_20 = getCubeUV(reflectedDirection, r2, level0, cubeUV_textureSize);",
+		"",
+		"	return TextureCubeUVData(uv_10, uv_20, t);",
+		"}" ].join( "\n" ), [ THREE.TextureCubeUVNode.fTextureCubeUVOutput, cubeUV_maxLods3, MipLevelInfo, getCubeUV ] );
+
+} )();
+
+THREE.TextureCubeUVNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.TextureCubeUVNode.prototype.constructor = THREE.TextureCubeUVNode;
+THREE.TextureCubeUVNode.prototype.nodeType = "TextureCubeUV";
+
+THREE.TextureCubeUVNode.prototype.generate = function ( builder, output ) {
+
+	var material = builder.material, func = THREE.TextureCubeUVNode.fTextureCubeUV;
+
+	builder.include( func );
+
+	if ( builder.isShader( 'fragment' ) ) {
+
+		return builder.format( func.name + '( ' + this.coord.build( builder, 'v3' ) + ', ' +
+			this.blinnExponentToRoughness.build( builder, 'fv1' ) + ', ' +
+			this.textureSize.build( builder, 'fv1' ) + ' )', this.getType( builder ), output );
+			
+	} else {
+
+		console.warn( "THREE.TextureCubeUVNode is not compatible with " + builder.shader + " shader." );
+
+		return builder.format( 'vec4( 0.0 )', this.getType( builder ), output );
+
+	}
+
+};
+
+THREE.TextureCubeUVNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.coord = this.coord.toJSON( meta ).uuid;
+		data.textureSize = this.textureSize.toJSON( meta ).uuid;
+		data.blinnExponentToRoughness = this.blinnExponentToRoughness.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 15 - 3
examples/js/nodes/utils/TimerNode.js

@@ -2,14 +2,14 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.TimerNode = function ( scale, scope ) {
+THREE.TimerNode = function ( scale, scope, useTimeScale ) {
 
 	THREE.FloatNode.call( this );
 
 	this.scale = scale !== undefined ? scale : 1;
 	this.scope = scope || THREE.TimerNode.GLOBAL;
 
-	this.timeScale = this.scale !== 1;
+	this.useTimeScale = useTimeScale !== undefined ? useTimeScale : this.scale !== 1;
 
 };
 
@@ -60,6 +60,17 @@ THREE.TimerNode.prototype.updateFrame = function ( frame ) {
 
 };
 
+THREE.TimerNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.scope = source.scope;
+	this.scale = source.scale;
+	
+	this.useTimeScale = source.useTimeScale;
+	
+};
+
 THREE.TimerNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );
@@ -70,7 +81,8 @@ THREE.TimerNode.prototype.toJSON = function ( meta ) {
 
 		data.scope = this.scope;
 		data.scale = this.scale;
-		data.timeScale = this.timeScale;
+		
+		data.useTimeScale = this.useTimeScale;
 
 	}
 

+ 12 - 3
examples/js/nodes/utils/UVTransformNode.js

@@ -2,12 +2,12 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.UVTransformNode = function () {
+THREE.UVTransformNode = function ( uv, transform ) {
 
 	THREE.FunctionNode.call( this, "( uvTransform * vec3( uvNode, 1 ) ).xy", "vec2" );
 
-	this.uv = new THREE.UVNode();
-	this.transform = new THREE.Matrix3Node();
+	this.uv = uv || new THREE.UVNode();
+	this.transform = transform || new THREE.Matrix3Node();
 
 };
 
@@ -33,6 +33,15 @@ THREE.UVTransformNode.prototype.setUvTransform = function ( tx, ty, sx, sy, rota
 
 };
 
+THREE.UVTransformNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	this.uv = source.uv;
+	this.transform = source.transform;
+					
+};
+
 THREE.UVTransformNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

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

@@ -140,6 +140,16 @@ THREE.VelocityNode.prototype.updateFrame = function ( frame ) {
 
 };
 
+THREE.VelocityNode.prototype.copy = function ( source ) {
+			
+	THREE.GLNode.prototype.copy.call( this, source );
+	
+	if ( source.target ) object.setTarget( source.target );
+	
+	object.setParams( source.params );
+	
+};
+
 THREE.VelocityNode.prototype.toJSON = function ( meta ) {
 
 	var data = this.getJSONNode( meta );

+ 200 - 67
examples/webgl_materials_nodes.html

@@ -41,21 +41,27 @@
 		<script src="js/libs/dat.gui.min.js"></script>
 
 		<!-- NodeLibrary -->
-		<script src="js/nodes/GLNode.js"></script>
-		<script src="js/nodes/RawNode.js"></script>
-		<script src="js/nodes/TempNode.js"></script>
-		<script src="js/nodes/InputNode.js"></script>
-		<script src="js/nodes/ConstNode.js"></script>
-		<script src="js/nodes/VarNode.js"></script>
-		<script src="js/nodes/FunctionNode.js"></script>
-		<script src="js/nodes/FunctionCallNode.js"></script>
-		<script src="js/nodes/AttributeNode.js"></script>
-		<script src="js/nodes/NodeUniform.js"></script>
-		<script src="js/nodes/NodeBuilder.js"></script>
-		<script src="js/nodes/NodeLib.js"></script>
-		<script src="js/nodes/NodeFrame.js"></script>
-		<script src="js/nodes/NodeMaterial.js"></script>
-
+		<script src="js/nodes/core/GLNode.js"></script>
+		<script src="js/nodes/core/RawNode.js"></script>
+		<script src="js/nodes/core/BypassNode.js"></script>
+		<script src="js/nodes/core/TempNode.js"></script>
+		<script src="js/nodes/core/InputNode.js"></script>
+		<script src="js/nodes/core/ConstNode.js"></script>
+		<script src="js/nodes/core/VarNode.js"></script>
+		<script src="js/nodes/core/StructNode.js"></script>
+		<script src="js/nodes/core/FunctionNode.js"></script>
+		<script src="js/nodes/core/FunctionCallNode.js"></script>
+		<script src="js/nodes/core/AttributeNode.js"></script>
+		<script src="js/nodes/core/NodeUniform.js"></script>
+		<script src="js/nodes/core/NodeBuilder.js"></script>
+		<script src="js/nodes/core/NodeLib.js"></script>
+		<script src="js/nodes/core/NodeFrame.js"></script>
+		<script src="js/nodes/core/NodeMaterial.js"></script>
+
+		<!-- Library -->
+		<script src="js/nodes/libs/common.js"></script>
+		<script src="js/nodes/libs/keywords.js"></script>
+		
 		<!-- Accessors -->
 		<script src="js/nodes/accessors/PositionNode.js"></script>
 		<script src="js/nodes/accessors/NormalNode.js"></script>
@@ -77,7 +83,7 @@
 		<script src="js/nodes/inputs/Matrix3Node.js"></script>
 		<script src="js/nodes/inputs/Matrix4Node.js"></script>
 		<script src="js/nodes/inputs/CubeTextureNode.js"></script>
-
+		
 		<!-- Math -->
 		<script src="js/nodes/math/Math1Node.js"></script>
 		<script src="js/nodes/math/Math2Node.js"></script>
@@ -89,14 +95,17 @@
 		<script src="js/nodes/utils/JoinNode.js"></script>
 		<script src="js/nodes/utils/TimerNode.js"></script>
 		<script src="js/nodes/utils/RoughnessToBlinnExponentNode.js"></script>
+		<script src="js/nodes/utils/BlinnShininessExponentNode.js"></script>
 		<script src="js/nodes/utils/VelocityNode.js"></script>
 		<script src="js/nodes/utils/LuminanceNode.js"></script>
 		<script src="js/nodes/utils/ColorAdjustmentNode.js"></script>
 		<script src="js/nodes/utils/NoiseNode.js"></script>
 		<script src="js/nodes/utils/ResolutionNode.js"></script>
-		<script src="js/nodes/utils/BumpNode.js"></script>
+		<script src="js/nodes/utils/BumpMapNode.js"></script>
 		<script src="js/nodes/utils/BlurNode.js"></script>
 		<script src="js/nodes/utils/UVTransformNode.js"></script>
+		<script src="js/nodes/utils/MaxMIPLevelNode.js"></script>
+		<script src="js/nodes/utils/NormalMapNode.js"></script>
 
 		<!-- Phong Material -->
 		<script src="js/nodes/materials/PhongNode.js"></script>
@@ -106,6 +115,10 @@
 		<script src="js/nodes/materials/StandardNode.js"></script>
 		<script src="js/nodes/materials/StandardNodeMaterial.js"></script>
 
+		<!-- Extended Materials -->
+		<script src="js/nodes/materials/MeshStandardNode.js"></script>
+		<script src="js/nodes/materials/MeshStandardNodeMaterial.js"></script>
+		
 		<!-- NodeMaterial Loader -->
 		<script src="js/loaders/NodeMaterialLoader.js"></script>
 
@@ -131,7 +144,7 @@
 			spherical: { url: 'textures/envmap.png' }
 		};
 
-		var param = { example: 'standard' };
+		var param = { example: 'mesh-standard' };
 
 		function getTexture( name ) {
 
@@ -228,6 +241,7 @@
 
 			var example = gui.add( param, 'example', {
 				'basic / standard': 'standard',
+				'basic / mesh-standard': 'mesh-standard',
 				'basic / physical': 'physical',
 				'basic / phong': 'phong',
 				'basic / layers': 'layers',
@@ -257,6 +271,7 @@
 				'misc / firefly': 'firefly',
 				'misc / reserved-keywords': 'reserved-keywords',
 				'misc / varying': 'varying',
+				'misc / void-function': 'void-function',
 				'misc / readonly': 'readonly',
 				'misc / custom-attribute': 'custom-attribute'
 			} ).onFinishChange( function () {
@@ -315,7 +330,7 @@
 
 			if ( rtTexture ) {
 
-				delete library[ rtTexture.uuid ];
+				delete library[ rtTexture.texture.uuid ];
 
 				rtTexture.dispose();
 				rtTexture = null;
@@ -346,8 +361,7 @@
 					//mtl.alpha = // opacity (float)
 					//mtl.specular = // specular color (vec3)
 					//mtl.shininess = // shininess (float)
-					//mtl.normal = // normalmap (vec3)
-					//mtl.normalScale = // normalmap scale (vec2)
+					//mtl.normal = // normal (vec3)
 					//mtl.emissive = // emissive color (vec3)
 					//mtl.ambient = // ambient color (vec3)
 					//mtl.shadow = // shadowmap (vec3)
@@ -365,8 +379,8 @@
 					mtl.shininess = new THREE.FloatNode( 15 );
 					mtl.environment = new THREE.CubeTextureNode( cubemap );
 					mtl.environmentAlpha = mask;
-					mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
-					mtl.normalScale = new THREE.Math1Node( mask, THREE.Math1Node.INVERT );
+					mtl.normal = new THREE.NormalMapNode( new THREE.TextureNode( getTexture( "grassNormal" ) ) );
+					mtl.normal.scale = new THREE.Math1Node( mask, THREE.Math1Node.INVERT );
 
 					break;
 
@@ -380,8 +394,7 @@
 					//mtl.alpha = // opacity (float)
 					//mtl.roughness = // roughness (float)
 					//mtl.metalness = // metalness (float)
-					//mtl.normal = // normalmap (vec3)
-					//mtl.normalScale = // normalmap scale (vec2)
+					//mtl.normal = // normal (vec3)
 					//mtl.emissive = // emissive color (vec3)
 					//mtl.ambient = // ambient color (vec3)
 					//mtl.shadow = // shadowmap (vec3)
@@ -424,8 +437,8 @@
 					mtl.roughness = roughness;
 					mtl.metalness = metalness;
 					mtl.environment = new THREE.CubeTextureNode( cubemap );
-					mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
-					mtl.normalScale = normalMask;
+					mtl.normal = new THREE.NormalMapNode( new THREE.TextureNode( getTexture( "grassNormal" ) ) );
+					mtl.normal.scale = normalMask;
 
 					// GUI
 
@@ -467,6 +480,85 @@
 
 					break;
 
+				case 'mesh-standard':
+
+					// MATERIAL
+					
+					var useNodeMaterial = true;
+					
+					var sataturation = new THREE.FloatNode( 1 );
+					
+					function updateMaterial() {
+					
+						var oldMaterial = mtl;
+					
+						mtl = useNodeMaterial ? new THREE.MeshStandardNodeMaterial() : new THREE.MeshStandardMaterial();
+					
+						// default syntax ( backward-compatible )
+					
+						mtl.map = getTexture( "brick" );
+						
+						mtl.normalMap = getTexture( "grassNormal" );
+						
+						mtl.envMap = cubemap;
+						
+						mtl.roughness = oldMaterial ? oldMaterial.roughness : .5;
+						mtl.metalness = oldMaterial ? oldMaterial.metalness : .5;
+						
+						// extended syntax ( only for NodeMaterial )
+						
+						if (useNodeMaterial) {
+						
+							mtl.map = new THREE.ColorAdjustmentNode( 
+								new THREE.TextureNode( mtl.map ), 
+								sataturation, 
+								THREE.ColorAdjustmentNode.SATURATION 
+							);
+							
+						}
+						
+						// apply material
+					
+						mtl.needsUpdate = true;
+					
+						mesh.material = mtl;
+
+					}
+
+					updateMaterial();
+					
+					// GUI
+					
+					addGui( 'use node material', true, function ( val ) {
+
+						useNodeMaterial = !useNodeMaterial;
+					
+						updateMaterial();
+
+					} );
+					
+					addGui( 'roughness', mtl.roughness, function ( val ) {
+
+						mtl.roughness = val;
+
+					}, false, 0, 1 );
+
+					addGui( 'metalness', mtl.roughness, function ( val ) {
+
+						mtl.metalness = val;
+
+					}, false, 0, 1 );
+					
+					addGui( 'sat. (only node)', sataturation.value, function ( val ) {
+
+						sataturation.value = val;
+						
+						updateMaterial();
+
+					}, false, 0, 2 );
+					
+					break;
+					
 				case 'physical':
 
 					// MATERIAL
@@ -480,8 +572,7 @@
 					//mtl.reflectivity = // reflectivity (float)
 					//mtl.clearCoat = // clearCoat (float)
 					//mtl.clearCoatRoughness = // clearCoatRoughness (float)
-					//mtl.normal = // normalmap (vec3)
-					//mtl.normalScale = // normalmap scale (vec2)
+					//mtl.normal = // normal (vec3)
 					//mtl.emissive = // emissive color (vec3)
 					//mtl.ambient = // ambient color (vec3)
 					//mtl.shadow = // shadowmap (vec3)
@@ -531,8 +622,8 @@
 					mtl.clearCoat = clearCoat;
 					mtl.clearCoatRoughness = clearCoatRoughness;
 					mtl.environment = new THREE.CubeTextureNode( cubemap );
-					mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
-					mtl.normalScale = normalMask;
+					mtl.normal = new THREE.NormalMapNode( new THREE.TextureNode( getTexture( "grassNormal" ) ) );
+					mtl.normal.scale = normalMask;
 
 					// GUI
 
@@ -921,23 +1012,21 @@
 
 					var diffuse = new THREE.TextureNode( getTexture( "brick" ) );
 
-					var bump = new THREE.BumpNode( new THREE.TextureNode( getTexture( "brick" ) ) );
-					bump.scale = new THREE.Vector2Node( - 1.5, - 1.5 );
+					var bumpMap = new THREE.BumpMapNode( new THREE.TextureNode( getTexture( "brick" ) ) );
+					bumpMap.scale = new THREE.FloatNode( .5 );
 
 					mtl.color = diffuse;
-					mtl.normal = bump;
+					mtl.normal = bumpMap;
 
+					// convert BumpMap to NormalMap
+					//bumpMap.toNormalMap = true;
+					//mtl.normal = new THREE.NormalMapNode( bumpMap );
+					
 					// GUI
 
-					addGui( 'scaleX', bump.scale.x, function ( val ) {
+					addGui( 'scale', bumpMap.scale.value, function ( val ) {
 
-						bump.scale.x = val;
-
-					}, false, - 2, 2 );
-
-					addGui( 'scaleY', bump.scale.y, function ( val ) {
-
-						bump.scale.y = val;
+						bumpMap.scale.value = val;
 
 					}, false, - 2, 2 );
 
@@ -1667,8 +1756,8 @@
 					);
 
 					mtl.color = color;
-					mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
-					mtl.normalScale = furScale;
+					mtl.normal = new THREE.NormalMapNode( new THREE.TextureNode( getTexture( "grassNormal" ) ) );
+					mtl.normal.scale = furScale;
 					mtl.environment = mildnessColor;
 					mtl.environmentAlpha = new THREE.Math1Node( viewZ, THREE.Math1Node.INVERT );
 					mtl.shininess = new THREE.FloatNode( 0 );
@@ -1993,36 +2082,74 @@
 					mtl.color = new THREE.FunctionCallNode( keywordsexample, [ new THREE.TextureNode( getTexture( "brick" ) ) ] );
 
 					break;
-
+					
 				case 'varying':
 
 					// MATERIAL
 
 					mtl = new THREE.PhongNodeMaterial();
 
+					var varying = new THREE.VarNode( "vec3" );
+					varying.value = new THREE.NormalNode( THREE.NormalNode.VIEW );
+					
+					// using BypassNode the NormalNode not apply the value in .transform slot
+					// but set the NormalNode value in VarNode
+					// it can be useful to send values between vertex to fragment shader
+					mtl.transform = new THREE.BypassNode( varying );
+					mtl.color = varying;
+					
+					// you can also set a value in .transform slot, such this expression using FunctionNode
+					mtl.transform = new THREE.BypassNode( varying, new THREE.FunctionNode("position * ( .1 + abs( sin( time ) ) )", "vec3") );
+
+					break;
+					
+				case 'void-function':
+
+					// MATERIAL
+
+					mtl = new THREE.PhongNodeMaterial();
+
 					var varying = new THREE.VarNode( "vec3" );
 
+					// VERTEX
+					
 					var setMyVar = new THREE.FunctionNode( [
-						"float setMyVar( vec3 pos ) {",
+						"void setMyVar( vec3 pos ) {",
 						// set "myVar" in vertex shader in this example,
 						// can be used in fragment shader too or in rest of the current shader
 						"	myVar = pos;",
-						// it is not accept "void" functions for now!
-						"	return 0.;",
+						
 						"}"
 					].join( "\n" ) );
 
 					// add keyword
 					setMyVar.keywords[ "myVar" ] = varying;
 
-					var transform = new THREE.FunctionNode( "setMyVar( position * .1 ) + position", "vec3", [ setMyVar ] );
+					var transform = new THREE.FunctionNode( "setMyVar( position * .1 )", "vec3" );
+					transform.includes = [ setMyVar ];
 					transform.keywords[ "tex" ] = new THREE.TextureNode( getTexture( "brick" ) );
 
-					mtl.transform = transform;
-					mtl.color = varying;
-
-					break;
+					// use BypassNode to "void" functions
+					mtl.transform = new THREE.BypassNode( transform );
+					
+					// FRAGMENT
+					
+					var clipFromPos = new THREE.FunctionNode( [
+						"void clipFromPos( vec3 pos ) {",
+						
+						"	if ( pos.y < .0 ) discard;",
+						
+						"}"
+					].join( "\n" ) );
+					
+					var clipFromPosCall = new THREE.FunctionCallNode( clipFromPos, {
+						pos: varying
+					} );
+					
+					mtl.color = new THREE.BypassNode( clipFromPosCall, varying );
 
+					break;	
+				
 				case 'readonly':
 
 					// MATERIAL
@@ -2191,7 +2318,7 @@
 
 					rtTexture = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat } );
 
-					library[ rtTexture.uuid ] = rtTexture;
+					library[ rtTexture.texture.uuid ] = rtTexture.texture;
 
 					var distanceMtl = new THREE.PhongNodeMaterial();
 					distanceMtl.environment = objectDepth;
@@ -2203,7 +2330,7 @@
 
 					mtl = new THREE.StandardNodeMaterial();
 
-					var backSideDepth = new THREE.TextureNode( rtTexture, new THREE.ScreenUVNode( new THREE.ResolutionNode( renderer ) ) );
+					var backSideDepth = new THREE.TextureNode( rtTexture.texture, new THREE.ScreenUVNode( new THREE.ResolutionNode( renderer ) ) );
 
 					var difference = new THREE.OperatorNode(
 						objectDepth,
@@ -2345,21 +2472,19 @@
 		function serialize() {
 
 			var json = mesh.material.toJSON(),
-				loader = new THREE.NodeMaterialLoader( null, library ),
-				material = loader.parse( json );
-
-			mesh.material = material;
-
+				jsonStr = JSON.stringify( json );
+				
 			// replace uuid to url (facilitates the load of textures using url otherside uuid)
 			// example:
 
-			THREE.NodeMaterialLoaderUtils.replaceUUID( json, getTexture( "cloud" ), "cloud" );
-
-			// get source
-
-			var jsonStr = JSON.stringify( json );
+			//THREE.NodeMaterialLoaderUtils.replaceUUID( json, getTexture( "cloud" ), "cloud" );
 
-			console.log( jsonStr );
+			var loader = new THREE.NodeMaterialLoader( null, library ),
+				material = loader.parse( json );
+			
+			mesh.material = material;
+			
+			//console.log( jsonStr );
 
 			// gui
 
@@ -2392,9 +2517,17 @@
 			//mesh.rotation.z += .01;
 
 			// update material animation and/or gpu calcs (pre-renderer)
-			frame.update( delta ).updateNode( mesh.material );
+			
+			frame.update( delta );
+			
+			if ( mesh.material instanceof THREE.NodeMaterial ) {
+			
+				frame.updateNode( mesh.material );
+				
+			}
 
 			// render to texture for sss/translucent material only
+			
 			if ( rtTexture ) {
 
 				scene.overrideMaterial = rtMaterial;