浏览代码

Merge pull request #13070 from sunag/90dev3

NodeMaterialLoader and improvements
Mr.doob 7 年之前
父节点
当前提交
bf91079e99
共有 66 个文件被更改,包括 2816 次插入657 次删除
  1. 1 0
      examples/files.js
  2. 587 0
      examples/js/loaders/NodeMaterialLoader.js
  3. 21 4
      examples/js/nodes/AttributeNode.js
  4. 27 6
      examples/js/nodes/ConstNode.js
  5. 39 5
      examples/js/nodes/FunctionCallNode.js
  6. 52 10
      examples/js/nodes/FunctionNode.js
  7. 64 11
      examples/js/nodes/GLNode.js
  8. 2 2
      examples/js/nodes/InputNode.js
  9. 32 32
      examples/js/nodes/NodeBuilder.js
  10. 56 56
      examples/js/nodes/NodeLib.js
  11. 147 80
      examples/js/nodes/NodeMaterial.js
  12. 19 2
      examples/js/nodes/RawNode.js
  13. 7 7
      examples/js/nodes/TempNode.js
  14. 20 3
      examples/js/nodes/VarNode.js
  15. 34 2
      examples/js/nodes/accessors/CameraNode.js
  16. 19 3
      examples/js/nodes/accessors/ColorsNode.js
  17. 4 3
      examples/js/nodes/accessors/LightNode.js
  18. 24 5
      examples/js/nodes/accessors/NormalNode.js
  19. 27 6
      examples/js/nodes/accessors/PositionNode.js
  20. 23 3
      examples/js/nodes/accessors/ReflectNode.js
  21. 19 2
      examples/js/nodes/accessors/ScreenUVNode.js
  22. 20 3
      examples/js/nodes/accessors/UVNode.js
  23. 20 1
      examples/js/nodes/inputs/ColorNode.js
  24. 24 4
      examples/js/nodes/inputs/CubeTextureNode.js
  25. 20 3
      examples/js/nodes/inputs/FloatNode.js
  26. 20 3
      examples/js/nodes/inputs/IntNode.js
  27. 18 1
      examples/js/nodes/inputs/Matrix4Node.js
  28. 30 5
      examples/js/nodes/inputs/ReflectorNode.js
  29. 4 3
      examples/js/nodes/inputs/ScreenNode.js
  30. 26 4
      examples/js/nodes/inputs/TextureNode.js
  31. 19 1
      examples/js/nodes/inputs/Vector2Node.js
  32. 20 1
      examples/js/nodes/inputs/Vector3Node.js
  33. 21 1
      examples/js/nodes/inputs/Vector4Node.js
  34. 43 2
      examples/js/nodes/materials/PhongNode.js
  35. 4 2
      examples/js/nodes/materials/PhongNodeMaterial.js
  36. 28 2
      examples/js/nodes/materials/SpriteNode.js
  37. 3 1
      examples/js/nodes/materials/SpriteNodeMaterial.js
  38. 48 4
      examples/js/nodes/materials/StandardNode.js
  39. 4 2
      examples/js/nodes/materials/StandardNodeMaterial.js
  40. 24 3
      examples/js/nodes/math/Math1Node.js
  41. 26 4
      examples/js/nodes/math/Math2Node.js
  42. 24 3
      examples/js/nodes/math/Math3Node.js
  43. 22 3
      examples/js/nodes/math/OperatorNode.js
  44. 44 2
      examples/js/nodes/postprocessing/NodePass.js
  45. 53 31
      examples/js/nodes/utils/BlurNode.js
  46. 29 10
      examples/js/nodes/utils/BumpNode.js
  47. 21 2
      examples/js/nodes/utils/ColorAdjustmentNode.js
  48. 37 4
      examples/js/nodes/utils/JoinNode.js
  49. 19 2
      examples/js/nodes/utils/LuminanceNode.js
  50. 19 2
      examples/js/nodes/utils/NoiseNode.js
  51. 23 2
      examples/js/nodes/utils/NormalMapNode.js
  52. 19 4
      examples/js/nodes/utils/ResolutionNode.js
  53. 12 11
      examples/js/nodes/utils/RoughnessToBlinnExponentNode.js
  54. 22 4
      examples/js/nodes/utils/SwitchNode.js
  55. 19 4
      examples/js/nodes/utils/TimerNode.js
  56. 18 0
      examples/js/nodes/utils/UVTransformNode.js
  57. 82 11
      examples/js/nodes/utils/VelocityNode.js
  58. 0 0
      examples/nodes/caustic.json
  59. 0 0
      examples/nodes/displace.json
  60. 0 0
      examples/nodes/wave.json
  61. 0 0
      examples/nodes/xray.json
  62. 285 0
      examples/webgl_loader_nodes.html
  63. 131 49
      examples/webgl_materials_nodes.html
  64. 206 187
      examples/webgl_mirror_nodes.html
  65. 44 30
      examples/webgl_postprocessing_nodes.html
  66. 41 4
      examples/webgl_sprites_nodes.html

+ 1 - 0
examples/files.js

@@ -102,6 +102,7 @@ var files = {
 		"webgl_loader_mmd_pose",
 		"webgl_loader_mmd_audio",
 		"webgl_loader_msgpack",
+		"webgl_loader_nodes",
 		"webgl_loader_obj",
 		"webgl_loader_obj_mtl",
 		"webgl_loader_obj2",

+ 587 - 0
examples/js/loaders/NodeMaterialLoader.js

@@ -0,0 +1,587 @@
+/**
+ * @author sunag / http://www.sunag.com.br/
+ */
+
+THREE.NodeMaterialLoader = function ( manager, library ) {
+
+	this.manager = ( manager !== undefined ) ? manager : THREE.DefaultLoadingManager;
+
+	this.nodes = {};
+	this.materials = {};
+	this.passes = {};
+	this.names = {};
+	this.library = library || {};
+
+};
+
+THREE.NodeMaterialLoaderUtils = {
+
+	replaceUUIDObject: function ( object, uuid, value, recursive ) {
+
+		recursive = recursive !== undefined ? recursive : true;
+
+		if ( typeof uuid === "object" ) uuid = uuid.uuid;
+
+		if ( typeof object === "object" ) {
+
+			var keys = Object.keys( object );
+
+			for ( var i = 0; i < keys.length; i ++ ) {
+
+				var key = keys[ i ];
+
+				if ( recursive ) {
+
+					object[ key ] = this.replaceUUIDObject( object[ key ], uuid, value );
+
+				}
+
+				if ( key === uuid ) {
+
+					object[ uuid ] = object[ key ];
+
+					delete object[ key ];
+
+				}
+
+			}
+
+		}
+
+		return object === uuid ? value : object;
+
+	},
+
+	replaceUUID: function ( json, uuid, value ) {
+
+		this.replaceUUIDObject( json, uuid, value, false );
+		this.replaceUUIDObject( json.nodes, uuid, value );
+		this.replaceUUIDObject( json.materials, uuid, value );
+		this.replaceUUIDObject( json.passes, uuid, value );
+		this.replaceUUIDObject( json.library, uuid, value, false );
+
+		return json;
+
+	}
+
+};
+
+Object.assign( THREE.NodeMaterialLoader.prototype, {
+
+	load: function ( url, onLoad, onProgress, onError ) {
+
+		var scope = this;
+
+		var loader = new THREE.FileLoader( scope.manager );
+		loader.load( url, function ( text ) {
+
+			onLoad( scope.parse( JSON.parse( text ) ) );
+
+		}, onProgress, onError );
+
+		return this;
+
+	},
+
+	getObjectByName: function ( uuid ) {
+
+		return this.names[ uuid ];
+
+	},
+
+	getObjectById: function ( uuid ) {
+
+		return this.library[ uuid ] || this.nodes[ uuid ] || this.names[ uuid ];
+
+	},
+
+	getNode: function ( uuid ) {
+
+		var object = this.getObjectById( uuid );
+
+		if ( ! object ) {
+
+			console.warn( "Node \"" + uuid + "\" not found." );
+
+		}
+
+		return object;
+
+	},
+
+	parse: function ( json ) {
+
+		var uuid, node, object, prop, i;
+
+		for ( uuid in json.nodes ) {
+
+			node = json.nodes[ uuid ];
+
+			object = new THREE[ node.type ]();
+
+			if ( node.name ) {
+
+				object.name = node.name;
+
+				this.names[ object.name ] = object;
+
+			}
+
+			this.nodes[ uuid ] = object;
+
+		}
+
+		for ( uuid in json.materials ) {
+
+			node = json.materials[ uuid ];
+
+			object = new THREE[ node.type ]();
+
+			if ( node.name ) {
+
+				object.name = node.name;
+
+				this.names[ object.name ] = object;
+
+			}
+
+			this.materials[ uuid ] = object;
+
+		}
+
+		for ( uuid in json.passes ) {
+
+			node = json.passes[ uuid ];
+
+			object = new THREE[ node.type ]();
+
+			if ( node.name ) {
+
+				object.name = node.name;
+
+				this.names[ object.name ] = object;
+
+			}
+
+			this.passes[ uuid ] = object;
+
+		}
+
+		if ( json.material ) this.material = this.materials[ uuid ];
+		if ( json.pass ) this.pass = this.passes[ uuid ];
+
+		for ( uuid in json.nodes ) {
+
+			node = json.nodes[ uuid ];
+			object = this.nodes[ uuid ];
+
+			switch ( node.type ) {
+
+				case "FloatNode":
+
+					object.number = node.number;
+
+					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 "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":
+
+					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.value.fromArray( node.elements );
+
+					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.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 "ScreenNode":
+
+					if ( node.value ) object.value = this.getNode( node.value );
+
+					object.coord = this.getNode( node.coord );
+					object.project = node.project;
+
+					if ( node.bias ) object.bias = this.getNode( node.bias );
+
+					break;
+
+				case "CubeTextureNode":
+
+					object.value = this.getNode( node.value );
+
+					break;
+
+				case "LightNode":
+				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." );
+
+			}
+
+		}
+
+		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;
+
+			}
+
+			object.build();
+
+			if ( node.fog !== undefined ) object.fog = node.fog;
+			if ( node.lights !== undefined ) object.lights = node.lights;
+
+			if ( node.transparent !== undefined ) object.transparent = node.transparent;
+
+		}
+
+		for ( uuid in json.passes ) {
+
+			node = json.passes[ uuid ];
+			object = this.passes[ uuid ];
+
+			object.value = this.getNode( node.value );
+
+			object.build();
+
+		}
+
+		return this.material || this.pass || this;
+
+	}
+
+} );

+ 21 - 4
examples/js/nodes/AttributeNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.AttributeNode = function( name, type ) {
+THREE.AttributeNode = function ( name, type ) {
 
 	THREE.GLNode.call( this, type );
 
@@ -12,14 +12,15 @@ THREE.AttributeNode = function( name, type ) {
 
 THREE.AttributeNode.prototype = Object.create( THREE.GLNode.prototype );
 THREE.AttributeNode.prototype.constructor = THREE.AttributeNode;
+THREE.AttributeNode.prototype.nodeType = "Attribute";
 
-THREE.AttributeNode.prototype.getAttributeType = function( builder ) {
+THREE.AttributeNode.prototype.getAttributeType = function ( builder ) {
 
 	return typeof this.type === 'number' ? builder.getConstructorFromLength( this.type ) : this.type;
 
 };
 
-THREE.AttributeNode.prototype.getType = function( builder ) {
+THREE.AttributeNode.prototype.getType = function ( builder ) {
 
 	var type = this.getAttributeType( builder );
 
@@ -27,7 +28,7 @@ THREE.AttributeNode.prototype.getType = function( builder ) {
 
 };
 
-THREE.AttributeNode.prototype.generate = function( builder, output ) {
+THREE.AttributeNode.prototype.generate = function ( builder, output ) {
 
 	var type = this.getAttributeType( builder );
 
@@ -36,3 +37,19 @@ THREE.AttributeNode.prototype.generate = function( builder, output ) {
 	return builder.format( builder.isShader( 'vertex' ) ? this.name : attribute.varying.name, this.getType( builder ), output );
 
 };
+
+THREE.AttributeNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.out = this.type;
+
+	}
+
+	return data;
+
+};

+ 27 - 6
examples/js/nodes/ConstNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ConstNode = function( src, useDefine ) {
+THREE.ConstNode = function ( src, useDefine ) {
 
 	THREE.TempNode.call( this );
 
@@ -19,18 +19,19 @@ THREE.ConstNode.EPSILON = 'EPSILON';
 
 THREE.ConstNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.ConstNode.prototype.constructor = THREE.ConstNode;
+THREE.ConstNode.prototype.nodeType = "Const";
 
-THREE.ConstNode.prototype.getType = function( builder ) {
+THREE.ConstNode.prototype.getType = function ( builder ) {
 
 	return builder.getTypeByFormat( this.type );
 
 };
 
-THREE.ConstNode.prototype.eval = function( src, useDefine ) {
+THREE.ConstNode.prototype.eval = function ( src, useDefine ) {
 
 	src = ( src || '' ).trim();
 
-	var name, type, value;
+	var name, type, value = "";
 
 	var rDeclaration = /^([a-z_0-9]+)\s([a-z_0-9]+)\s?\=?\s?(.*?)(\;|$)/i;
 	var match = src.match( rDeclaration );
@@ -56,7 +57,7 @@ THREE.ConstNode.prototype.eval = function( src, useDefine ) {
 
 };
 
-THREE.ConstNode.prototype.build = function( builder, output ) {
+THREE.ConstNode.prototype.build = function ( builder, output ) {
 
 	if ( output === 'source' ) {
 
@@ -82,8 +83,28 @@ THREE.ConstNode.prototype.build = function( builder, output ) {
 
 };
 
-THREE.ConstNode.prototype.generate = function( builder, output ) {
+THREE.ConstNode.prototype.generate = function ( builder, output ) {
 
 	return builder.format( this.name, this.getType( builder ), output );
 
 };
+
+THREE.ConstNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.name = this.name;
+		data.out = this.type;
+
+		if ( this.value ) data.value = this.value;
+		if ( data.useDefine === true ) data.useDefine = true;
+
+	}
+
+	return data;
+
+};

+ 39 - 5
examples/js/nodes/FunctionCallNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.FunctionCallNode = function( func, inputs ) {
+THREE.FunctionCallNode = function ( func, inputs ) {
 
 	THREE.TempNode.call( this );
 
@@ -12,27 +12,28 @@ THREE.FunctionCallNode = function( func, inputs ) {
 
 THREE.FunctionCallNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.FunctionCallNode.prototype.constructor = THREE.FunctionCallNode;
+THREE.FunctionCallNode.prototype.nodeType = "FunctionCall";
 
-THREE.FunctionCallNode.prototype.setFunction = function( func, inputs ) {
+THREE.FunctionCallNode.prototype.setFunction = function ( func, inputs ) {
 
 	this.value = func;
 	this.inputs = inputs || [];
 
 };
 
-THREE.FunctionCallNode.prototype.getFunction = function() {
+THREE.FunctionCallNode.prototype.getFunction = function () {
 
 	return this.value;
 
 };
 
-THREE.FunctionCallNode.prototype.getType = function( builder ) {
+THREE.FunctionCallNode.prototype.getType = function ( builder ) {
 
 	return this.value.getType( builder );
 
 };
 
-THREE.FunctionCallNode.prototype.generate = function( builder, output ) {
+THREE.FunctionCallNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
@@ -56,3 +57,36 @@ THREE.FunctionCallNode.prototype.generate = function( builder, output ) {
 	return builder.format( code, type, output );
 
 };
+
+THREE.FunctionCallNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		var func = this.value;
+
+		data = this.createJSONNode( meta );
+
+		data.value = this.value.toJSON( meta ).uuid;
+
+		if ( func.inputs.length ) {
+
+			data.inputs = {};
+
+			for ( var i = 0; i < func.inputs.length; i ++ ) {
+
+				var inpt = func.inputs[ i ];
+				var node = this.inputs[ i ] || this.inputs[ inpt.name ];
+
+				data.inputs[ inpt.name ] = node.toJSON( meta ).uuid;
+
+			}
+
+		}
+
+	}
+
+	return data;
+
+};

+ 52 - 10
examples/js/nodes/FunctionNode.js

@@ -3,7 +3,7 @@
  * @thanks bhouston / https://clara.io/
  */
 
-THREE.FunctionNode = function( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions ) {
+THREE.FunctionNode = function ( src, includesOrType, extensionsOrIncludes, keywordsOrExtensions ) {
 
 	src = src || '';
 
@@ -22,20 +22,21 @@ THREE.FunctionNode.rProperties = /[a-z_0-9]+/ig;
 
 THREE.FunctionNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.FunctionNode.prototype.constructor = THREE.FunctionNode;
+THREE.FunctionNode.prototype.nodeType = "Function";
 
-THREE.FunctionNode.prototype.isShared = function( builder, output ) {
+THREE.FunctionNode.prototype.isShared = function ( builder, output ) {
 
 	return ! this.isMethod;
 
 };
 
-THREE.FunctionNode.prototype.getType = function( builder ) {
+THREE.FunctionNode.prototype.getType = function ( builder ) {
 
 	return builder.getTypeByFormat( this.type );
 
 };
 
-THREE.FunctionNode.prototype.getInputByName = function( name ) {
+THREE.FunctionNode.prototype.getInputByName = function ( name ) {
 
 	var i = this.inputs.length;
 
@@ -48,7 +49,7 @@ THREE.FunctionNode.prototype.getInputByName = function( name ) {
 
 };
 
-THREE.FunctionNode.prototype.getIncludeByName = function( name ) {
+THREE.FunctionNode.prototype.getIncludeByName = function ( name ) {
 
 	var i = this.includes.length;
 
@@ -61,7 +62,7 @@ THREE.FunctionNode.prototype.getIncludeByName = function( name ) {
 
 };
 
-THREE.FunctionNode.prototype.generate = function( builder, output ) {
+THREE.FunctionNode.prototype.generate = function ( builder, output ) {
 
 	var match, offset = 0, src = this.value;
 
@@ -136,7 +137,7 @@ THREE.FunctionNode.prototype.generate = function( builder, output ) {
 
 };
 
-THREE.FunctionNode.prototype.eval = function( src, includes, extensions, keywords ) {
+THREE.FunctionNode.prototype.eval = function ( src, includes, extensions, keywords ) {
 
 	src = ( src || '' ).trim();
 
@@ -180,9 +181,9 @@ THREE.FunctionNode.prototype.eval = function( src, includes, extensions, keyword
 					name = inputs[ i ++ ];
 
 					this.inputs.push( {
-						name : name,
-						type : type,
-						qualifier : qualifier
+						name: name,
+						type: type,
+						qualifier: qualifier
 					} );
 
 				}
@@ -201,3 +202,44 @@ THREE.FunctionNode.prototype.eval = function( src, includes, extensions, keyword
 	this.value = src;
 
 };
+
+THREE.FunctionNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.src = this.value;
+		data.isMethod = this.isMethod;
+		data.useKeywords = this.useKeywords;
+
+		if ( ! this.isMethod ) data.out = this.type;
+
+		data.extensions = JSON.parse( JSON.stringify( this.extensions ) );
+		data.keywords = {};
+
+		for ( var keyword in this.keywords ) {
+
+			data.keywords[ keyword ] = this.keywords[ keyword ].toJSON( meta ).uuid;
+
+		}
+
+		if ( this.includes.length ) {
+
+			data.includes = [];
+
+			for ( var i = 0; i < this.includes.length; i ++ ) {
+
+				data.includes.push( this.includes[ i ].toJSON( meta ).uuid );
+
+			}
+
+		}
+
+	}
+
+	return data;
+
+};

+ 64 - 11
examples/js/nodes/GLNode.js

@@ -2,18 +2,22 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.GLNode = function( type ) {
+THREE.GLNode = function ( type ) {
 
 	this.uuid = THREE.Math.generateUUID();
 
+	this.name = "";
 	this.allows = {};
-	this.requestUpdate = false;
 
 	this.type = type;
 
+	this.userData = {};
+
 };
 
-THREE.GLNode.prototype.parse = function( builder, context ) {
+THREE.GLNode.prototype.isNode = true;
+
+THREE.GLNode.prototype.parse = function ( builder, context ) {
 
 	context = context || {};
 
@@ -32,7 +36,7 @@ THREE.GLNode.prototype.parse = function( builder, context ) {
 
 };
 
-THREE.GLNode.prototype.parseAndBuildCode = function( builder, output, context ) {
+THREE.GLNode.prototype.parseAndBuildCode = function ( builder, output, context ) {
 
 	context = context || {};
 
@@ -42,13 +46,13 @@ THREE.GLNode.prototype.parseAndBuildCode = function( builder, output, context )
 
 };
 
-THREE.GLNode.prototype.buildCode = function( builder, output, context ) {
+THREE.GLNode.prototype.buildCode = function ( builder, output, context ) {
 
 	context = context || {};
 
 	var material = builder.material;
 
-	var data = { result : this.build( builder.addCache( context.cache, context.requires ).addSlot( context.slot ), output ) };
+	var data = { result: this.build( builder.addCache( context.cache, context.requires ).addSlot( context.slot ), output ) };
 
 	if ( builder.isShader( 'vertex' ) ) data.code = material.clearVertexNode();
 	else data.code = material.clearFragmentNode();
@@ -59,7 +63,7 @@ THREE.GLNode.prototype.buildCode = function( builder, output, context ) {
 
 };
 
-THREE.GLNode.prototype.build = function( builder, output, uuid ) {
+THREE.GLNode.prototype.build = function ( builder, output, uuid ) {
 
 	output = output || this.getType( builder, output );
 
@@ -73,9 +77,15 @@ THREE.GLNode.prototype.build = function( builder, output, uuid ) {
 
 	}
 
-	if ( this.requestUpdate && material.requestUpdate.indexOf( this ) === - 1 ) {
+	if ( material.nodes.indexOf( this ) === - 1 ) {
 
-		material.requestUpdate.push( this );
+		material.nodes.push( this );
+
+	}
+
+	if ( this.updateFrame !== undefined && material.updaters.indexOf( this ) === - 1 ) {
+
+		material.updaters.push( this );
 
 	}
 
@@ -83,7 +93,7 @@ THREE.GLNode.prototype.build = function( builder, output, uuid ) {
 
 };
 
-THREE.GLNode.prototype.appendDepsNode = function( builder, data, output ) {
+THREE.GLNode.prototype.appendDepsNode = function ( builder, data, output ) {
 
 	data.deps = ( data.deps || 0 ) + 1;
 
@@ -98,8 +108,51 @@ THREE.GLNode.prototype.appendDepsNode = function( builder, data, output ) {
 
 };
 
-THREE.GLNode.prototype.getType = function( builder, output ) {
+THREE.GLNode.prototype.getType = function ( builder, output ) {
 
 	return output === 'sampler2D' || output === 'samplerCube' ? output : this.type;
 
 };
+
+THREE.GLNode.prototype.getJSONNode = function ( meta ) {
+
+	var isRootObject = ( meta === undefined || typeof meta === 'string' );
+
+	if ( ! isRootObject && meta.nodes[ this.uuid ] !== undefined ) {
+
+		return meta.nodes[ this.uuid ];
+
+	}
+
+};
+
+THREE.GLNode.prototype.createJSONNode = function ( meta ) {
+
+	var isRootObject = ( meta === undefined || typeof meta === 'string' );
+
+	var data = {};
+
+	if ( typeof this.nodeType !== "string" ) throw new Error( "Node does not allow serialization." );
+
+	data.uuid = this.uuid;
+	data.type = this.nodeType + "Node";
+
+	if ( this.name !== "" ) data.name = this.name;
+
+	if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
+
+	if ( ! isRootObject ) {
+
+		meta.nodes[ this.uuid ] = data;
+
+	}
+
+	return data;
+
+};
+
+THREE.GLNode.prototype.toJSON = function ( meta ) {
+
+	return this.getJSONNode( meta ) || this.createJSONNode( meta );
+
+};

+ 2 - 2
examples/js/nodes/InputNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.InputNode = function( type, params ) {
+THREE.InputNode = function ( type, params ) {
 
 	params = params || {};
 	params.shared = params.shared !== undefined ? params.shared : false;
@@ -14,7 +14,7 @@ THREE.InputNode = function( type, params ) {
 THREE.InputNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.InputNode.prototype.constructor = THREE.InputNode;
 
-THREE.InputNode.prototype.generate = function( builder, output, uuid, type, ns, needsUpdate ) {
+THREE.InputNode.prototype.generate = function ( builder, output, uuid, type, ns, needsUpdate ) {
 
 	var material = builder.material;
 

+ 32 - 32
examples/js/nodes/NodeBuilder.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.NodeBuilder = function( material ) {
+THREE.NodeBuilder = function ( material ) {
 
 	this.material = material;
 
@@ -19,12 +19,12 @@ THREE.NodeBuilder = function( material ) {
 };
 
 THREE.NodeBuilder.type = {
-	float : 'fv1',
-	vec2 : 'v2',
-	vec3 : 'v3',
-	vec4 : 'v4',
-	mat4 : 'v4',
-	int : 'iv1'
+	float: 'fv1',
+	vec2: 'v2',
+	vec3: 'v3',
+	vec4: 'v4',
+	mat4: 'v4',
+	int: 'iv1'
 };
 
 THREE.NodeBuilder.constructors = [
@@ -45,18 +45,18 @@ THREE.NodeBuilder.prototype = {
 
 	constructor: THREE.NodeBuilder,
 
-	addCache : function( name, requires ) {
+	addCache: function ( name, requires ) {
 
 		this.caches.push( {
-			name : name || '',
-			requires : requires || {}
+			name: name || '',
+			requires: requires || {}
 		} );
 
 		return this.update();
 
 	},
 
-	removeCache : function() {
+	removeCache: function () {
 
 		this.caches.pop();
 
@@ -64,17 +64,17 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	addSlot : function( name ) {
+	addSlot: function ( name ) {
 
 		this.slots.push( {
-			name : name || ''
+			name: name || ''
 		} );
 
 		return this.update();
 
 	},
 
-	removeSlot : function() {
+	removeSlot: function () {
 
 		this.slots.pop();
 
@@ -82,7 +82,7 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	isCache : function( name ) {
+	isCache: function ( name ) {
 
 		var i = this.caches.length;
 
@@ -96,7 +96,7 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	isSlot : function( name ) {
+	isSlot: function ( name ) {
 
 		var i = this.slots.length;
 
@@ -110,7 +110,7 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	update : function() {
+	update: function () {
 
 		var cache = this.caches[ this.caches.length - 1 ];
 		var slot = this.slots[ this.slots.length - 1 ];
@@ -123,7 +123,7 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	require : function( name, node ) {
+	require: function ( name, node ) {
 
 		this.requires[ name ] = node;
 
@@ -131,7 +131,7 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	include : function( node, parent, source ) {
+	include: function ( node, parent, source ) {
 
 		this.material.include( this, node, parent, source );
 
@@ -139,37 +139,37 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	colorToVector : function( color ) {
+	colorToVector: function ( color ) {
 
 		return color.replace( 'r', 'x' ).replace( 'g', 'y' ).replace( 'b', 'z' ).replace( 'a', 'w' );
 
 	},
 
-	getConstructorFromLength : function( len ) {
+	getConstructorFromLength: function ( len ) {
 
 		return THREE.NodeBuilder.constructors[ len - 1 ];
 
 	},
 
-	getFormatName : function( format ) {
+	getFormatName: function ( format ) {
 
 		return format.replace( /c/g, 'v3' ).replace( /fv1/g, 'v1' ).replace( /iv1/g, 'i' );
 
 	},
 
-	isFormatMatrix : function( format ) {
+	isFormatMatrix: function ( format ) {
 
 		return /^m/.test( format );
 
 	},
 
-	getFormatLength : function( format ) {
+	getFormatLength: function ( format ) {
 
 		return parseInt( this.getFormatName( format ).substr( 1 ) );
 
 	},
 
-	getFormatFromLength : function( len ) {
+	getFormatFromLength: function ( len ) {
 
 		if ( len == 1 ) return 'fv1';
 
@@ -177,7 +177,7 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	format : function( code, from, to ) {
+	format: function ( code, from, to ) {
 
 		var format = this.getFormatName( to + '=' + from );
 
@@ -214,13 +214,13 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	getTypeByFormat : function( format ) {
+	getTypeByFormat: function ( format ) {
 
 		return THREE.NodeBuilder.type[ format ] || format;
 
 	},
 
-	getUuid : function( uuid, useCache ) {
+	getUuid: function ( uuid, useCache ) {
 
 		useCache = useCache !== undefined ? useCache : true;
 
@@ -230,25 +230,25 @@ THREE.NodeBuilder.prototype = {
 
 	},
 
-	getElementByIndex : function( index ) {
+	getElementByIndex: function ( index ) {
 
 		return THREE.NodeBuilder.elements[ index ];
 
 	},
 
-	getIndexByElement : function( elm ) {
+	getIndexByElement: function ( elm ) {
 
 		return THREE.NodeBuilder.elements.indexOf( elm );
 
 	},
 
-	isShader : function( shader ) {
+	isShader: function ( shader ) {
 
 		return this.shader == shader;
 
 	},
 
-	setShader : function( shader ) {
+	setShader: function ( shader ) {
 
 		this.shader = shader;
 

+ 56 - 56
examples/js/nodes/NodeLib.js

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

+ 147 - 80
examples/js/nodes/NodeMaterial.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.NodeMaterial = function( vertex, fragment ) {
+THREE.NodeMaterial = function ( vertex, fragment ) {
 
 	THREE.ShaderMaterial.call( this );
 
@@ -12,29 +12,29 @@ THREE.NodeMaterial = function( vertex, fragment ) {
 };
 
 THREE.NodeMaterial.types = {
-	t : 'sampler2D',
-	tc : 'samplerCube',
-	bv1 : 'bool',
-	iv1 : 'int',
-	fv1 : 'float',
-	c : 'vec3',
-	v2 : 'vec2',
-	v3 : 'vec3',
-	v4 : 'vec4',
-	m4 : 'mat4'
+	t: 'sampler2D',
+	tc: 'samplerCube',
+	bv1: 'bool',
+	iv1: 'int',
+	fv1: 'float',
+	c: 'vec3',
+	v2: 'vec2',
+	v3: 'vec3',
+	v4: 'vec4',
+	m4: 'mat4'
 };
 
-THREE.NodeMaterial.addShortcuts = function( proto, prop, list ) {
+THREE.NodeMaterial.addShortcuts = function ( proto, prop, list ) {
 
 	function applyShortcut( prop, name ) {
 
 		return {
-			get: function() {
+			get: function () {
 
 				return this[ prop ][ name ];
 
 			},
-			set: function( val ) {
+			set: function ( val ) {
 
 				this[ prop ][ name ] = val;
 
@@ -43,7 +43,7 @@ THREE.NodeMaterial.addShortcuts = function( proto, prop, list ) {
 
 	}
 
-	return ( function() {
+	return ( function () {
 
 		var shortcuts = {};
 
@@ -63,28 +63,31 @@ THREE.NodeMaterial.addShortcuts = function( proto, prop, list ) {
 
 THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
 THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
+THREE.NodeMaterial.prototype.type = "NodeMaterial";
 
-THREE.NodeMaterial.prototype.updateFrame = function( delta ) {
+THREE.NodeMaterial.prototype.updateFrame = function ( delta ) {
 
-	for ( var i = 0; i < this.requestUpdate.length; ++ i ) {
+	for ( var i = 0; i < this.updaters.length; ++ i ) {
 
-		this.requestUpdate[ i ].updateFrame( delta );
+		this.updaters[ i ].updateFrame( delta );
 
 	}
 
 };
 
-THREE.NodeMaterial.prototype.build = function() {
+THREE.NodeMaterial.prototype.build = function () {
 
 	var vertex, fragment;
 
+	this.nodes = [];
+
 	this.defines = {};
 	this.uniforms = {};
 	this.attributes = {};
 
 	this.extensions = {};
 
-	this.nodeData = {};	
+	this.nodeData = {};
 
 	this.vertexUniform = [];
 	this.fragmentUniform = [];
@@ -98,11 +101,13 @@ THREE.NodeMaterial.prototype.build = function() {
 	this.consts = [];
 	this.functions = [];
 
-	this.requestUpdate = [];
+	this.updaters = [];
 
-	this.requestAttribs = {
+	this.requires = {
 		uv: [],
-		color: []
+		color: [],
+		lights: this.lights,
+		fog: this.fog
 	};
 
 	this.vertexPars = '';
@@ -115,25 +120,25 @@ THREE.NodeMaterial.prototype.build = function() {
 	this.fragmentNode = '';
 
 	this.prefixCode = [
-	"#ifdef GL_EXT_shader_texture_lod",
+		"#ifdef GL_EXT_shader_texture_lod",
 
-	"	#define texCube(a, b) textureCube(a, b)",
-	"	#define texCubeBias(a, b, c) textureCubeLodEXT(a, b, c)",
+		"	#define texCube(a, b) textureCube(a, b)",
+		"	#define texCubeBias(a, b, c) textureCubeLodEXT(a, b, c)",
 
-	"	#define tex2D(a, b) texture2D(a, b)",
-	"	#define tex2DBias(a, b, c) texture2DLodEXT(a, b, c)",
+		"	#define tex2D(a, b) texture2D(a, b)",
+		"	#define tex2DBias(a, b, c) texture2DLodEXT(a, b, c)",
 
-	"#else",
+		"#else",
 
-	"	#define texCube(a, b) textureCube(a, b)",
-	"	#define texCubeBias(a, b, c) textureCube(a, b, c)",
+		"	#define texCube(a, b) textureCube(a, b)",
+		"	#define texCubeBias(a, b, c) textureCube(a, b, c)",
 
-	"	#define tex2D(a, b) texture2D(a, b)",
-	"	#define tex2DBias(a, b, c) texture2D(a, b, c)",
+		"	#define tex2D(a, b) texture2D(a, b)",
+		"	#define tex2DBias(a, b, c) texture2D(a, b, c)",
 
-	"#endif",
+		"#endif",
 
-	"#include <packing>"
+		"#include <packing>"
 
 	].join( "\n" );
 
@@ -142,7 +147,7 @@ THREE.NodeMaterial.prototype.build = function() {
 	vertex = this.vertex.build( builder.setShader( 'vertex' ), 'v4' );
 	fragment = this.fragment.build( builder.setShader( 'fragment' ), 'v4' );
 
-	if ( this.requestAttribs.uv[ 0 ] ) {
+	if ( this.requires.uv[ 0 ] ) {
 
 		this.addVertexPars( 'varying vec2 vUv;' );
 		this.addFragmentPars( 'varying vec2 vUv;' );
@@ -151,7 +156,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttribs.uv[ 1 ] ) {
+	if ( this.requires.uv[ 1 ] ) {
 
 		this.addVertexPars( 'varying vec2 vUv2; attribute vec2 uv2;' );
 		this.addFragmentPars( 'varying vec2 vUv2;' );
@@ -160,7 +165,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttribs.color[ 0 ] ) {
+	if ( this.requires.color[ 0 ] ) {
 
 		this.addVertexPars( 'varying vec4 vColor; attribute vec4 color;' );
 		this.addFragmentPars( 'varying vec4 vColor;' );
@@ -169,7 +174,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttribs.color[ 1 ] ) {
+	if ( this.requires.color[ 1 ] ) {
 
 		this.addVertexPars( 'varying vec4 vColor2; attribute vec4 color2;' );
 		this.addFragmentPars( 'varying vec4 vColor2;' );
@@ -178,7 +183,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttribs.position ) {
+	if ( this.requires.position ) {
 
 		this.addVertexPars( 'varying vec3 vPosition;' );
 		this.addFragmentPars( 'varying vec3 vPosition;' );
@@ -187,16 +192,16 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttribs.worldPosition ) {
+	if ( this.requires.worldPosition ) {
 
 		this.addVertexPars( 'varying vec3 vWPosition;' );
 		this.addFragmentPars( 'varying vec3 vWPosition;' );
 
-		this.addVertexCode( 'vWPosition = ( modelMatrix * vec4( position, 1.0 ) ).xyz;' );
+		this.addVertexCode( 'vWPosition = ( modelMatrix * vec4( transformed, 1.0 ) ).xyz;' );
 
 	}
 
-	if ( this.requestAttribs.normal ) {
+	if ( this.requires.normal ) {
 
 		this.addVertexPars( 'varying vec3 vObjectNormal;' );
 		this.addFragmentPars( 'varying vec3 vObjectNormal;' );
@@ -205,7 +210,7 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	if ( this.requestAttribs.worldNormal ) {
+	if ( this.requires.worldNormal ) {
 
 		this.addVertexPars( 'varying vec3 vWNormal;' );
 		this.addFragmentPars( 'varying vec3 vWNormal;' );
@@ -214,8 +219,10 @@ THREE.NodeMaterial.prototype.build = function() {
 
 	}
 
-	this.lights = this.requestAttribs.light;
-	this.transparent = this.requestAttribs.transparent || this.blending > THREE.NormalBlending;
+	this.fog = this.requires.fog;
+	this.lights = this.requires.lights;
+
+	this.transparent = this.requires.transparent || this.blending > THREE.NormalBlending;
 
 	this.vertexShader = [
 		this.prefixCode,
@@ -250,19 +257,19 @@ THREE.NodeMaterial.prototype.build = function() {
 
 };
 
-THREE.NodeMaterial.prototype.define = function( name, value ) {
+THREE.NodeMaterial.prototype.define = function ( name, value ) {
 
 	this.defines[ name ] = value == undefined ? 1 : value;
 
 };
 
-THREE.NodeMaterial.prototype.isDefined = function( name ) {
+THREE.NodeMaterial.prototype.isDefined = function ( name ) {
 
 	return this.defines[ name ] != undefined;
 
 };
 
-THREE.NodeMaterial.prototype.mergeUniform = function( uniforms ) {
+THREE.NodeMaterial.prototype.mergeUniform = function ( uniforms ) {
 
 	for ( var name in uniforms ) {
 
@@ -272,15 +279,15 @@ THREE.NodeMaterial.prototype.mergeUniform = function( uniforms ) {
 
 };
 
-THREE.NodeMaterial.prototype.createUniform = function( type, value, ns, needsUpdate ) {
+THREE.NodeMaterial.prototype.createUniform = function ( type, value, ns, needsUpdate ) {
 
 	var index = this.uniformList.length;
 
 	var uniform = {
-		type : type,
-		value : value,
-		name : ns ? ns : 'nVu' + index,
-		needsUpdate : needsUpdate
+		type: type,
+		value: value,
+		name: ns ? ns : 'nVu' + index,
+		needsUpdate: needsUpdate
 	};
 
 	this.uniformList.push( uniform );
@@ -289,7 +296,7 @@ THREE.NodeMaterial.prototype.createUniform = function( type, value, ns, needsUpd
 
 };
 
-THREE.NodeMaterial.prototype.getVertexTemp = function( uuid, type, ns ) {
+THREE.NodeMaterial.prototype.getVertexTemp = function ( uuid, type, ns ) {
 
 	var data = this.vertexTemps[ uuid ];
 
@@ -298,7 +305,7 @@ THREE.NodeMaterial.prototype.getVertexTemp = function( uuid, type, ns ) {
 		var index = this.vertexTemps.length,
 			name = ns ? ns : 'nVt' + index;
 
-		data = { name : name, type : type };
+		data = { name: name, type: type };
 
 		this.vertexTemps.push( data );
 		this.vertexTemps[ uuid ] = data;
@@ -309,7 +316,7 @@ THREE.NodeMaterial.prototype.getVertexTemp = function( uuid, type, ns ) {
 
 };
 
-THREE.NodeMaterial.prototype.getFragmentTemp = function( uuid, type, ns ) {
+THREE.NodeMaterial.prototype.getFragmentTemp = function ( uuid, type, ns ) {
 
 	var data = this.fragmentTemps[ uuid ];
 
@@ -318,7 +325,7 @@ THREE.NodeMaterial.prototype.getFragmentTemp = function( uuid, type, ns ) {
 		var index = this.fragmentTemps.length,
 			name = ns ? ns : 'nVt' + index;
 
-		data = { name : name, type : type };
+		data = { name: name, type: type };
 
 		this.fragmentTemps.push( data );
 		this.fragmentTemps[ uuid ] = data;
@@ -329,7 +336,7 @@ THREE.NodeMaterial.prototype.getFragmentTemp = function( uuid, type, ns ) {
 
 };
 
-THREE.NodeMaterial.prototype.getVar = function( uuid, type, ns ) {
+THREE.NodeMaterial.prototype.getVar = function ( uuid, type, ns ) {
 
 	var data = this.vars[ uuid ];
 
@@ -338,7 +345,7 @@ THREE.NodeMaterial.prototype.getVar = function( uuid, type, ns ) {
 		var index = this.vars.length,
 			name = ns ? ns : 'nVv' + index;
 
-		data = { name : name, type : type };
+		data = { name: name, type: type };
 
 		this.vars.push( data );
 		this.vars[ uuid ] = data;
@@ -352,7 +359,7 @@ THREE.NodeMaterial.prototype.getVar = function( uuid, type, ns ) {
 
 };
 
-THREE.NodeMaterial.prototype.getAttribute = function( name, type ) {
+THREE.NodeMaterial.prototype.getAttribute = function ( name, type ) {
 
 	if ( ! this.attributes[ name ] ) {
 
@@ -361,7 +368,7 @@ THREE.NodeMaterial.prototype.getAttribute = function( name, type ) {
 		this.addVertexPars( 'attribute ' + type + ' ' + name + ';' );
 		this.addVertexCode( varying.name + ' = ' + name + ';' );
 
-		this.attributes[ name ] = { varying : varying, name : name, type : type };
+		this.attributes[ name ] = { varying: varying, name: name, type: type };
 
 	}
 
@@ -369,7 +376,7 @@ THREE.NodeMaterial.prototype.getAttribute = function( name, type ) {
 
 };
 
-THREE.NodeMaterial.prototype.getIncludes = function() {
+THREE.NodeMaterial.prototype.getIncludes = function () {
 
 	function sortByPosition( a, b ) {
 
@@ -377,7 +384,7 @@ THREE.NodeMaterial.prototype.getIncludes = function() {
 
 	}
 
-	return function( incs ) {
+	return function ( incs ) {
 
 		if ( ! incs ) return '';
 
@@ -391,41 +398,41 @@ THREE.NodeMaterial.prototype.getIncludes = function() {
 
 		return code;
 
-	}
+	};
 
 }();
 
-THREE.NodeMaterial.prototype.addVertexPars = function( code ) {
+THREE.NodeMaterial.prototype.addVertexPars = function ( code ) {
 
 	this.vertexPars += code + '\n';
 
 };
 
-THREE.NodeMaterial.prototype.addFragmentPars = function( code ) {
+THREE.NodeMaterial.prototype.addFragmentPars = function ( code ) {
 
 	this.fragmentPars += code + '\n';
 
 };
 
-THREE.NodeMaterial.prototype.addVertexCode = function( code ) {
+THREE.NodeMaterial.prototype.addVertexCode = function ( code ) {
 
 	this.vertexCode += code + '\n';
 
 };
 
-THREE.NodeMaterial.prototype.addFragmentCode = function( code ) {
+THREE.NodeMaterial.prototype.addFragmentCode = function ( code ) {
 
 	this.fragmentCode += code + '\n';
 
 };
 
-THREE.NodeMaterial.prototype.addVertexNode = function( code ) {
+THREE.NodeMaterial.prototype.addVertexNode = function ( code ) {
 
 	this.vertexNode += code + '\n';
 
 };
 
-THREE.NodeMaterial.prototype.clearVertexNode = function() {
+THREE.NodeMaterial.prototype.clearVertexNode = function () {
 
 	var code = this.vertexNode;
 
@@ -435,13 +442,13 @@ THREE.NodeMaterial.prototype.clearVertexNode = function() {
 
 };
 
-THREE.NodeMaterial.prototype.addFragmentNode = function( code ) {
+THREE.NodeMaterial.prototype.addFragmentNode = function ( code ) {
 
 	this.fragmentNode += code + '\n';
 
 };
 
-THREE.NodeMaterial.prototype.clearFragmentNode = function() {
+THREE.NodeMaterial.prototype.clearFragmentNode = function () {
 
 	var code = this.fragmentNode;
 
@@ -451,7 +458,7 @@ THREE.NodeMaterial.prototype.clearFragmentNode = function() {
 
 };
 
-THREE.NodeMaterial.prototype.getCodePars = function( pars, prefix ) {
+THREE.NodeMaterial.prototype.getCodePars = function ( pars, prefix ) {
 
 	prefix = prefix || '';
 
@@ -477,7 +484,7 @@ THREE.NodeMaterial.prototype.getCodePars = function( pars, prefix ) {
 
 };
 
-THREE.NodeMaterial.prototype.createVertexUniform = function( type, value, ns, needsUpdate ) {
+THREE.NodeMaterial.prototype.createVertexUniform = function ( type, value, ns, needsUpdate ) {
 
 	var uniform = this.createUniform( type, value, ns, needsUpdate );
 
@@ -490,7 +497,7 @@ THREE.NodeMaterial.prototype.createVertexUniform = function( type, value, ns, ne
 
 };
 
-THREE.NodeMaterial.prototype.createFragmentUniform = function( type, value, ns, needsUpdate ) {
+THREE.NodeMaterial.prototype.createFragmentUniform = function ( type, value, ns, needsUpdate ) {
 
 	var uniform = this.createUniform( type, value, ns, needsUpdate );
 
@@ -503,13 +510,13 @@ THREE.NodeMaterial.prototype.createFragmentUniform = function( type, value, ns,
 
 };
 
-THREE.NodeMaterial.prototype.getDataNode = function( uuid ) {
+THREE.NodeMaterial.prototype.getDataNode = function ( uuid ) {
 
 	return this.nodeData[ uuid ] = this.nodeData[ uuid ] || {};
 
 };
 
-THREE.NodeMaterial.prototype.include = function( builder, node, parent, source ) {
+THREE.NodeMaterial.prototype.include = function ( builder, node, parent, source ) {
 
 	var includes;
 
@@ -530,8 +537,8 @@ THREE.NodeMaterial.prototype.include = function( builder, node, parent, source )
 	if ( ! included ) {
 
 		included = includes[ node.name ] = {
-			node : node,
-			deps : []
+			node: node,
+			deps: []
 		};
 
 		includes.push( included );
@@ -565,3 +572,63 @@ THREE.NodeMaterial.prototype.include = function( builder, node, parent, source )
 	}
 
 };
+
+THREE.NodeMaterial.prototype.toJSON = function ( meta ) {
+
+	var isRootObject = ( meta === undefined || typeof meta === 'string' );
+
+	if ( isRootObject ) {
+
+		meta = {
+			nodes: {}
+		};
+
+	}
+
+	if ( meta && ! meta.materials ) meta.materials = {};
+
+	if ( ! meta.materials[ this.uuid ] ) {
+
+		var data = {};
+
+		data.uuid = this.uuid;
+		data.type = this.type;
+
+		meta.materials[ data.uuid ] = data;
+
+		if ( this.name !== "" ) data.name = this.name;
+
+		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.transparent === true ) data.transparent = this.transparent;
+
+		data.depthFunc = this.depthFunc;
+		data.depthTest = this.depthTest;
+		data.depthWrite = this.depthWrite;
+
+		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.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.vertex = this.vertex.toJSON( meta ).uuid;
+		data.fragment = this.fragment.toJSON( meta ).uuid;
+
+	}
+
+	meta.material = this.uuid;
+
+	return meta;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.RawNode = function( value ) {
+THREE.RawNode = function ( value ) {
 
 	THREE.GLNode.call( this, 'v4' );
 
@@ -12,8 +12,9 @@ THREE.RawNode = function( value ) {
 
 THREE.RawNode.prototype = Object.create( THREE.GLNode.prototype );
 THREE.RawNode.prototype.constructor = THREE.RawNode;
+THREE.RawNode.prototype.nodeType = "Raw";
 
-THREE.GLNode.prototype.generate = function( builder ) {
+THREE.RawNode.prototype.generate = function ( builder ) {
 
 	var material = builder.material;
 
@@ -34,3 +35,19 @@ THREE.GLNode.prototype.generate = function( builder ) {
 	return code;
 
 };
+
+THREE.RawNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.value = this.value.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 7 - 7
examples/js/nodes/TempNode.js

@@ -3,7 +3,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.TempNode = function( type, params ) {
+THREE.TempNode = function ( type, params ) {
 
 	THREE.GLNode.call( this, type );
 
@@ -17,7 +17,7 @@ THREE.TempNode = function( type, params ) {
 THREE.TempNode.prototype = Object.create( THREE.GLNode.prototype );
 THREE.TempNode.prototype.constructor = THREE.TempNode;
 
-THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
+THREE.TempNode.prototype.build = function ( builder, output, uuid, ns ) {
 
 	output = output || this.getType( builder );
 
@@ -89,19 +89,19 @@ THREE.TempNode.prototype.build = function( builder, output, uuid, ns ) {
 
 };
 
-THREE.TempNode.prototype.isShared = function( builder, output ) {
+THREE.TempNode.prototype.isShared = function ( builder, output ) {
 
 	return output !== 'sampler2D' && output !== 'samplerCube' && this.shared;
 
 };
 
-THREE.TempNode.prototype.isUnique = function( builder, output ) {
+THREE.TempNode.prototype.isUnique = function ( builder, output ) {
 
 	return this.unique;
 
 };
 
-THREE.TempNode.prototype.getUuid = function( unique ) {
+THREE.TempNode.prototype.getUuid = function ( unique ) {
 
 	var uuid = unique || unique == undefined ? this.constructor.uuid || this.uuid : this.uuid;
 
@@ -111,7 +111,7 @@ THREE.TempNode.prototype.getUuid = function( unique ) {
 
 };
 
-THREE.TempNode.prototype.getTemp = function( builder, uuid ) {
+THREE.TempNode.prototype.getTemp = function ( builder, uuid ) {
 
 	uuid = uuid || this.uuid;
 
@@ -122,7 +122,7 @@ THREE.TempNode.prototype.getTemp = function( builder, uuid ) {
 
 };
 
-THREE.TempNode.prototype.generate = function( builder, output, uuid, type, ns ) {
+THREE.TempNode.prototype.generate = function ( builder, output, uuid, type, ns ) {
 
 	if ( ! this.isShared( builder, output ) ) console.error( "THREE.TempNode is not shared!" );
 

+ 20 - 3
examples/js/nodes/VarNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.VarNode = function( type ) {
+THREE.VarNode = function ( type ) {
 
 	THREE.GLNode.call( this, type );
 
@@ -10,17 +10,34 @@ THREE.VarNode = function( type ) {
 
 THREE.VarNode.prototype = Object.create( THREE.GLNode.prototype );
 THREE.VarNode.prototype.constructor = THREE.VarNode;
+THREE.VarNode.prototype.nodeType = "Var";
 
-THREE.VarNode.prototype.getType = function( builder ) {
+THREE.VarNode.prototype.getType = function ( builder ) {
 
 	return builder.getTypeByFormat( this.type );
 
 };
 
-THREE.VarNode.prototype.generate = function( builder, output ) {
+THREE.VarNode.prototype.generate = function ( builder, output ) {
 
 	var varying = builder.material.getVar( this.uuid, this.type );
 
 	return builder.format( varying.name, this.getType( builder ), output );
 
 };
+
+THREE.VarNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.out = this.type;
+
+	}
+
+	return data;
+
+};

+ 34 - 2
examples/js/nodes/accessors/CameraNode.js

@@ -28,11 +28,12 @@ THREE.CameraNode.TO_VERTEX = 'toVertex';
 
 THREE.CameraNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.CameraNode.prototype.constructor = THREE.CameraNode;
+THREE.CameraNode.prototype.nodeType = "Camera";
 
 THREE.CameraNode.prototype.setCamera = function ( camera ) {
 
 	this.camera = camera;
-	this.requestUpdate = camera !== undefined;
+	this.updateFrame = camera !== undefined ? this.onUpdateFrame : undefined;
 
 };
 
@@ -56,6 +57,7 @@ THREE.CameraNode.prototype.setScope = function ( scope ) {
 		case THREE.CameraNode.DEPTH:
 
 			var camera = this.camera;
+
 			this.near = new THREE.FloatNode( camera ? camera.near : 1 );
 			this.far = new THREE.FloatNode( camera ? camera.far : 1200 );
 
@@ -140,13 +142,14 @@ THREE.CameraNode.prototype.generate = function ( builder, output ) {
 
 };
 
-THREE.CameraNode.prototype.updateFrame = function ( delta ) {
+THREE.CameraNode.prototype.onUpdateFrame = function ( delta ) {
 
 	switch ( this.scope ) {
 
 		case THREE.CameraNode.DEPTH:
 
 			var camera = this.camera;
+
 			this.near.number = camera.near;
 			this.far.number = camera.far;
 
@@ -155,3 +158,32 @@ THREE.CameraNode.prototype.updateFrame = function ( delta ) {
 	}
 
 };
+
+THREE.CameraNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.scope = this.scope;
+
+		if ( this.camera ) data.camera = this.camera.uuid;
+
+		switch ( this.scope ) {
+
+			case THREE.CameraNode.DEPTH:
+
+				data.near = this.near.number;
+				data.far = this.far.number;
+
+				break;
+
+		}
+
+	}
+
+	return data;
+
+};

+ 19 - 3
examples/js/nodes/accessors/ColorsNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ColorsNode = function( index ) {
+THREE.ColorsNode = function ( index ) {
 
 	THREE.TempNode.call( this, 'v4', { shared: false } );
 
@@ -16,12 +16,12 @@ THREE.ColorsNode.fragmentDict = [ 'vColor', 'vColor2' ];
 THREE.ColorsNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.ColorsNode.prototype.constructor = THREE.ColorsNode;
 
-THREE.ColorsNode.prototype.generate = function( builder, output ) {
+THREE.ColorsNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 	var result;
 
-	material.requestAttribs.color[ this.index ] = true;
+	material.requires.color[ this.index ] = true;
 
 	if ( builder.isShader( 'vertex' ) ) result = THREE.ColorsNode.vertexDict[ this.index ];
 	else result = THREE.ColorsNode.fragmentDict[ this.index ];
@@ -29,3 +29,19 @@ THREE.ColorsNode.prototype.generate = function( builder, output ) {
 	return builder.format( result, this.getType( builder ), output );
 
 };
+
+THREE.ColorsNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.index = this.index;
+
+	}
+
+	return data;
+
+};

+ 4 - 3
examples/js/nodes/accessors/LightNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.LightNode = function() {
+THREE.LightNode = function () {
 
 	THREE.TempNode.call( this, 'v3', { shared: false } );
 
@@ -10,12 +10,13 @@ THREE.LightNode = function() {
 
 THREE.LightNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.LightNode.prototype.constructor = THREE.LightNode;
+THREE.LightNode.prototype.nodeType = "Light";
 
-THREE.LightNode.prototype.generate = function( builder, output ) {
+THREE.LightNode.prototype.generate = function ( builder, output ) {
 
 	if ( builder.isCache( 'light' ) ) {
 
-		return builder.format( 'reflectedLight.directDiffuse', this.getType( builder ), output )
+		return builder.format( 'reflectedLight.directDiffuse', this.getType( builder ), output );
 
 	} else {
 

+ 24 - 5
examples/js/nodes/accessors/NormalNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.NormalNode = function( scope ) {
+THREE.NormalNode = function ( scope ) {
 
 	THREE.TempNode.call( this, 'v3' );
 
@@ -16,19 +16,22 @@ THREE.NormalNode.VIEW = 'view';
 
 THREE.NormalNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.NormalNode.prototype.constructor = THREE.NormalNode;
+THREE.NormalNode.prototype.nodeType = "Normal";
 
-THREE.NormalNode.prototype.isShared = function( builder ) {
+THREE.NormalNode.prototype.isShared = function ( builder ) {
 
 	switch ( this.scope ) {
+
 		case THREE.NormalNode.WORLD:
 			return true;
+
 	}
 
 	return false;
 
 };
 
-THREE.NormalNode.prototype.generate = function( builder, output ) {
+THREE.NormalNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 	var result;
@@ -37,7 +40,7 @@ THREE.NormalNode.prototype.generate = function( builder, output ) {
 
 		case THREE.NormalNode.LOCAL:
 
-			material.requestAttribs.normal = true;
+			material.requires.normal = true;
 
 			if ( builder.isShader( 'vertex' ) ) result = 'normal';
 			else result = 'vObjectNormal';
@@ -46,7 +49,7 @@ THREE.NormalNode.prototype.generate = function( builder, output ) {
 
 		case THREE.NormalNode.WORLD:
 
-			material.requestAttribs.worldNormal = true;
+			material.requires.worldNormal = true;
 
 			if ( builder.isShader( 'vertex' ) ) result = '( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz';
 			else result = 'vWNormal';
@@ -64,3 +67,19 @@ THREE.NormalNode.prototype.generate = function( builder, output ) {
 	return builder.format( result, this.getType( builder ), output );
 
 };
+
+THREE.NormalNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.scope = this.scope;
+
+	}
+
+	return data;
+
+};

+ 27 - 6
examples/js/nodes/accessors/PositionNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.PositionNode = function( scope ) {
+THREE.PositionNode = function ( scope ) {
 
 	THREE.TempNode.call( this, 'v3' );
 
@@ -17,31 +17,36 @@ THREE.PositionNode.PROJECTION = 'projection';
 
 THREE.PositionNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.PositionNode.prototype.constructor = THREE.PositionNode;
+THREE.PositionNode.prototype.nodeType = "Position";
 
-THREE.PositionNode.prototype.getType = function( builder ) {
+THREE.PositionNode.prototype.getType = function ( builder ) {
 
 	switch ( this.scope ) {
+
 		case THREE.PositionNode.PROJECTION:
 			return 'v4';
+
 	}
 
 	return this.type;
 
 };
 
-THREE.PositionNode.prototype.isShared = function( builder ) {
+THREE.PositionNode.prototype.isShared = function ( builder ) {
 
 	switch ( this.scope ) {
+
 		case THREE.PositionNode.LOCAL:
 		case THREE.PositionNode.WORLD:
 			return false;
+
 	}
 
 	return true;
 
 };
 
-THREE.PositionNode.prototype.generate = function( builder, output ) {
+THREE.PositionNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 	var result;
@@ -50,7 +55,7 @@ THREE.PositionNode.prototype.generate = function( builder, output ) {
 
 		case THREE.PositionNode.LOCAL:
 
-			material.requestAttribs.position = true;
+			material.requires.position = true;
 
 			if ( builder.isShader( 'vertex' ) ) result = 'transformed';
 			else result = 'vPosition';
@@ -59,7 +64,7 @@ THREE.PositionNode.prototype.generate = function( builder, output ) {
 
 		case THREE.PositionNode.WORLD:
 
-			material.requestAttribs.worldPosition = true;
+			material.requires.worldPosition = true;
 
 			if ( builder.isShader( 'vertex' ) ) result = 'vWPosition';
 			else result = 'vWPosition';
@@ -85,3 +90,19 @@ THREE.PositionNode.prototype.generate = function( builder, output ) {
 	return builder.format( result, this.getType( builder ), output );
 
 };
+
+THREE.PositionNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.scope = this.scope;
+
+	}
+
+	return data;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ReflectNode = function( scope ) {
+THREE.ReflectNode = function ( scope ) {
 
 	THREE.TempNode.call( this, 'v3', { unique: true } );
 
@@ -16,19 +16,22 @@ THREE.ReflectNode.VECTOR = 'vector';
 
 THREE.ReflectNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.ReflectNode.prototype.constructor = THREE.ReflectNode;
+THREE.ReflectNode.prototype.nodeType = "Reflect";
 
-THREE.ReflectNode.prototype.getType = function( builder ) {
+THREE.ReflectNode.prototype.getType = function ( builder ) {
 
 	switch ( this.scope ) {
+
 		case THREE.ReflectNode.SPHERE:
 			return 'v2';
+
 	}
 
 	return this.type;
 
 };
 
-THREE.ReflectNode.prototype.generate = function( builder, output ) {
+THREE.ReflectNode.prototype.generate = function ( builder, output ) {
 
 	var result;
 
@@ -61,8 +64,25 @@ THREE.ReflectNode.prototype.generate = function( builder, output ) {
 			result = 'reflectSphereVec';
 
 			break;
+
 	}
 
 	return builder.format( result, this.getType( this.type ), output );
 
 };
+
+THREE.ReflectNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.scope = this.scope;
+
+	}
+
+	return data;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ScreenUVNode = function( resolution ) {
+THREE.ScreenUVNode = function ( resolution ) {
 
 	THREE.TempNode.call( this, 'v2' );
 
@@ -12,8 +12,9 @@ THREE.ScreenUVNode = function( resolution ) {
 
 THREE.ScreenUVNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.ScreenUVNode.prototype.constructor = THREE.ScreenUVNode;
+THREE.ScreenUVNode.prototype.nodeType = "ScreenUV";
 
-THREE.ScreenUVNode.prototype.generate = function( builder, output ) {
+THREE.ScreenUVNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 	var result;
@@ -33,3 +34,19 @@ THREE.ScreenUVNode.prototype.generate = function( builder, output ) {
 	return builder.format( result, this.getType( builder ), output );
 
 };
+
+THREE.ScreenUVNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.resolution = this.resolution.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 20 - 3
examples/js/nodes/accessors/UVNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.UVNode = function( index ) {
+THREE.UVNode = function ( index ) {
 
 	THREE.TempNode.call( this, 'v2', { shared: false } );
 
@@ -15,13 +15,14 @@ THREE.UVNode.fragmentDict = [ 'vUv', 'vUv2' ];
 
 THREE.UVNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.UVNode.prototype.constructor = THREE.UVNode;
+THREE.UVNode.prototype.nodeType = "UV";
 
-THREE.UVNode.prototype.generate = function( builder, output ) {
+THREE.UVNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 	var result;
 
-	material.requestAttribs.uv[ this.index ] = true;
+	material.requires.uv[ this.index ] = true;
 
 	if ( builder.isShader( 'vertex' ) ) result = THREE.UVNode.vertexDict[ this.index ];
 	else result = THREE.UVNode.fragmentDict[ this.index ];
@@ -29,3 +30,19 @@ THREE.UVNode.prototype.generate = function( builder, output ) {
 	return builder.format( result, this.getType( builder ), output );
 
 };
+
+THREE.UVNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.index = this.index;
+
+	}
+
+	return data;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ColorNode = function( color ) {
+THREE.ColorNode = function ( color ) {
 
 	THREE.InputNode.call( this, 'c' );
 
@@ -12,5 +12,24 @@ THREE.ColorNode = function( color ) {
 
 THREE.ColorNode.prototype = Object.create( THREE.InputNode.prototype );
 THREE.ColorNode.prototype.constructor = THREE.ColorNode;
+THREE.ColorNode.prototype.nodeType = "Color";
 
 THREE.NodeMaterial.addShortcuts( THREE.ColorNode.prototype, 'value', [ 'r', 'g', 'b' ] );
+
+THREE.ColorNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.r = this.r;
+		data.g = this.g;
+		data.b = this.b;
+
+	}
+
+	return data;
+
+};

+ 24 - 4
examples/js/nodes/inputs/CubeTextureNode.js

@@ -2,9 +2,9 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.CubeTextureNode = function( value, coord, bias ) {
+THREE.CubeTextureNode = function ( value, coord, bias ) {
 
-	THREE.InputNode.call( this, 'v4', { shared : true } );
+	THREE.InputNode.call( this, 'v4', { shared: true } );
 
 	this.value = value;
 	this.coord = coord || new THREE.ReflectNode();
@@ -14,14 +14,15 @@ THREE.CubeTextureNode = function( value, coord, bias ) {
 
 THREE.CubeTextureNode.prototype = Object.create( THREE.InputNode.prototype );
 THREE.CubeTextureNode.prototype.constructor = THREE.CubeTextureNode;
+THREE.CubeTextureNode.prototype.nodeType = "CubeTexture";
 
-THREE.CubeTextureNode.prototype.getTexture = function( builder, output ) {
+THREE.CubeTextureNode.prototype.getTexture = function ( builder, output ) {
 
 	return THREE.InputNode.prototype.generate.call( this, builder, output, this.value.uuid, 't' );
 
 };
 
-THREE.CubeTextureNode.prototype.generate = function( builder, output ) {
+THREE.CubeTextureNode.prototype.generate = function ( builder, output ) {
 
 	if ( output === 'samplerCube' ) {
 
@@ -61,3 +62,22 @@ THREE.CubeTextureNode.prototype.generate = function( builder, output ) {
 	return builder.format( code, this.type, output );
 
 };
+
+THREE.CubeTextureNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.value = this.value.uuid;
+		data.coord = this.coord.toJSON( meta ).uuid;
+
+		if ( this.bias ) data.bias = this.bias.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 20 - 3
examples/js/nodes/inputs/FloatNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.FloatNode = function( value ) {
+THREE.FloatNode = function ( value ) {
 
 	THREE.InputNode.call( this, 'fv1' );
 
@@ -12,18 +12,35 @@ THREE.FloatNode = function( value ) {
 
 THREE.FloatNode.prototype = Object.create( THREE.InputNode.prototype );
 THREE.FloatNode.prototype.constructor = THREE.FloatNode;
+THREE.FloatNode.prototype.nodeType = "Float";
 
 Object.defineProperties( THREE.FloatNode.prototype, {
 	number: {
-		get: function() {
+		get: function () {
 
 			return this.value[ 0 ];
 
 		},
-		set: function( val ) {
+		set: function ( val ) {
 
 			this.value[ 0 ] = val;
 
 		}
 	}
 } );
+
+THREE.FloatNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.number = this.number;
+
+	}
+
+	return data;
+
+};

+ 20 - 3
examples/js/nodes/inputs/IntNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.IntNode = function( value ) {
+THREE.IntNode = function ( value ) {
 
 	THREE.InputNode.call( this, 'iv1' );
 
@@ -12,18 +12,35 @@ THREE.IntNode = function( value ) {
 
 THREE.IntNode.prototype = Object.create( THREE.InputNode.prototype );
 THREE.IntNode.prototype.constructor = THREE.IntNode;
+THREE.IntNode.prototype.nodeType = "Int";
 
 Object.defineProperties( THREE.IntNode.prototype, {
 	number: {
-		get: function() {
+		get: function () {
 
 			return this.value[ 0 ];
 
 		},
-		set: function( val ) {
+		set: function ( val ) {
 
 			this.value[ 0 ] = Math.floor( val );
 
 		}
 	}
 } );
+
+THREE.IntNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.number = this.number;
+
+	}
+
+	return data;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.Matrix4Node = function( matrix ) {
+THREE.Matrix4Node = function ( matrix ) {
 
 	THREE.InputNode.call( this, 'm4' );
 
@@ -12,3 +12,20 @@ THREE.Matrix4Node = function( matrix ) {
 
 THREE.Matrix4Node.prototype = Object.create( THREE.InputNode.prototype );
 THREE.Matrix4Node.prototype.constructor = THREE.Matrix4Node;
+THREE.Matrix4Node.prototype.nodeType = "Matrix4";
+
+THREE.Matrix4Node.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.elements = this.value.elements.concat();
+
+	}
+
+	return data;
+
+};

+ 30 - 5
examples/js/nodes/inputs/ReflectorNode.js

@@ -1,7 +1,17 @@
-THREE.ReflectorNode = function( mirror, camera, options ) {
+THREE.ReflectorNode = function ( mirror ) {
 
 	THREE.TempNode.call( this, 'v4' );
 
+	if ( mirror ) this.setMirror( mirror );
+
+};
+
+THREE.ReflectorNode.prototype = Object.create( THREE.TempNode.prototype );
+THREE.ReflectorNode.prototype.constructor = THREE.ReflectorNode;
+THREE.ReflectorNode.prototype.nodeType = "Reflector";
+
+THREE.ReflectorNode.prototype.setMirror = function ( mirror ) {
+
 	this.mirror = mirror;
 
 	this.textureMatrix = new THREE.Matrix4Node( this.mirror.material.uniforms.textureMatrix.value );
@@ -15,10 +25,7 @@ THREE.ReflectorNode = function( mirror, camera, options ) {
 
 };
 
-THREE.ReflectorNode.prototype = Object.create( THREE.TempNode.prototype );
-THREE.ReflectorNode.prototype.constructor = THREE.ReflectorNode;
-
-THREE.ReflectorNode.prototype.generate = function( builder, output ) {
+THREE.ReflectorNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
@@ -44,3 +51,21 @@ THREE.ReflectorNode.prototype.generate = function( builder, output ) {
 	}
 
 };
+
+THREE.ReflectorNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.mirror = this.mirror.uuid;
+
+		if ( this.offset ) data.offset = this.offset.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 4 - 3
examples/js/nodes/inputs/ScreenNode.js

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

+ 26 - 4
examples/js/nodes/inputs/TextureNode.js

@@ -2,9 +2,9 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.TextureNode = function( value, coord, bias, project ) {
+THREE.TextureNode = function ( value, coord, bias, project ) {
 
-	THREE.InputNode.call( this, 'v4', { shared : true } );
+	THREE.InputNode.call( this, 'v4', { shared: true } );
 
 	this.value = value;
 	this.coord = coord || new THREE.UVNode();
@@ -15,14 +15,15 @@ THREE.TextureNode = function( value, coord, bias, project ) {
 
 THREE.TextureNode.prototype = Object.create( THREE.InputNode.prototype );
 THREE.TextureNode.prototype.constructor = THREE.TextureNode;
+THREE.TextureNode.prototype.nodeType = "Texture";
 
-THREE.TextureNode.prototype.getTexture = function( builder, output ) {
+THREE.TextureNode.prototype.getTexture = function ( builder, output ) {
 
 	return THREE.InputNode.prototype.generate.call( this, builder, output, this.value.uuid, 't' );
 
 };
 
-THREE.TextureNode.prototype.generate = function( builder, output ) {
+THREE.TextureNode.prototype.generate = function ( builder, output ) {
 
 	if ( output === 'sampler2D' ) {
 
@@ -65,3 +66,24 @@ THREE.TextureNode.prototype.generate = function( builder, output ) {
 	return builder.format( code, this.type, output );
 
 };
+
+THREE.TextureNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		if ( this.value ) data.value = this.value.uuid;
+
+		data.coord = this.coord.toJSON( meta ).uuid;
+		data.project = this.project;
+
+		if ( this.bias ) data.bias = this.bias.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.Vector2Node = function( x, y ) {
+THREE.Vector2Node = function ( x, y ) {
 
 	THREE.InputNode.call( this, 'v2' );
 
@@ -12,5 +12,23 @@ THREE.Vector2Node = function( x, y ) {
 
 THREE.Vector2Node.prototype = Object.create( THREE.InputNode.prototype );
 THREE.Vector2Node.prototype.constructor = THREE.Vector2Node;
+THREE.Vector2Node.prototype.nodeType = "Vector2";
 
 THREE.NodeMaterial.addShortcuts( THREE.Vector2Node.prototype, 'value', [ 'x', 'y' ] );
+
+THREE.Vector2Node.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.x = this.x;
+		data.y = this.y;
+
+	}
+
+	return data;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.Vector3Node = function( x, y, z ) {
+THREE.Vector3Node = function ( x, y, z ) {
 
 	THREE.InputNode.call( this, 'v3' );
 
@@ -13,5 +13,24 @@ THREE.Vector3Node = function( x, y, z ) {
 
 THREE.Vector3Node.prototype = Object.create( THREE.InputNode.prototype );
 THREE.Vector3Node.prototype.constructor = THREE.Vector3Node;
+THREE.Vector3Node.prototype.nodeType = "Vector3";
 
 THREE.NodeMaterial.addShortcuts( THREE.Vector3Node.prototype, 'value', [ 'x', 'y', 'z' ] );
+
+THREE.Vector3Node.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.x = this.x;
+		data.y = this.y;
+		data.z = this.z;
+
+	}
+
+	return data;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.Vector4Node = function( x, y, z, w ) {
+THREE.Vector4Node = function ( x, y, z, w ) {
 
 	THREE.InputNode.call( this, 'v4' );
 
@@ -12,5 +12,25 @@ THREE.Vector4Node = function( x, y, z, w ) {
 
 THREE.Vector4Node.prototype = Object.create( THREE.InputNode.prototype );
 THREE.Vector4Node.prototype.constructor = THREE.Vector4Node;
+THREE.Vector4Node.prototype.nodeType = "Vector4";
 
 THREE.NodeMaterial.addShortcuts( THREE.Vector4Node.prototype, 'value', [ 'x', 'y', 'z', 'w' ] );
+
+THREE.Vector4Node.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.x = this.x;
+		data.y = this.y;
+		data.z = this.z;
+		data.w = this.w;
+
+	}
+
+	return data;
+
+};

+ 43 - 2
examples/js/nodes/materials/PhongNode.js

@@ -14,6 +14,7 @@ THREE.PhongNode = function () {
 
 THREE.PhongNode.prototype = Object.create( THREE.GLNode.prototype );
 THREE.PhongNode.prototype.constructor = THREE.PhongNode;
+THREE.PhongNode.prototype.nodeType = "Phong";
 
 THREE.PhongNode.prototype.build = function ( builder ) {
 
@@ -23,7 +24,7 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 	material.define( 'PHONG' );
 	material.define( 'ALPHATEST', '0.0' );
 
-	material.requestAttribs.light = true;
+	material.requires.lights = true;
 
 	if ( builder.isShader( 'vertex' ) ) {
 
@@ -137,7 +138,7 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 		var environment = this.environment ? this.environment.buildCode( builder, 'c', { slot: 'environment' } ) : undefined;
 		var environmentAlpha = this.environmentAlpha && this.environment ? this.environmentAlpha.buildCode( builder, 'fv1' ) : undefined;
 
-		material.requestAttribs.transparent = alpha != undefined;
+		material.requires.transparent = alpha != undefined;
 
 		material.addFragmentPars( [
 			"#include <common>",
@@ -308,3 +309,43 @@ THREE.PhongNode.prototype.build = function ( builder ) {
 	return code;
 
 };
+
+
+THREE.PhongNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		// vertex
+
+		if ( this.transform ) data.transform = this.transform.toJSON( meta ).uuid;
+
+		// fragment
+
+		data.color = this.color.toJSON( meta ).uuid;
+		data.specular = this.specular.toJSON( meta ).uuid;
+		data.shininess = this.shininess.toJSON( meta ).uuid;
+
+		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;
+
+		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.environment ) data.environment = this.environment.toJSON( meta ).uuid;
+		if ( this.environmentAlpha ) data.environmentAlpha = this.environmentAlpha.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

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

@@ -2,16 +2,18 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.PhongNodeMaterial = function() {
+THREE.PhongNodeMaterial = function () {
 
 	this.node = new THREE.PhongNode();
 
 	THREE.NodeMaterial.call( this, this.node, this.node );
 
+	this.type = "PhongNodeMaterial";
+
 };
 
 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' ] );
+	[ 'color', 'alpha', 'specular', 'shininess', 'normal', 'normalScale', 'emissive', 'ambient', 'light', 'shadow', 'ao', 'environment', 'environmentAlpha', 'transform' ] );

+ 28 - 2
examples/js/nodes/materials/SpriteNode.js

@@ -13,6 +13,7 @@ THREE.SpriteNode = function () {
 
 THREE.SpriteNode.prototype = Object.create( THREE.GLNode.prototype );
 THREE.SpriteNode.prototype.constructor = THREE.SpriteNode;
+THREE.SpriteNode.prototype.nodeType = "Sprite";
 
 THREE.SpriteNode.prototype.build = function ( builder ) {
 
@@ -21,8 +22,8 @@ THREE.SpriteNode.prototype.build = function ( builder ) {
 
 	material.define( 'SPRITE' );
 
-	material.requestAttribs.light = false;
-	material.requestAttribs.transparent = this.alpha != undefined;
+	material.requires.lights = false;
+	material.requires.transparent = this.alpha != undefined;
 
 	if ( builder.isShader( 'vertex' ) ) {
 
@@ -137,3 +138,28 @@ THREE.SpriteNode.prototype.build = function ( builder ) {
 	return output.join( "\n" );
 
 };
+
+THREE.SpriteNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		// vertex
+
+		if ( this.transform ) data.transform = this.transform.toJSON( meta ).uuid;
+
+		// 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;
+
+	}
+
+	return data;
+
+};

+ 3 - 1
examples/js/nodes/materials/SpriteNodeMaterial.js

@@ -8,10 +8,12 @@ THREE.SpriteNodeMaterial = function () {
 
 	THREE.NodeMaterial.call( this, this.node, this.node );
 
+	this.type = "SpriteNodeMaterial";
+
 };
 
 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' ] );
+	[ 'color', 'alpha', 'transform', 'spherical' ] );

+ 48 - 4
examples/js/nodes/materials/StandardNode.js

@@ -14,6 +14,7 @@ THREE.StandardNode = function () {
 
 THREE.StandardNode.prototype = Object.create( THREE.GLNode.prototype );
 THREE.StandardNode.prototype.constructor = THREE.StandardNode;
+THREE.StandardNode.prototype.nodeType = "Standard";
 
 THREE.StandardNode.prototype.build = function ( builder ) {
 
@@ -26,7 +27,7 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 
 	material.define( 'ALPHATEST', '0.0' );
 
-	material.requestAttribs.light = true;
+	material.requires.lights = true;
 
 	material.extensions.shaderTextureLOD = true;
 
@@ -166,7 +167,7 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 
 		var clearCoatEnv = useClearCoat && environment ? this.environment.buildCode( builder, 'c', { cache: 'clearCoat', requires: requires, slot: 'environment' } ) : undefined;
 
-		material.requestAttribs.transparent = alpha != undefined;
+		material.requires.transparent = alpha != undefined;
 
 		material.addFragmentPars( [
 
@@ -189,10 +190,10 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 		].join( "\n" ) );
 
 		var output = [
-				// prevent undeclared normal
+			// prevent undeclared normal
 			"	#include <normal_fragment>",
 
-				// prevent undeclared material
+			// prevent undeclared material
 			"	PhysicalMaterial material;",
 			"	material.diffuseColor = vec3( 1.0 );",
 
@@ -393,3 +394,46 @@ THREE.StandardNode.prototype.build = function ( builder ) {
 	return code;
 
 };
+
+THREE.StandardNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		// vertex
+
+		if ( this.transform ) data.transform = this.transform.toJSON( meta ).uuid;
+
+		// fragment
+
+		data.color = this.color.toJSON( meta ).uuid;
+		data.roughness = this.roughness.toJSON( meta ).uuid;
+		data.metalness = this.metalness.toJSON( meta ).uuid;
+
+		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;
+
+		if ( this.reflectivity ) data.reflectivity = this.reflectivity.toJSON( meta ).uuid;
+
+		if ( this.light ) data.light = this.light.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.environment ) data.environment = this.environment.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

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

@@ -2,16 +2,18 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.StandardNodeMaterial = function() {
+THREE.StandardNodeMaterial = function () {
 
 	this.node = new THREE.StandardNode();
 
 	THREE.NodeMaterial.call( this, this.node, this.node );
 
+	this.type = "StandardNodeMaterial";
+
 };
 
 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' ] );
+	[ 'color', 'alpha', 'roughness', 'metalness', 'reflectivity', 'clearCoat', 'clearCoatRoughness', 'normal', 'normalScale', 'emissive', 'ambient', 'light', 'shadow', 'ao', 'environment', 'transform' ] );

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.Math1Node = function( a, method ) {
+THREE.Math1Node = function ( a, method ) {
 
 	THREE.TempNode.call( this );
 
@@ -39,19 +39,22 @@ THREE.Math1Node.INVERT = 'invert';
 
 THREE.Math1Node.prototype = Object.create( THREE.TempNode.prototype );
 THREE.Math1Node.prototype.constructor = THREE.Math1Node;
+THREE.Math1Node.prototype.nodeType = "Math1";
 
-THREE.Math1Node.prototype.getType = function( builder ) {
+THREE.Math1Node.prototype.getType = function ( builder ) {
 
 	switch ( this.method ) {
+
 		case THREE.Math1Node.LENGTH:
 			return 'fv1';
+
 	}
 
 	return this.a.getType( builder );
 
 };
 
-THREE.Math1Node.prototype.generate = function( builder, output ) {
+THREE.Math1Node.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
@@ -72,8 +75,26 @@ THREE.Math1Node.prototype.generate = function( builder, output ) {
 		default:
 			result = this.method + '(' + result + ')';
 			break;
+
 	}
 
 	return builder.format( result, type, output );
 
 };
+
+THREE.Math1Node.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.a = this.a.toJSON( meta ).uuid;
+		data.method = this.method;
+
+	}
+
+	return data;
+
+};

+ 26 - 4
examples/js/nodes/math/Math2Node.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.Math2Node = function( a, b, method ) {
+THREE.Math2Node = function ( a, b, method ) {
 
 	THREE.TempNode.call( this );
 
@@ -25,8 +25,9 @@ THREE.Math2Node.POW = 'pow';
 
 THREE.Math2Node.prototype = Object.create( THREE.TempNode.prototype );
 THREE.Math2Node.prototype.constructor = THREE.Math2Node;
+THREE.Math2Node.prototype.nodeType = "Math2";
 
-THREE.Math2Node.prototype.getInputType = function( builder ) {
+THREE.Math2Node.prototype.getInputType = function ( builder ) {
 
 	// use the greater length vector
 	if ( builder.getFormatLength( this.b.getType( builder ) ) > builder.getFormatLength( this.a.getType( builder ) ) ) {
@@ -39,22 +40,24 @@ THREE.Math2Node.prototype.getInputType = function( builder ) {
 
 };
 
-THREE.Math2Node.prototype.getType = function( builder ) {
+THREE.Math2Node.prototype.getType = function ( builder ) {
 
 	switch ( this.method ) {
+
 		case THREE.Math2Node.DISTANCE:
 		case THREE.Math2Node.DOT:
 			return 'fv1';
 
 		case THREE.Math2Node.CROSS:
 			return 'v3';
+
 	}
 
 	return this.getInputType( builder );
 
 };
 
-THREE.Math2Node.prototype.generate = function( builder, output ) {
+THREE.Math2Node.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
@@ -67,6 +70,7 @@ THREE.Math2Node.prototype.generate = function( builder, output ) {
 	// optimzer
 
 	switch ( this.method ) {
+
 		case THREE.Math2Node.CROSS:
 			a = this.a.build( builder, 'v3' );
 			b = this.b.build( builder, 'v3' );
@@ -94,3 +98,21 @@ THREE.Math2Node.prototype.generate = function( builder, output ) {
 	return builder.format( this.method + '(' + a + ',' + b + ')', this.getType( builder ), output );
 
 };
+
+THREE.Math2Node.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.a = this.a.toJSON( meta ).uuid;
+		data.b = this.b.toJSON( meta ).uuid;
+		data.method = this.method;
+
+	}
+
+	return data;
+
+};

+ 24 - 3
examples/js/nodes/math/Math3Node.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.Math3Node = function( a, b, c, method ) {
+THREE.Math3Node = function ( a, b, c, method ) {
 
 	THREE.TempNode.call( this );
 
@@ -21,8 +21,9 @@ THREE.Math3Node.FACEFORWARD = 'faceforward';
 
 THREE.Math3Node.prototype = Object.create( THREE.TempNode.prototype );
 THREE.Math3Node.prototype.constructor = THREE.Math3Node;
+THREE.Math3Node.prototype.nodeType = "Math3";
 
-THREE.Math3Node.prototype.getType = function( builder ) {
+THREE.Math3Node.prototype.getType = function ( builder ) {
 
 	var a = builder.getFormatLength( this.a.getType( builder ) );
 	var b = builder.getFormatLength( this.b.getType( builder ) );
@@ -35,7 +36,7 @@ THREE.Math3Node.prototype.getType = function( builder ) {
 
 };
 
-THREE.Math3Node.prototype.generate = function( builder, output ) {
+THREE.Math3Node.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
@@ -49,6 +50,7 @@ THREE.Math3Node.prototype.generate = function( builder, output ) {
 	// optimzer
 
 	switch ( this.method ) {
+
 		case THREE.Math3Node.REFRACT:
 			a = this.a.build( builder, type );
 			b = this.b.build( builder, type );
@@ -72,3 +74,22 @@ THREE.Math3Node.prototype.generate = function( builder, output ) {
 	return builder.format( this.method + '(' + a + ',' + b + ',' + c + ')', type, output );
 
 };
+
+THREE.Math3Node.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.a = this.a.toJSON( meta ).uuid;
+		data.b = this.b.toJSON( meta ).uuid;
+		data.c = this.c.toJSON( meta ).uuid;
+		data.method = this.method;
+
+	}
+
+	return data;
+
+};

+ 22 - 3
examples/js/nodes/math/OperatorNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.OperatorNode = function( a, b, op ) {
+THREE.OperatorNode = function ( a, b, op ) {
 
 	THREE.TempNode.call( this );
 
@@ -19,8 +19,9 @@ THREE.OperatorNode.DIV = '/';
 
 THREE.OperatorNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.OperatorNode.prototype.constructor = THREE.OperatorNode;
+THREE.OperatorNode.prototype.nodeType = "Operator";
 
-THREE.OperatorNode.prototype.getType = function( builder ) {
+THREE.OperatorNode.prototype.getType = function ( builder ) {
 
 	var a = this.a.getType( builder );
 	var b = this.b.getType( builder );
@@ -41,7 +42,7 @@ THREE.OperatorNode.prototype.getType = function( builder ) {
 
 };
 
-THREE.OperatorNode.prototype.generate = function( builder, output ) {
+THREE.OperatorNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material,
 		data = material.getDataNode( this.uuid );
@@ -54,3 +55,21 @@ THREE.OperatorNode.prototype.generate = function( builder, output ) {
 	return builder.format( '(' + a + this.op + b + ')', type, output );
 
 };
+
+THREE.OperatorNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.a = this.a.toJSON( meta ).uuid;
+		data.b = this.b.toJSON( meta ).uuid;
+		data.op = this.op;
+
+	}
+
+	return data;
+
+};

+ 44 - 2
examples/js/nodes/postprocessing/NodePass.js

@@ -2,10 +2,15 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.NodePass = function() {
+THREE.NodePass = function () {
 
 	THREE.ShaderPass.call( this );
 
+	this.name = "";
+	this.uuid = THREE.Math.generateUUID();
+
+	this.userData = {};
+
 	this.textureID = 'renderTexture';
 
 	this.fragment = new THREE.RawNode( new THREE.ScreenNode() );
@@ -22,7 +27,7 @@ THREE.NodePass.prototype.constructor = THREE.NodePass;
 
 THREE.NodeMaterial.addShortcuts( THREE.NodePass.prototype, 'fragment', [ 'value' ] );
 
-THREE.NodePass.prototype.build = function() {
+THREE.NodePass.prototype.build = function () {
 
 	this.node.build();
 
@@ -30,3 +35,40 @@ THREE.NodePass.prototype.build = function() {
 	this.material = this.node;
 
 };
+
+THREE.NodePass.prototype.toJSON = function ( meta ) {
+
+	var isRootObject = ( meta === undefined || typeof meta === 'string' );
+
+	if ( isRootObject ) {
+
+		meta = {
+			nodes: {}
+		};
+
+	}
+
+	if ( meta && ! meta.passes ) meta.passes = {};
+
+	if ( ! meta.passes[ this.uuid ] ) {
+
+		var data = {};
+
+		data.uuid = this.uuid;
+		data.type = "NodePass";
+
+		meta.passes[ this.uuid ] = data;
+
+		if ( this.name !== "" ) data.name = this.name;
+
+		if ( JSON.stringify( this.userData ) !== '{}' ) data.userData = this.userData;
+
+		data.value = this.value.toJSON( meta ).uuid;
+
+	}
+
+	meta.pass = this.uuid;
+
+	return meta;
+
+};

+ 53 - 31
examples/js/nodes/utils/BlurNode.js

@@ -2,12 +2,10 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.BlurNode = function( value, coord, radius, size ) {
+THREE.BlurNode = function ( value, coord, radius, size ) {
 
 	THREE.TempNode.call( this, 'v4' );
 
-	this.requestUpdate = true;
-
 	this.value = value;
 	this.coord = coord || new THREE.UVNode();
 	this.radius = new THREE.Vector2Node( 1, 1 );
@@ -22,41 +20,42 @@ THREE.BlurNode = function( value, coord, radius, size ) {
 };
 
 THREE.BlurNode.fBlurX = new THREE.FunctionNode( [
-"vec4 blurX( sampler2D texture, vec2 uv, float s ) {",
-"	vec4 sum = vec4( 0.0 );",
-"	sum += texture2D( texture, vec2( uv.x - 4.0 * s, uv.y ) ) * 0.051;",
-"	sum += texture2D( texture, vec2( uv.x - 3.0 * s, uv.y ) ) * 0.0918;",
-"	sum += texture2D( texture, vec2( uv.x - 2.0 * s, uv.y ) ) * 0.12245;",
-"	sum += texture2D( texture, vec2( uv.x - 1.0 * s, uv.y ) ) * 0.1531;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y ) ) * 0.1633;",
-"	sum += texture2D( texture, vec2( uv.x + 1.0 * s, uv.y ) ) * 0.1531;",
-"	sum += texture2D( texture, vec2( uv.x + 2.0 * s, uv.y ) ) * 0.12245;",
-"	sum += texture2D( texture, vec2( uv.x + 3.0 * s, uv.y ) ) * 0.0918;",
-"	sum += texture2D( texture, vec2( uv.x + 4.0 * s, uv.y ) ) * 0.051;",
-"	return sum;",
-"}"
+	"vec4 blurX( sampler2D texture, vec2 uv, float s ) {",
+	"	vec4 sum = vec4( 0.0 );",
+	"	sum += texture2D( texture, vec2( uv.x - 4.0 * s, uv.y ) ) * 0.051;",
+	"	sum += texture2D( texture, vec2( uv.x - 3.0 * s, uv.y ) ) * 0.0918;",
+	"	sum += texture2D( texture, vec2( uv.x - 2.0 * s, uv.y ) ) * 0.12245;",
+	"	sum += texture2D( texture, vec2( uv.x - 1.0 * s, uv.y ) ) * 0.1531;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y ) ) * 0.1633;",
+	"	sum += texture2D( texture, vec2( uv.x + 1.0 * s, uv.y ) ) * 0.1531;",
+	"	sum += texture2D( texture, vec2( uv.x + 2.0 * s, uv.y ) ) * 0.12245;",
+	"	sum += texture2D( texture, vec2( uv.x + 3.0 * s, uv.y ) ) * 0.0918;",
+	"	sum += texture2D( texture, vec2( uv.x + 4.0 * s, uv.y ) ) * 0.051;",
+	"	return sum;",
+	"}"
 ].join( "\n" ) );
 
 THREE.BlurNode.fBlurY = new THREE.FunctionNode( [
-"vec4 blurY( sampler2D texture, vec2 uv, float s ) {",
-"	vec4 sum = vec4( 0.0 );",
-"	sum += texture2D( texture, vec2( uv.x, uv.y - 4.0 * s ) ) * 0.051;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y - 3.0 * s ) ) * 0.0918;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y - 2.0 * s ) ) * 0.12245;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y - 1.0 * s ) ) * 0.1531;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y ) ) * 0.1633;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y + 1.0 * s ) ) * 0.1531;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y + 2.0 * s ) ) * 0.12245;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y + 3.0 * s ) ) * 0.0918;",
-"	sum += texture2D( texture, vec2( uv.x, uv.y + 4.0 * s ) ) * 0.051;",
-"	return sum;",
-"}"
+	"vec4 blurY( sampler2D texture, vec2 uv, float s ) {",
+	"	vec4 sum = vec4( 0.0 );",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y - 4.0 * s ) ) * 0.051;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y - 3.0 * s ) ) * 0.0918;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y - 2.0 * s ) ) * 0.12245;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y - 1.0 * s ) ) * 0.1531;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y ) ) * 0.1633;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y + 1.0 * s ) ) * 0.1531;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y + 2.0 * s ) ) * 0.12245;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y + 3.0 * s ) ) * 0.0918;",
+	"	sum += texture2D( texture, vec2( uv.x, uv.y + 4.0 * s ) ) * 0.051;",
+	"	return sum;",
+	"}"
 ].join( "\n" ) );
 
 THREE.BlurNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.BlurNode.prototype.constructor = THREE.BlurNode;
+THREE.BlurNode.prototype.nodeType = "Blur";
 
-THREE.BlurNode.prototype.updateFrame = function( delta ) {
+THREE.BlurNode.prototype.updateFrame = function ( delta ) {
 
 	if ( this.size ) {
 
@@ -74,7 +73,7 @@ THREE.BlurNode.prototype.updateFrame = function( delta ) {
 
 };
 
-THREE.BlurNode.prototype.generate = function( builder, output ) {
+THREE.BlurNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material, blurX = THREE.BlurNode.fBlurX, blurY = THREE.BlurNode.fBlurY;
 
@@ -112,3 +111,26 @@ THREE.BlurNode.prototype.generate = function( builder, output ) {
 	}
 
 };
+
+THREE.BlurNode.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.radius = this.radius.toJSON( meta ).uuid;
+
+		if ( this.size ) data.size = { x: this.size.x, y: this.size.y };
+
+		data.blurX = this.blurX;
+		data.blurY = this.blurY;
+
+	}
+
+	return data;
+
+};

+ 29 - 10
examples/js/nodes/utils/BumpNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.BumpNode = function( value, coord, scale ) {
+THREE.BumpNode = function ( value, coord, scale ) {
 
 	THREE.TempNode.call( this, 'v3' );
 
@@ -13,20 +13,21 @@ THREE.BumpNode = function( value, coord, scale ) {
 };
 
 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 );",
-"}"
+	"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 ) {
+THREE.BumpNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material, func = THREE.BumpNode.fBumpToNormal;
 
@@ -47,3 +48,21 @@ THREE.BumpNode.prototype.generate = function( 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;
+
+};

+ 21 - 2
examples/js/nodes/utils/ColorAdjustmentNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ColorAdjustmentNode = function( rgb, adjustment, method ) {
+THREE.ColorAdjustmentNode = function ( rgb, adjustment, method ) {
 
 	THREE.TempNode.call( this, 'v3' );
 
@@ -21,8 +21,9 @@ THREE.ColorAdjustmentNode.CONTRAST = 'contrast';
 
 THREE.ColorAdjustmentNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.ColorAdjustmentNode.prototype.constructor = THREE.ColorAdjustmentNode;
+THREE.ColorAdjustmentNode.prototype.nodeType = "ColorAdjustment";
 
-THREE.ColorAdjustmentNode.prototype.generate = function( builder, output ) {
+THREE.ColorAdjustmentNode.prototype.generate = function ( builder, output ) {
 
 	var rgb = this.rgb.build( builder, 'v3' );
 	var adjustment = this.adjustment.build( builder, 'fv1' );
@@ -68,3 +69,21 @@ THREE.ColorAdjustmentNode.prototype.generate = function( builder, output ) {
 	return builder.format( name + '(' + rgb + ',' + adjustment + ')', this.getType( builder ), output );
 
 };
+
+THREE.ColorAdjustmentNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.rgb = this.rgb.toJSON( meta ).uuid;
+		data.adjustment = this.adjustment.toJSON( meta ).uuid;
+		data.method = this.method;
+
+	}
+
+	return data;
+
+};

+ 37 - 4
examples/js/nodes/utils/JoinNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.JoinNode = function( x, y, z, w ) {
+THREE.JoinNode = function ( x, y, z, w ) {
 
 	THREE.TempNode.call( this, 'fv1' );
 
@@ -17,8 +17,9 @@ THREE.JoinNode.inputs = [ 'x', 'y', 'z', 'w' ];
 
 THREE.JoinNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.JoinNode.prototype.constructor = THREE.JoinNode;
+THREE.JoinNode.prototype.nodeType = "Join";
 
-THREE.JoinNode.prototype.getNumElements = function() {
+THREE.JoinNode.prototype.getNumElements = function () {
 
 	var inputs = THREE.JoinNode.inputs;
 	var i = inputs.length;
@@ -38,13 +39,13 @@ THREE.JoinNode.prototype.getNumElements = function() {
 
 };
 
-THREE.JoinNode.prototype.getType = function( builder ) {
+THREE.JoinNode.prototype.getType = function ( builder ) {
 
 	return builder.getFormatFromLength( this.getNumElements() );
 
 };
 
-THREE.JoinNode.prototype.generate = function( builder, output ) {
+THREE.JoinNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
@@ -67,3 +68,35 @@ THREE.JoinNode.prototype.generate = function( builder, output ) {
 	return builder.format( code, type, output );
 
 };
+
+THREE.JoinNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.inputs = {};
+
+		var length = this.getNumElements();
+		var inputs = THREE.JoinNode.inputs;
+
+		for ( var i = 0; i < length; i ++ ) {
+
+			var elm = this[ inputs[ i ] ];
+
+			if ( elm ) {
+
+				data.inputs[ inputs[ i ] ] = elm.toJSON( meta ).uuid;
+
+			}
+
+		}
+
+
+	}
+
+	return data;
+
+};

+ 19 - 2
examples/js/nodes/utils/LuminanceNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.LuminanceNode = function( rgb ) {
+THREE.LuminanceNode = function ( rgb ) {
 
 	THREE.TempNode.call( this, 'fv1' );
 
@@ -12,11 +12,28 @@ THREE.LuminanceNode = function( rgb ) {
 
 THREE.LuminanceNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.LuminanceNode.prototype.constructor = THREE.LuminanceNode;
+THREE.LuminanceNode.prototype.nodeType = "Luminance";
 
-THREE.LuminanceNode.prototype.generate = function( builder, output ) {
+THREE.LuminanceNode.prototype.generate = function ( builder, output ) {
 
 	builder.include( 'luminance_rgb' );
 
 	return builder.format( 'luminance_rgb(' + this.rgb.build( builder, 'v3' ) + ')', this.getType( builder ), output );
 
 };
+
+THREE.LuminanceNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.rgb = this.rgb.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 19 - 2
examples/js/nodes/utils/NoiseNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.NoiseNode = function( coord ) {
+THREE.NoiseNode = function ( coord ) {
 
 	THREE.TempNode.call( this, 'fv1' );
 
@@ -12,11 +12,28 @@ THREE.NoiseNode = function( coord ) {
 
 THREE.NoiseNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.NoiseNode.prototype.constructor = THREE.NoiseNode;
+THREE.NoiseNode.prototype.nodeType = "Noise";
 
-THREE.NoiseNode.prototype.generate = function( builder, output ) {
+THREE.NoiseNode.prototype.generate = function ( builder, output ) {
 
 	builder.include( 'snoise' );
 
 	return builder.format( 'snoise(' + this.coord.build( builder, 'v2' ) + ')', this.getType( builder ), output );
 
 };
+
+THREE.NoiseNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.coord = this.coord.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

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

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.NormalMapNode = function( value, uv, scale, normal, position ) {
+THREE.NormalMapNode = function ( value, uv, scale, normal, position ) {
 
 	THREE.TempNode.call( this, 'v3' );
 
@@ -16,8 +16,9 @@ THREE.NormalMapNode = function( value, uv, scale, normal, position ) {
 
 THREE.NormalMapNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.NormalMapNode.prototype.constructor = THREE.NormalMapNode;
+THREE.NormalMapNode.prototype.nodeType = "NormalMap";
 
-THREE.NormalMapNode.prototype.generate = function( builder, output ) {
+THREE.NormalMapNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
@@ -40,3 +41,23 @@ THREE.NormalMapNode.prototype.generate = function( builder, output ) {
 	}
 
 };
+
+THREE.NormalMapNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.value = this.value.uuid;
+		data.scale = this.scale.toJSON( meta ).uuid;
+
+		data.normal = this.normal.toJSON( meta ).uuid;
+		data.position = this.position.toJSON( meta ).uuid;
+
+	}
+
+	return data;
+
+};

+ 19 - 4
examples/js/nodes/utils/ResolutionNode.js

@@ -2,20 +2,19 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.ResolutionNode = function( renderer ) {
+THREE.ResolutionNode = function ( renderer ) {
 
 	THREE.Vector2Node.call( this );
 
-	this.requestUpdate = true;
-
 	this.renderer = renderer;
 
 };
 
 THREE.ResolutionNode.prototype = Object.create( THREE.Vector2Node.prototype );
 THREE.ResolutionNode.prototype.constructor = THREE.ResolutionNode;
+THREE.ResolutionNode.prototype.nodeType = "Resolution";
 
-THREE.ResolutionNode.prototype.updateFrame = function( delta ) {
+THREE.ResolutionNode.prototype.updateFrame = function ( delta ) {
 
 	var size = this.renderer.getSize(),
 		pixelRatio = this.renderer.getPixelRatio();
@@ -24,3 +23,19 @@ THREE.ResolutionNode.prototype.updateFrame = function( delta ) {
 	this.y = size.height * pixelRatio;
 
 };
+
+THREE.ResolutionNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.renderer = this.renderer.uuid;
+
+	}
+
+	return data;
+
+};

+ 12 - 11
examples/js/nodes/utils/RoughnessToBlinnExponentNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.RoughnessToBlinnExponentNode = function() {
+THREE.RoughnessToBlinnExponentNode = function () {
 
 	THREE.TempNode.call( this, 'fv1' );
 
@@ -10,22 +10,23 @@ THREE.RoughnessToBlinnExponentNode = function() {
 
 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 ) {",
+	"float getSpecularMIPLevel( const in float blinnShininessExponent, const in int maxMIPLevel ) {",
 
-	//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 );",
+	//	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.
-	"return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );",
-"}"
+	"	return clamp( desiredMIPLevel, 0.0, maxMIPLevelScalar );",
+	"}"
 ].join( "\n" ) );
 
 THREE.RoughnessToBlinnExponentNode.prototype = Object.create( THREE.TempNode.prototype );
 THREE.RoughnessToBlinnExponentNode.prototype.constructor = THREE.RoughnessToBlinnExponentNode;
+THREE.RoughnessToBlinnExponentNode.prototype.nodeType = "RoughnessToBlinnExponent";
 
-THREE.RoughnessToBlinnExponentNode.prototype.generate = function( builder, output ) {
+THREE.RoughnessToBlinnExponentNode.prototype.generate = function ( builder, output ) {
 
 	var material = builder.material;
 
@@ -40,9 +41,9 @@ THREE.RoughnessToBlinnExponentNode.prototype.generate = function( builder, outpu
 				return builder.format( 'getSpecularMIPLevel( Material_ClearCoat_BlinnShininessExponent( material ), 8 )', this.type, output );
 
 			} else {
-				
+
 				return builder.format( 'getSpecularMIPLevel( Material_BlinnShininessExponent( material ), 8 )', this.type, output );
-				
+
 			}
 
 		} else {

+ 22 - 4
examples/js/nodes/utils/SwitchNode.js

@@ -2,7 +2,7 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.SwitchNode = function( node, components ) {
+THREE.SwitchNode = function ( node, components ) {
 
 	THREE.GLNode.call( this );
 
@@ -13,14 +13,15 @@ THREE.SwitchNode = function( node, components ) {
 
 THREE.SwitchNode.prototype = Object.create( THREE.GLNode.prototype );
 THREE.SwitchNode.prototype.constructor = THREE.SwitchNode;
+THREE.SwitchNode.prototype.nodeType = "Switch";
 
-THREE.SwitchNode.prototype.getType = function( builder ) {
+THREE.SwitchNode.prototype.getType = function ( builder ) {
 
 	return builder.getFormatFromLength( this.components.length );
 
 };
 
-THREE.SwitchNode.prototype.generate = function( builder, output ) {
+THREE.SwitchNode.prototype.generate = function ( builder, output ) {
 
 	var type = this.node.getType( builder );
 	var inputLength = builder.getFormatLength( type ) - 1;
@@ -65,8 +66,25 @@ THREE.SwitchNode.prototype.generate = function( builder, output ) {
 
 		// join
 
-		return builder.format( node, type, output )
+		return builder.format( node, type, output );
 
 	}
 
 };
+
+THREE.SwitchNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.node = this.node.toJSON( meta ).uuid;
+		data.components = this.components;
+
+	}
+
+	return data;
+
+};

+ 19 - 4
examples/js/nodes/utils/TimerNode.js

@@ -2,21 +2,36 @@
  * @author sunag / http://www.sunag.com.br/
  */
 
-THREE.TimerNode = function( value, scale ) {
+THREE.TimerNode = function ( value, scale ) {
 
 	THREE.FloatNode.call( this, value );
 
-	this.requestUpdate = true;
-
 	this.scale = scale !== undefined ? scale : 1;
 
 };
 
 THREE.TimerNode.prototype = Object.create( THREE.FloatNode.prototype );
 THREE.TimerNode.prototype.constructor = THREE.TimerNode;
+THREE.TimerNode.prototype.nodeType = "Timer";
 
-THREE.TimerNode.prototype.updateFrame = function( delta ) {
+THREE.TimerNode.prototype.updateFrame = function ( delta ) {
 
 	this.number += delta * this.scale;
 
 };
+
+THREE.TimerNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.scale = this.scale;
+
+	}
+
+	return data;
+
+};

+ 18 - 0
examples/js/nodes/utils/UVTransformNode.js

@@ -13,6 +13,7 @@ THREE.UVTransformNode = function () {
 
 THREE.UVTransformNode.prototype = Object.create( THREE.FunctionNode.prototype );
 THREE.UVTransformNode.prototype.constructor = THREE.UVTransformNode;
+THREE.UVTransformNode.prototype.nodeType = "UVTransform";
 
 THREE.UVTransformNode.prototype.generate = function ( builder, output ) {
 
@@ -46,3 +47,20 @@ THREE.UVTransformNode.prototype.compose = function () {
 	};
 
 }();
+
+THREE.UVTransformNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		data.uv = this.uv.toJSON( meta ).uuid;
+		data.elements = this.transform.value.elements.concat();
+
+	}
+
+	return data;
+
+};

+ 82 - 11
examples/js/nodes/utils/VelocityNode.js

@@ -6,14 +6,38 @@ THREE.VelocityNode = function ( target, params ) {
 
 	THREE.Vector3Node.call( this );
 
-	this.requestUpdate = true;
+	this.params = {};
 
-	this.target = target;
-	this.params = params || {};
-
-	this.position = this.target.position.clone();
 	this.velocity = new THREE.Vector3();
 
+	this.setTarget( target );
+	this.setParams( params );
+
+};
+
+THREE.VelocityNode.prototype = Object.create( THREE.Vector3Node.prototype );
+THREE.VelocityNode.prototype.constructor = THREE.VelocityNode;
+THREE.VelocityNode.prototype.nodeType = "Velocity";
+
+THREE.VelocityNode.prototype.setParams = function ( params ) {
+
+	switch ( this.params.type ) {
+
+		case "elastic":
+
+			delete this.moment;
+
+			delete this.speed;
+			delete this.springVelocity;
+
+			delete this.lastVelocity;
+
+			break;
+
+	}
+
+	this.params = params || {};
+
 	switch ( this.params.type ) {
 
 		case "elastic":
@@ -31,26 +55,54 @@ THREE.VelocityNode = function ( target, params ) {
 
 };
 
-THREE.VelocityNode.prototype = Object.create( THREE.Vector3Node.prototype );
-THREE.VelocityNode.prototype.constructor = THREE.VelocityNode;
+THREE.VelocityNode.prototype.setTarget = function ( target ) {
+
+	if ( this.target ) {
+
+		delete this.position;
+		delete this.oldPosition;
+
+	}
+
+	this.target = target;
+
+	if ( target ) {
+
+		this.position = target.getWorldPosition();
+		this.oldPosition = this.position.clone();
+
+	}
+
+};
+
+THREE.VelocityNode.prototype.updateFrameVelocity = function ( delta ) {
+
+	if ( this.target ) {
+
+		this.position = this.target.getWorldPosition();
+		this.velocity.subVectors( this.position, this.oldPosition );
+		this.oldPosition.copy( this.position );
+
+	}
+
+};
 
 THREE.VelocityNode.prototype.updateFrame = function ( delta ) {
 
-	this.velocity.subVectors( this.target.position, this.position );
-	this.position.copy( this.target.position );
+	this.updateFrameVelocity( delta );
 
 	switch ( this.params.type ) {
 
 		case "elastic":
 
 			// convert to real scale: 0 at 1 values
-			var deltaFps = delta * (this.params.fps || 60);
+			var deltaFps = delta * ( this.params.fps || 60 );
 
 			var spring = Math.pow( this.params.spring, deltaFps ),
 				damping = Math.pow( this.params.damping, deltaFps );
 
 			// fix relative frame-rate
-			this.velocity.multiplyScalar( Math.exp( -this.params.damping * deltaFps ) );
+			this.velocity.multiplyScalar( Math.exp( - this.params.damping * deltaFps ) );
 
 			// elastic
 			this.velocity.add( this.springVelocity );
@@ -81,3 +133,22 @@ THREE.VelocityNode.prototype.updateFrame = function ( delta ) {
 	}
 
 };
+
+THREE.VelocityNode.prototype.toJSON = function ( meta ) {
+
+	var data = this.getJSONNode( meta );
+
+	if ( ! data ) {
+
+		data = this.createJSONNode( meta );
+
+		if ( this.target ) data.target = this.target.uuid;
+
+		// clone params
+		data.params = JSON.parse( JSON.stringify( this.params ) );
+
+	}
+
+	return data;
+
+};

文件差异内容过多而无法显示
+ 0 - 0
examples/nodes/caustic.json


文件差异内容过多而无法显示
+ 0 - 0
examples/nodes/displace.json


文件差异内容过多而无法显示
+ 0 - 0
examples/nodes/wave.json


文件差异内容过多而无法显示
+ 0 - 0
examples/nodes/xray.json


+ 285 - 0
examples/webgl_loader_nodes.html

@@ -0,0 +1,285 @@
+<!DOCTYPE html>
+<html lang="en">
+	<head>
+		<title>three.js webgl - node material</title>
+		<meta charset="utf-8">
+		<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
+		<style>
+			body {
+				color: #fff;
+				font-family:Monospace;
+				font-size:13px;
+				margin: 0px;
+				text-align:center;
+				overflow: hidden;
+			}
+
+			#info {
+				color: #fff;
+				position: absolute;
+				top: 10px;
+				width: 100%;
+				text-align: center;
+				display:block;
+			}
+
+			a { color: white }
+		</style>
+	</head>
+	<body>
+
+		<div id="container"></div>
+		<div id="info">
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - Node-Based Material</br>
+			Serialized using <a href="webgl_materials_nodes.html">webgl_materials_nodes.html</a>
+		</div>
+
+		<script src="../build/three.js"></script>
+
+		<script src='js/geometries/TeapotBufferGeometry.js'></script>
+		<script src="js/controls/OrbitControls.js"></script>
+		<script src="js/libs/dat.gui.min.js"></script>
+
+		<!-- NodeLibrary -->
+		<script src="js/nodes/GLNode.js"></script>
+		<script src="js/nodes/RawNode.js"></script>
+		<script src="js/nodes/TempNode.js"></script>
+		<script src="js/nodes/InputNode.js"></script>
+		<script src="js/nodes/ConstNode.js"></script>
+		<script src="js/nodes/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/NodeBuilder.js"></script>
+		<script src="js/nodes/NodeLib.js"></script>
+		<script src="js/nodes/NodeMaterial.js"></script>
+
+		<!-- Accessors -->
+		<script src="js/nodes/accessors/PositionNode.js"></script>
+		<script src="js/nodes/accessors/NormalNode.js"></script>
+		<script src="js/nodes/accessors/UVNode.js"></script>
+		<script src="js/nodes/accessors/ScreenUVNode.js"></script>
+		<script src="js/nodes/accessors/ColorsNode.js"></script>
+		<script src="js/nodes/accessors/CameraNode.js"></script>
+		<script src="js/nodes/accessors/ReflectNode.js"></script>
+		<script src="js/nodes/accessors/LightNode.js"></script>
+
+		<!-- Inputs -->
+		<script src="js/nodes/inputs/IntNode.js"></script>
+		<script src="js/nodes/inputs/FloatNode.js"></script>
+		<script src="js/nodes/inputs/ColorNode.js"></script>
+		<script src="js/nodes/inputs/Vector2Node.js"></script>
+		<script src="js/nodes/inputs/Vector3Node.js"></script>
+		<script src="js/nodes/inputs/Vector4Node.js"></script>
+		<script src="js/nodes/inputs/TextureNode.js"></script>
+		<script src="js/nodes/inputs/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>
+		<script src="js/nodes/math/Math3Node.js"></script>
+		<script src="js/nodes/math/OperatorNode.js"></script>
+
+		<!-- Utils -->
+		<script src="js/nodes/utils/SwitchNode.js"></script>
+		<script src="js/nodes/utils/JoinNode.js"></script>
+		<script src="js/nodes/utils/TimerNode.js"></script>
+		<script src="js/nodes/utils/RoughnessToBlinnExponentNode.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/BlurNode.js"></script>
+		<script src="js/nodes/utils/UVTransformNode.js"></script>
+
+		<!-- Phong Material -->
+		<script src="js/nodes/materials/PhongNode.js"></script>
+		<script src="js/nodes/materials/PhongNodeMaterial.js"></script>
+
+		<!-- Standard Material -->
+		<script src="js/nodes/materials/StandardNode.js"></script>
+		<script src="js/nodes/materials/StandardNodeMaterial.js"></script>
+
+		<!-- NodeMaterial Loader -->
+		<script src="js/loaders/NodeMaterialLoader.js"></script>
+
+		<script>
+
+		var container = document.getElementById( 'container' );
+
+		var renderer, scene, camera, clock = new THREE.Clock(), fov = 50;
+		var teapot, mesh, cloud;
+		var controls;
+		var gui;
+
+		var param = { load: 'caustic' };
+
+		window.addEventListener( 'load', init );
+
+		function init() {
+
+			renderer = new THREE.WebGLRenderer( { antialias: true } );
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( window.innerWidth, window.innerHeight );
+			container.appendChild( renderer.domElement );
+
+			scene = new THREE.Scene();
+
+			camera = new THREE.PerspectiveCamera( fov, window.innerWidth / window.innerHeight, 1, 1000 );
+			camera.position.x = 50;
+			camera.position.z = - 50;
+			camera.position.y = 30;
+			camera.target = new THREE.Vector3();
+
+			cloud = new THREE.TextureLoader().load( 'textures/lava/cloud.png' );
+			cloud.wrapS = cloud.wrapT = THREE.RepeatWrapping;
+
+			controls = new THREE.OrbitControls( camera, renderer.domElement );
+			controls.minDistance = 50;
+			controls.maxDistance = 200;
+
+			scene.add( new THREE.AmbientLight( 0x464646 ) );
+
+			var light = new THREE.DirectionalLight( 0xffddcc, 1 );
+			light.position.set( 1, 0.75, 0.5 );
+			scene.add( light );
+
+			var light = new THREE.DirectionalLight( 0xccccff, 1 );
+			light.position.set( - 1, 0.75, - 0.5 );
+			scene.add( light );
+
+			teapot = new THREE.TeapotBufferGeometry( 15, 18 );
+
+			mesh = new THREE.Mesh( teapot );
+			scene.add( mesh );
+
+			window.addEventListener( 'resize', onWindowResize, false );
+
+			updateMaterial();
+
+			onWindowResize();
+			animate();
+
+		}
+
+		function clearGui() {
+
+			if ( gui ) gui.destroy();
+
+			gui = new dat.GUI();
+
+			var example = gui.add( param, 'load', {
+				'caustic': 'caustic',
+				'displace': 'displace',
+				'wave': 'wave',
+				'xray': 'xray'
+			} ).onFinishChange( function () {
+
+				updateMaterial();
+
+			} );
+
+			gui.open();
+
+		}
+
+		function addGui( name, value, callback, isColor, min, max ) {
+
+			var node;
+
+			param[ name ] = value;
+
+			if ( isColor ) {
+
+				node = gui.addColor( param, name ).onChange( function () {
+
+					callback( param[ name ] );
+
+				} );
+
+			} else if ( typeof value == 'object' ) {
+
+				node = gui.add( param, name, value ).onChange( function () {
+
+					callback( param[ name ] );
+
+				} );
+
+			} else {
+
+				node = gui.add( param, name, min, max ).onChange( function () {
+
+					callback( param[ name ] );
+
+				} );
+
+			}
+
+			return node;
+
+		}
+
+		
+		function updateMaterial() {
+
+			if ( mesh.material ) mesh.material.dispose();
+
+			clearGui();
+
+			var url = "nodes/" + param.load + ".json";
+
+			var library = {
+				"cloud" : cloud
+			};
+
+			var loader = new THREE.NodeMaterialLoader( undefined, library ).load( url, function () {
+
+				var time = loader.getObjectByName("time");
+			
+				if (time) {
+				
+					addGui( 'timeScale', time.scale, function ( val ) {
+
+						time.scale = val;
+
+					}, false, -2, 2 );
+				
+				}
+			
+				// set material
+				mesh.material = loader.material;
+
+			} );
+
+		}
+
+		function onWindowResize() {
+
+			var width = window.innerWidth, height = window.innerHeight;
+
+			camera.aspect = width / height;
+			camera.updateProjectionMatrix();
+
+			renderer.setSize( width, height );
+
+		}
+
+		function animate() {
+
+			var delta = clock.getDelta();
+
+			// update material animation and/or gpu calcs (pre-renderer)
+			if ( mesh.material instanceof THREE.NodeMaterial ) mesh.material.updateFrame( delta );
+
+			renderer.render( scene, camera );
+
+			requestAnimationFrame( animate );
+
+		}
+
+		</script>
+	</body>
+</html>

+ 131 - 49
examples/webgl_materials_nodes.html

@@ -30,7 +30,8 @@
 
 		<div id="container"></div>
 		<div id="info">
-			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - Node-Based Material
+			<a href="http://threejs.org" target="_blank" rel="noopener">three.js</a> - Node-Based Material</br>
+			<a id="serialize" onclick="toggleSerialize()" href="javascript:void(0);">Serialize and apply</a>
 		</div>
 
 		<script src="../build/three.js"></script>
@@ -102,6 +103,9 @@
 		<script src="js/nodes/materials/StandardNode.js"></script>
 		<script src="js/nodes/materials/StandardNodeMaterial.js"></script>
 
+		<!-- NodeMaterial Loader -->
+		<script src="js/loaders/NodeMaterialLoader.js"></script>
+
 		<script>
 
 		var container = document.getElementById( 'container' );
@@ -112,6 +116,8 @@
 		var move = false;
 		var rtTexture, rtMaterial;
 		var gui, guiElements = [];
+		var library = {};
+		var serialized = false;
 		var textures = {
 			brick: { url: 'textures/brick_diffuse.jpg' },
 			grass: { url: 'textures/terrain/grasslight-big.jpg' },
@@ -132,6 +138,8 @@
 				texture = textures[ name ].texture = new THREE.TextureLoader().load( textures[ name ].url );
 				texture.wrapS = texture.wrapT = THREE.RepeatWrapping;
 
+				library[ texture.uuid ] = texture;
+
 			}
 
 			return texture;
@@ -151,6 +159,8 @@
 			var textureCube = new THREE.CubeTextureLoader().load( urls );
 			textureCube.format = THREE.RGBFormat;
 
+			library[ textureCube.uuid ] = textureCube;
+
 			return textureCube;
 
 		}();
@@ -162,6 +172,7 @@
 			renderer = new THREE.WebGLRenderer( { antialias: true } );
 			renderer.setPixelRatio( window.devicePixelRatio );
 			renderer.setSize( window.innerWidth, window.innerHeight );
+			renderer.uuid = THREE.Math.generateUUID(); // generate to library
 			container.appendChild( renderer.domElement );
 
 			scene = new THREE.Scene();
@@ -172,6 +183,7 @@
 			camera.position.y = 30;
 			camera.target = new THREE.Vector3();
 
+
 			controls = new THREE.OrbitControls( camera, renderer.domElement );
 			controls.minDistance = 50;
 			controls.maxDistance = 200;
@@ -191,6 +203,10 @@
 			mesh = new THREE.Mesh( teapot );
 			scene.add( mesh );
 
+			library[ renderer.uuid ] = renderer;
+			library[ camera.uuid ] = camera;
+			library[ mesh.uuid ] = mesh;
+
 			window.addEventListener( 'resize', onWindowResize, false );
 
 			updateMaterial();
@@ -292,6 +308,8 @@
 
 			if ( rtTexture ) {
 
+				delete library[ rtTexture.uuid ];
+
 				rtTexture.dispose();
 				rtTexture = null;
 
@@ -412,7 +430,7 @@
 
 					addGui( 'roughnessA', roughnessA.number, function ( val ) {
 
-						 roughnessA.number = val;
+						roughnessA.number = val;
 
 					}, false, 0, 1 );
 
@@ -424,7 +442,7 @@
 
 					addGui( 'roughnessB', roughnessB.number, function ( val ) {
 
-						 roughnessB.number = val;
+						roughnessB.number = val;
 
 					}, false, 0, 1 );
 
@@ -444,26 +462,26 @@
 
 				case 'physical':
 
-						// MATERIAL
+					// MATERIAL
 
 					mtl = new THREE.StandardNodeMaterial();
 
-						//mtl.color = // albedo (vec3)
-						//mtl.alpha = // opacity (float)
-						//mtl.roughness = // roughness (float)
-						//mtl.metalness = // metalness (float)
-						//mtl.reflectivity = // reflectivity (float)
-						//mtl.clearCoat = // clearCoat (float)
-						//mtl.clearCoatRoughness = // clearCoatRoughness (float)
-						//mtl.normal = // normalmap (vec3)
-						//mtl.normalScale = // normalmap scale (vec2)
-						//mtl.emissive = // emissive color (vec3)
-						//mtl.ambient = // ambient color (vec3)
-						//mtl.shadow = // shadowmap (vec3)
-						//mtl.light = // custom-light (vec3)
-						//mtl.ao = // ambient occlusion (float)
-						//mtl.environment = // reflection/refraction (vec3)
-						//mtl.transform = // vertex transformation (vec3)
+					//mtl.color = // albedo (vec3)
+					//mtl.alpha = // opacity (float)
+					//mtl.roughness = // roughness (float)
+					//mtl.metalness = // metalness (float)
+					//mtl.reflectivity = // reflectivity (float)
+					//mtl.clearCoat = // clearCoat (float)
+					//mtl.clearCoatRoughness = // clearCoatRoughness (float)
+					//mtl.normal = // normalmap (vec3)
+					//mtl.normalScale = // normalmap scale (vec2)
+					//mtl.emissive = // emissive color (vec3)
+					//mtl.ambient = // ambient color (vec3)
+					//mtl.shadow = // shadowmap (vec3)
+					//mtl.light = // custom-light (vec3)
+					//mtl.ao = // ambient occlusion (float)
+					//mtl.environment = // reflection/refraction (vec3)
+					//mtl.transform = // vertex transformation (vec3)
 
 					var mask = new THREE.SwitchNode( new THREE.TextureNode( getTexture( "decalDiffuse" ) ), 'w' );
 
@@ -480,24 +498,24 @@
 					var clearCoatRoughness = new THREE.FloatNode( 1 );
 
 					var roughness = new THREE.Math3Node(
-							roughnessA,
-							roughnessB,
-							mask,
-							THREE.Math3Node.MIX
-						);
+						roughnessA,
+						roughnessB,
+						mask,
+						THREE.Math3Node.MIX
+					);
 
 					var metalness = new THREE.Math3Node(
-							metalnessA,
-							metalnessB,
-							mask,
-							THREE.Math3Node.MIX
-						);
+						metalnessA,
+						metalnessB,
+						mask,
+						THREE.Math3Node.MIX
+					);
 
 					var normalMask = new THREE.OperatorNode(
-							new THREE.Math1Node( mask, THREE.Math1Node.INVERT ),
-							normalScale,
-							THREE.OperatorNode.MUL
-						);
+						new THREE.Math1Node( mask, THREE.Math1Node.INVERT ),
+						normalScale,
+						THREE.OperatorNode.MUL
+					);
 
 					mtl.color = new THREE.ColorNode( 0xEEEEEE );
 					mtl.roughness = roughness;
@@ -509,7 +527,7 @@
 					mtl.normal = new THREE.TextureNode( getTexture( "grassNormal" ) );
 					mtl.normalScale = normalMask;
 
-						// GUI
+					// GUI
 
 					addGui( 'color', mtl.color.value.getHex(), function ( val ) {
 
@@ -519,25 +537,25 @@
 
 					addGui( 'reflectivity', reflectivity.number, function ( val ) {
 
-							 reflectivity.number = val;
+						reflectivity.number = val;
 
 					}, false, 0, 1 );
 
 					addGui( 'clearCoat', clearCoat.number, function ( val ) {
 
-							 clearCoat.number = val;
+						clearCoat.number = val;
 
 					}, false, 0, 1 );
 
 					addGui( 'clearCoatRoughness', clearCoatRoughness.number, function ( val ) {
 
-							 clearCoatRoughness.number = val;
+						clearCoatRoughness.number = val;
 
 					}, false, 0, 1 );
 
 					addGui( 'roughnessA', roughnessA.number, function ( val ) {
 
-							 roughnessA.number = val;
+						roughnessA.number = val;
 
 					}, false, 0, 1 );
 
@@ -549,7 +567,7 @@
 
 					addGui( 'roughnessB', roughnessB.number, function ( val ) {
 
-							 roughnessB.number = val;
+						roughnessB.number = val;
 
 					}, false, 0, 1 );
 
@@ -580,6 +598,10 @@
 					var colorA = new THREE.ColorNode( 0xFFFFFF );
 					var colorB = new THREE.ColorNode( 0x0054df );
 
+					// used for serialization only
+					time.name = "time";
+					speed.name = "speed";
+
 					var timeScale = new THREE.OperatorNode(
 						time,
 						speed,
@@ -1078,7 +1100,7 @@
 
 					var satrgb = new THREE.FunctionNode( [
 						"vec3 satrgb(vec3 rgb, float adjustment) {",
-					//"	const vec3 W = vec3(0.2125, 0.7154, 0.0721);", // LUMA
+						//"	const vec3 W = vec3(0.2125, 0.7154, 0.0721);", // LUMA
 						"	vec3 intensity = vec3(dot(rgb, LUMA));",
 						"	return mix(intensity, rgb, adjustment);",
 						"}"
@@ -1166,6 +1188,10 @@
 					var colorA = new THREE.ColorNode( 0xFFFFFF );
 					var colorB = new THREE.ColorNode( 0x0054df );
 
+					// used for serialization only
+					time.name = "time";
+					speed.name = "speed";
+
 					var uv = new THREE.UVNode();
 
 					var timeScl = new THREE.OperatorNode(
@@ -1357,7 +1383,7 @@
 					].join( "\n" ) );
 
 					var voronoi = new THREE.FunctionNode( [
-					// Based off of iq's described here: http://www.iquilezles.org/www/articles/voronoili
+						// Based off of iq's described here: http://www.iquilezles.org/www/articles/voronoili
 						"float voronoi(vec2 p, in float time) {",
 						"	vec2 n = floor(p);",
 						"	vec2 f = fract(p);",
@@ -1381,7 +1407,7 @@
 					].join( "\n" ), [ hash2 ] ); // define hash2 as dependencies
 
 					var voronoiLayers = new THREE.FunctionNode( [
-					// based on https://www.shadertoy.com/view/4tXSDf
+						// based on https://www.shadertoy.com/view/4tXSDf
 						"float voronoiLayers(vec2 p, in float time) {",
 						"	float v = 0.0;",
 						"	float a = 0.4;",
@@ -1397,6 +1423,10 @@
 					var time = new THREE.TimerNode();
 					var timeScale = new THREE.FloatNode( 2 );
 
+					// used for serialization only
+					time.name = "time";
+					timeScale.name = "speed";
+
 					var alpha = new THREE.FloatNode( 1 );
 					var scale = new THREE.FloatNode( .1 );
 					var intensity = new THREE.FloatNode( 1.5 );
@@ -1928,7 +1958,7 @@
 					mtl = new THREE.PhongNodeMaterial();
 
 					var keywordsexample = new THREE.FunctionNode( [
-					// use "uv" reserved keyword
+						// use "uv" reserved keyword
 						"vec4 keywordsexample( sampler2D texture ) {",
 						"	return texture2D( texture, myUV ) + vec4( position * myAlpha, 0.0 );",
 						"}"
@@ -1967,10 +1997,10 @@
 
 					var setMyVar = new THREE.FunctionNode( [
 						"float 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
+						// 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!
+						// it is not accept "void" functions for now!
 						"	return 0.;",
 						"}"
 					].join( "\n" ) );
@@ -1996,11 +2026,11 @@
 					var alpha = new THREE.FloatNode( 1 );
 
 					var blurtexture = new THREE.FunctionNode( [
-					// Reference: TriangleBlurShader.js
+						// Reference: TriangleBlurShader.js
 						"vec4 blurtexture(sampler2D texture, vec2 uv, vec2 delta) {",
 						"	vec4 color = vec4( 0.0 );",
 						"	float total = 0.0;",
-					// randomize the lookup values to hide the fixed number of samples
+						// randomize the lookup values to hide the fixed number of samples
 						"	float offset = rand( uv );",
 						"	for ( float t = -BLUR_ITERATIONS; t <= BLUR_ITERATIONS; t ++ ) {",
 						"		float percent = ( t + offset - 0.5 ) / BLUR_ITERATIONS;",
@@ -2136,6 +2166,8 @@
 
 					rtTexture = new THREE.WebGLRenderTarget( window.innerWidth, window.innerHeight, { minFilter: THREE.LinearFilter, magFilter: THREE.NearestFilter, format: THREE.RGBFormat } );
 
+					library[ rtTexture.uuid ] = rtTexture;
+
 					var distanceMtl = new THREE.PhongNodeMaterial();
 					distanceMtl.environment = objectDepth;
 					distanceMtl.side = THREE.BackSide;
@@ -2269,6 +2301,56 @@
 
 		}
 
+		function toggleSerialize() {
+
+			if ( serialized ) reset();
+			else serialize();
+
+			serialized = ! serialized;
+
+		}
+
+		function reset() {
+
+			updateMaterial();
+
+			// gui
+
+			var div = document.getElementById( 'serialize' );
+			div.textContent = "Serialize and apply";
+
+		}
+
+		function serialize() {
+
+			var json = mesh.material.toJSON(),
+				loader = new THREE.NodeMaterialLoader( null, library ),
+				material = loader.parse( json );
+
+			mesh.material = material;
+
+			// 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 );
+
+			console.log( jsonStr );
+
+			// gui
+
+			var div = document.getElementById( 'serialize' );
+			div.textContent = "Click to reset - JSON Generate: " + ( jsonStr.length / 1024 ).toFixed( 3 ) + "kB";
+
+			if ( gui ) gui.destroy();
+
+			gui = null;
+
+		}
+
 		function animate() {
 
 			var delta = clock.getDelta();

+ 206 - 187
examples/webgl_mirror_nodes.html

@@ -96,223 +96,242 @@
 		<script src="js/nodes/materials/PhongNode.js"></script>
 		<script src="js/nodes/materials/PhongNodeMaterial.js"></script>
 
+		<!-- NodeMaterial Loader -->
+		<script src="js/loaders/NodeMaterialLoader.js"></script>
+
 		<script>
 
-			// scene size
-			var WIDTH = window.innerWidth;
-			var HEIGHT = window.innerHeight;
+		// scene size
+		var WIDTH = window.innerWidth;
+		var HEIGHT = window.innerHeight;
+
+		// camera
+		var VIEW_ANGLE = 45;
+		var ASPECT = WIDTH / HEIGHT;
+		var NEAR = 1;
+		var FAR = 500;
+
+		var decalNormal = new THREE.TextureLoader().load( 'textures/decal/decal-normal.jpg' );
+
+		var decalDiffuse = new THREE.TextureLoader().load( 'textures/decal/decal-diffuse.png' );
+		decalDiffuse.wrapS = decalDiffuse.wrapT = THREE.RepeatWrapping;
+
+		var camera, scene, renderer;
+		var clock = new THREE.Clock();
+
+		var cameraControls;
+
+		var gui = new dat.GUI();
+
+		var sphereGroup, smallSphere;
+		var groundMirrorMaterial;
+
+		function init() {
+
+			// renderer
+			renderer = new THREE.WebGLRenderer();
+			renderer.setPixelRatio( window.devicePixelRatio );
+			renderer.setSize( WIDTH, HEIGHT );
+
+			// scene
+			scene = new THREE.Scene();
 
 			// camera
-			var VIEW_ANGLE = 45;
-			var ASPECT = WIDTH / HEIGHT;
-			var NEAR = 1;
-			var FAR = 500;
+			camera = new THREE.PerspectiveCamera( VIEW_ANGLE, ASPECT, NEAR, FAR );
+			camera.position.set( 0, 75, 160 );
+
+			cameraControls = new THREE.OrbitControls( camera, renderer.domElement );
+			cameraControls.target.set( 0, 40, 0 );
+			cameraControls.maxDistance = 400;
+			cameraControls.minDistance = 10;
+			cameraControls.update();
+
+			var container = document.getElementById( 'container' );
+			container.appendChild( renderer.domElement );
+
+		}
+
+		function fillScene() {
+
+			var planeGeo = new THREE.PlaneBufferGeometry( 100.1, 100.1 );
+
+			// reflector/mirror plane
+			var geometry = new THREE.PlaneBufferGeometry( 100, 100 );
+			var groundMirror = new THREE.ReflectorRTT( geometry, { clipBias: 0.003, textureWidth: WIDTH, textureHeight: HEIGHT } );
+
+			var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
+			var maskFlip = new THREE.Math1Node( mask, THREE.Math1Node.INVERT );
+
+			var mirror = new THREE.ReflectorNode( groundMirror );
+
+			var normal = new THREE.TextureNode( decalNormal );
+			var normalXY = new THREE.SwitchNode( normal, 'xy' );
+			var normalXYFlip = new THREE.Math1Node(
+				normalXY,
+				THREE.Math1Node.INVERT
+			);
+
+			var offsetNormal = new THREE.OperatorNode(
+				normalXYFlip,
+				new THREE.FloatNode( .5 ),
+				THREE.OperatorNode.SUB
+			);
+
+			mirror.offset = new THREE.OperatorNode(
+				offsetNormal, // normal
+				new THREE.FloatNode( 6 ), // scale
+				THREE.OperatorNode.MUL
+			);
+
+			var clr = new THREE.Math3Node(
+				mirror,
+				new THREE.ColorNode( 0xFFFFFF ),
+				mask,
+				THREE.Math3Node.MIX
+			);
+
+			var blurMirror = new THREE.BlurNode( mirror );
+			blurMirror.size = new THREE.Vector2( WIDTH, HEIGHT );
+			blurMirror.coord = new THREE.FunctionNode( "projCoord.xyz / projCoord.q", "vec3" );
+			blurMirror.coord.keywords[ "projCoord" ] = new THREE.OperatorNode( mirror.offset, mirror.coord, THREE.OperatorNode.ADD );
+			blurMirror.radius.x = blurMirror.radius.y = 0;
+
+			gui.add( { blur: blurMirror.radius.x }, "blur", 0, 25 ).onChange( function ( v ) {
+
+				blurMirror.radius.x = blurMirror.radius.y = v;
+
+			} );
+
+			groundMirrorMaterial = new THREE.PhongNodeMaterial();
+			groundMirrorMaterial.environment = blurMirror; // or add "mirror" variable to disable blur
+			groundMirrorMaterial.environmentAlpha = mask;
+			groundMirrorMaterial.normal = normal;
+			//groundMirrorMaterial.normalScale = new THREE.FloatNode( 1 );
+			groundMirrorMaterial.build();
+
+			// test serialization
+/*
+			var library = {};
+			library[ groundMirror.uuid ] = groundMirror;
+			library[ decalDiffuse.uuid ] = decalDiffuse;
+			library[ decalNormal.uuid ] = decalNormal;
+			library[ mirror.textureMatrix.uuid ] = mirror.textureMatrix; // use textureMatrix to projection
+
+			var json = groundMirrorMaterial.toJSON();
+
+			groundMirrorMaterial = new THREE.NodeMaterialLoader( null, library ).parse( json );
+*/
+			//--
+
+			var mirrorMesh = new THREE.Mesh( planeGeo, groundMirrorMaterial );
+			mirrorMesh.add( groundMirror );
+			mirrorMesh.rotateX( - Math.PI / 2 );
+			scene.add( mirrorMesh );
 
-			var decalNormal = new THREE.TextureLoader().load( 'textures/decal/decal-normal.jpg' );
+			sphereGroup = new THREE.Object3D();
+			scene.add( sphereGroup );
 
-			var decalDiffuse = new THREE.TextureLoader().load( 'textures/decal/decal-diffuse.png' );
-			decalDiffuse.wrapS = decalDiffuse.wrapT = THREE.RepeatWrapping;
+			var geometry = new THREE.CylinderGeometry( 0.1, 15 * Math.cos( Math.PI / 180 * 30 ), 0.1, 24, 1 );
+			var material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x444444 } );
+			var sphereCap = new THREE.Mesh( geometry, material );
+			sphereCap.position.y = - 15 * Math.sin( Math.PI / 180 * 30 ) - 0.05;
+			sphereCap.rotateX( - Math.PI );
 
-			var camera, scene, renderer;
-			var clock = new THREE.Clock();
+			var geometry = new THREE.SphereGeometry( 15, 24, 24, Math.PI / 2, Math.PI * 2, 0, Math.PI / 180 * 120 );
+			var halfSphere = new THREE.Mesh( geometry, material );
+			halfSphere.add( sphereCap );
+			halfSphere.rotateX( - Math.PI / 180 * 135 );
+			halfSphere.rotateZ( - Math.PI / 180 * 20 );
+			halfSphere.position.y = 7.5 + 15 * Math.sin( Math.PI / 180 * 30 );
 
-			var cameraControls;
+			sphereGroup.add( halfSphere );
 
-			var gui = new dat.GUI();
+			var geometry = new THREE.IcosahedronGeometry( 5, 0 );
+			var material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x333333, flatShading: true } );
+			smallSphere = new THREE.Mesh( geometry, material );
+			scene.add( smallSphere );
 
-			var sphereGroup, smallSphere;
-			var groundMirrorMaterial;
+			// walls
+			var planeTop = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );
+			planeTop.position.y = 100;
+			planeTop.rotateX( Math.PI / 2 );
+			scene.add( planeTop );
+
+			var planeBack = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );
+			planeBack.position.z = - 50;
+			planeBack.position.y = 50;
+			scene.add( planeBack );
 
-			function init() {
+			var planeFront = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0x7f7fff } ) );
+			planeFront.position.z = 50;
+			planeFront.position.y = 50;
+			planeFront.rotateY( Math.PI );
+			scene.add( planeFront );
+
+			var planeRight = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0x00ff00 } ) );
+			planeRight.position.x = 50;
+			planeRight.position.y = 50;
+			planeRight.rotateY( - Math.PI / 2 );
+			scene.add( planeRight );
 
-				// renderer
-				renderer = new THREE.WebGLRenderer();
-				renderer.setPixelRatio( window.devicePixelRatio );
-				renderer.setSize( WIDTH, HEIGHT );
+			var planeLeft = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xff0000 } ) );
+			planeLeft.position.x = - 50;
+			planeLeft.position.y = 50;
+			planeLeft.rotateY( Math.PI / 2 );
+			scene.add( planeLeft );
 
-				// scene
-				scene = new THREE.Scene();
+			// lights
+			var mainLight = new THREE.PointLight( 0xcccccc, 1.5, 250 );
+			mainLight.position.y = 60;
+			scene.add( mainLight );
 
-				// camera
-				camera = new THREE.PerspectiveCamera(VIEW_ANGLE, ASPECT, NEAR, FAR);
-				camera.position.set( 0, 75, 160 );
+			var greenLight = new THREE.PointLight( 0x00ff00, 0.25, 1000 );
+			greenLight.position.set( 550, 50, 0 );
+			scene.add( greenLight );
 
-				cameraControls = new THREE.OrbitControls(camera, renderer.domElement);
-				cameraControls.target.set( 0, 40, 0);
-				cameraControls.maxDistance = 400;
-				cameraControls.minDistance = 10;
-				cameraControls.update();
+			var redLight = new THREE.PointLight( 0xff0000, 0.25, 1000 );
+			redLight.position.set( - 550, 50, 0 );
+			scene.add( redLight );
 
-				var container = document.getElementById( 'container' );
-				container.appendChild( renderer.domElement );
+			var blueLight = new THREE.PointLight( 0x7f7fff, 0.25, 1000 );
+			blueLight.position.set( 0, 50, 550 );
+			scene.add( blueLight );
 
-			}
+		}
 
-			function fillScene() {
-
-				var planeGeo = new THREE.PlaneBufferGeometry( 100.1, 100.1 );
-
-				// reflector/mirror plane
-				var geometry = new THREE.PlaneBufferGeometry( 100, 100 );
-				var groundMirror = new THREE.ReflectorRTT( geometry, { clipBias: 0.003, textureWidth: WIDTH, textureHeight: HEIGHT } );
-
-				var mask = new THREE.SwitchNode( new THREE.TextureNode( decalDiffuse ), 'w' );
-				var maskFlip = new THREE.Math1Node( mask, THREE.Math1Node.INVERT );
-
-				var mirror = new THREE.ReflectorNode( groundMirror );
-
-				var normal = new THREE.TextureNode( decalNormal );
-				var normalXY = new THREE.SwitchNode( normal, 'xy' );
-				var normalXYFlip = new THREE.Math1Node(
-					normalXY,
-					THREE.Math1Node.INVERT
-				);
-
-				var offsetNormal = new THREE.OperatorNode(
-					normalXYFlip,
-					new THREE.FloatNode( .5 ),
-					THREE.OperatorNode.SUB
-				);
-
-				mirror.offset = new THREE.OperatorNode(
-					offsetNormal, // normal
-					new THREE.FloatNode( 6 ),// scale
-					THREE.OperatorNode.MUL
-				);
-
-				var clr = new THREE.Math3Node(
-					mirror,
-					new THREE.ColorNode( 0xFFFFFF ),
-					mask,
-					THREE.Math3Node.MIX
-				);
-
-				var blurMirror = new THREE.BlurNode( mirror );
-				blurMirror.size = new THREE.Vector2( WIDTH, HEIGHT );
-				blurMirror.coord = new THREE.FunctionNode( "projCoord.xyz / projCoord.q", "vec3" );
-				blurMirror.coord.keywords[ "projCoord" ] = new THREE.OperatorNode( mirror.offset, mirror.coord, THREE.OperatorNode.ADD );
-				blurMirror.radius.x = blurMirror.radius.y = 0;
-
-				gui.add( { blur : blurMirror.radius.x }, "blur", 0, 25 ).onChange( function(v) {
-
-					blurMirror.radius.x = blurMirror.radius.y = v;
-
-				} );
-
-				groundMirrorMaterial = new THREE.PhongNodeMaterial();
-				groundMirrorMaterial.environment = blurMirror; // or add "mirror" variable to disable blur
-				groundMirrorMaterial.environmentAlpha = mask;
-				groundMirrorMaterial.normal = normal;
-				//groundMirrorMaterial.normalScale = new THREE.FloatNode( 1 );
-				groundMirrorMaterial.build();
-
-				var mirrorMesh = new THREE.Mesh( planeGeo, groundMirrorMaterial );
-				mirrorMesh.add( groundMirror );
-				mirrorMesh.rotateX( - Math.PI / 2 );
-				scene.add( mirrorMesh );
-
-				sphereGroup = new THREE.Object3D();
-				scene.add( sphereGroup );
-
-				var geometry = new THREE.CylinderGeometry( 0.1, 15 * Math.cos( Math.PI / 180 * 30 ), 0.1, 24, 1 );
-				var material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x444444 } );
-				var sphereCap = new THREE.Mesh( geometry, material );
-				sphereCap.position.y = -15 * Math.sin( Math.PI / 180 * 30 ) - 0.05;
-				sphereCap.rotateX(-Math.PI);
-
-				var geometry = new THREE.SphereGeometry( 15, 24, 24, Math.PI / 2, Math.PI * 2, 0, Math.PI / 180 * 120 );
-				var halfSphere = new THREE.Mesh( geometry, material );
-				halfSphere.add( sphereCap );
-				halfSphere.rotateX( - Math.PI / 180 * 135 );
-				halfSphere.rotateZ( - Math.PI / 180 * 20 );
-				halfSphere.position.y = 7.5 + 15 * Math.sin( Math.PI / 180 * 30 );
-
-				sphereGroup.add( halfSphere );
-
-				var geometry = new THREE.IcosahedronGeometry( 5, 0 );
-				var material = new THREE.MeshPhongMaterial( { color: 0xffffff, emissive: 0x333333, flatShading: true } );
-				smallSphere = new THREE.Mesh( geometry, material );
-				scene.add(smallSphere);
-
-				// walls
-				var planeTop = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );
-				planeTop.position.y = 100;
-				planeTop.rotateX( Math.PI / 2 );
-				scene.add( planeTop );
-
-				var planeBack = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xffffff } ) );
-				planeBack.position.z = -50;
-				planeBack.position.y = 50;
-				scene.add( planeBack );
-
-				var planeFront = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0x7f7fff } ) );
-				planeFront.position.z = 50;
-				planeFront.position.y = 50;
-				planeFront.rotateY( Math.PI );
-				scene.add( planeFront );
-
-				var planeRight = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0x00ff00 } ) );
-				planeRight.position.x = 50;
-				planeRight.position.y = 50;
-				planeRight.rotateY( - Math.PI / 2 );
-				scene.add( planeRight );
-
-				var planeLeft = new THREE.Mesh( planeGeo, new THREE.MeshPhongMaterial( { color: 0xff0000 } ) );
-				planeLeft.position.x = -50;
-				planeLeft.position.y = 50;
-				planeLeft.rotateY( Math.PI / 2 );
-				scene.add( planeLeft );
-
-				// lights
-				var mainLight = new THREE.PointLight( 0xcccccc, 1.5, 250 );
-				mainLight.position.y = 60;
-				scene.add( mainLight );
-
-				var greenLight = new THREE.PointLight( 0x00ff00, 0.25, 1000 );
-				greenLight.position.set( 550, 50, 0 );
-				scene.add( greenLight );
-
-				var redLight = new THREE.PointLight( 0xff0000, 0.25, 1000 );
-				redLight.position.set( - 550, 50, 0 );
-				scene.add( redLight );
-
-				var blueLight = new THREE.PointLight( 0x7f7fff, 0.25, 1000 );
-				blueLight.position.set( 0, 50, 550 );
-				scene.add( blueLight );
+		function render() {
 
-			}
+			renderer.render( scene, camera );
 
-			function render() {
+		}
 
-				renderer.render(scene, camera);
+		function update() {
 
-			}
+			requestAnimationFrame( update );
 
-			function update() {
+			var delta = clock.getDelta();
+			var timer = Date.now() * 0.01;
 
-				requestAnimationFrame( update );
+			sphereGroup.rotation.y -= 0.002;
 
-				var delta = clock.getDelta();
-				var timer = Date.now() * 0.01;
+			smallSphere.position.set(
+				Math.cos( timer * 0.1 ) * 30,
+				Math.abs( Math.cos( timer * 0.2 ) ) * 20 + 5,
+				Math.sin( timer * 0.1 ) * 30
+			);
+			smallSphere.rotation.y = ( Math.PI / 2 ) - timer * 0.1;
+			smallSphere.rotation.z = timer * 0.8;
 
-				sphereGroup.rotation.y -= 0.002;
+			groundMirrorMaterial.updateFrame( delta );
 
-				smallSphere.position.set(
-					Math.cos( timer * 0.1 ) * 30,
-					Math.abs( Math.cos( timer * 0.2 ) ) * 20 + 5,
-					Math.sin( timer * 0.1 ) * 30
-				);
-				smallSphere.rotation.y = ( Math.PI / 2 ) - timer * 0.1;
-				smallSphere.rotation.z = timer * 0.8;
+			render();
 
-				groundMirrorMaterial.updateFrame( delta );
+		}
 
-				render();
-			}
+		init();
+		fillScene();
+		update();
 
-			init();
-			fillScene();
-			update();
 
 		</script>
 	</body>

+ 44 - 30
examples/webgl_postprocessing_nodes.html

@@ -86,6 +86,9 @@
 		<!-- Post-Processing -->
 		<script src="js/nodes/postprocessing/NodePass.js"></script>
 
+		<!-- NodeMaterial Loader -->
+		<script src="js/loaders/NodeMaterialLoader.js"></script>
+
 		<script>
 
 			var camera, scene, renderer, composer;
@@ -121,7 +124,7 @@
 					'adv / saturation': 'saturation',
 					'adv / refraction': 'refraction',
 					'adv / mosaic': 'mosaic'
-				} ).onFinishChange( function() {
+				} ).onFinishChange( function () {
 
 					updateMaterial();
 
@@ -139,7 +142,7 @@
 
 				if ( isColor ) {
 
-					node = gui.addColor( param, name ).onChange( function() {
+					node = gui.addColor( param, name ).onChange( function () {
 
 						callback( param[ name ] );
 
@@ -147,7 +150,7 @@
 
 				} else if ( typeof value == 'object' ) {
 
-					node = gui.add( param, name, value ).onChange( function() {
+					node = gui.add( param, name, value ).onChange( function () {
 
 						callback( param[ name ] );
 
@@ -155,7 +158,7 @@
 
 				} else {
 
-					node = gui.add( param, name, min, max ).onChange( function() {
+					node = gui.add( param, name, min, max ).onChange( function () {
 
 						callback( param[ name ] );
 
@@ -195,31 +198,31 @@
 
 						// GUI
 
-						addGui( 'hue', hue.number, function( val ) {
+						addGui( 'hue', hue.number, function ( val ) {
 
 							hue.number = val;
 
 						}, false, 0, Math.PI * 2 );
 
-						addGui( 'saturation', sataturation.number, function( val ) {
+						addGui( 'saturation', sataturation.number, function ( val ) {
 
 							sataturation.number = val;
 
 						}, false, 0, 2 );
 
-						addGui( 'vibrance', vibrance.number, function( val ) {
+						addGui( 'vibrance', vibrance.number, function ( val ) {
 
 							vibrance.number = val;
 
 						}, false, - 1, 1 );
 
-						addGui( 'brightness', brightness.number, function( val ) {
+						addGui( 'brightness', brightness.number, function ( val ) {
 
 							brightness.number = val;
 
 						}, false, 0, .5 );
 
-						addGui( 'contrast', contrast.number, function( val ) {
+						addGui( 'contrast', contrast.number, function ( val ) {
 
 							contrast.number = val;
 
@@ -245,13 +248,13 @@
 
 						// GUI
 
-						addGui( 'color', color.value.getHex(), function( val ) {
+						addGui( 'color', color.value.getHex(), function ( val ) {
 
 							color.value.setHex( val );
 
 						}, true );
 
-						addGui( 'fade', percent.number, function( val ) {
+						addGui( 'fade', percent.number, function ( val ) {
 
 							percent.number = val;
 
@@ -279,7 +282,7 @@
 
 						// GUI
 
-						addGui( 'alpha', alpha.number, function( val ) {
+						addGui( 'alpha', alpha.number, function ( val ) {
 
 							alpha.number = val;
 
@@ -302,11 +305,11 @@
 						// GUI
 
 						addGui( 'blend', {
-							'addition' : THREE.OperatorNode.ADD,
-							'subtract' : THREE.OperatorNode.SUB,
-							'multiply' : THREE.OperatorNode.MUL,
-							'division' : THREE.OperatorNode.DIV
-						}, function( val ) {
+							'addition': THREE.OperatorNode.ADD,
+							'subtract': THREE.OperatorNode.SUB,
+							'multiply': THREE.OperatorNode.MUL,
+							'division': THREE.OperatorNode.DIV
+						}, function ( val ) {
 
 							multiply.op = val;
 
@@ -324,11 +327,11 @@
 						var sat = new THREE.FloatNode( 0 );
 
 						var satrgb = new THREE.FunctionNode( [
-						"vec3 satrgb(vec3 rgb, float adjustment) {",
-						//"	const vec3 W = vec3(0.2125, 0.7154, 0.0721);", // LUMA
-						"	vec3 intensity = vec3(dot(rgb, LUMA));",
-						"	return mix(intensity, rgb, adjustment);",
-						"}"
+							"vec3 satrgb(vec3 rgb, float adjustment) {",
+							//"	const vec3 W = vec3(0.2125, 0.7154, 0.0721);", // LUMA
+							"	vec3 intensity = vec3(dot(rgb, LUMA));",
+							"	return mix(intensity, rgb, adjustment);",
+							"}"
 						].join( "\n" ) );
 
 						var saturation = new THREE.FunctionCallNode( satrgb );
@@ -339,7 +342,7 @@
 
 						// GUI
 
-						addGui( 'saturation', sat.number, function( val ) {
+						addGui( 'saturation', sat.number, function ( val ) {
 
 							sat.number = val;
 
@@ -392,13 +395,13 @@
 
 						// GUI
 
-						addGui( 'scale', scale.number, function( val ) {
+						addGui( 'scale', scale.number, function ( val ) {
 
 							scale.number = val;
 
 						}, false, 0, 1 );
 
-						addGui( 'invert', false, function( val ) {
+						addGui( 'invert', false, function ( val ) {
 
 							offsetNormal.a = val ? normalXYFlip : normalXY;
 
@@ -444,19 +447,19 @@
 
 						// GUI
 
-						addGui( 'scale', scale.number, function( val ) {
+						addGui( 'scale', scale.number, function ( val ) {
 
 							scale.number = val;
 
 						}, false, 16, 1024 );
 
-						addGui( 'fade', fade.number, function( val ) {
+						addGui( 'fade', fade.number, function ( val ) {
 
 							fade.number = val;
 
 						}, false, 0, 1 );
 
-						addGui( 'mask', true, function( val ) {
+						addGui( 'mask', true, function ( val ) {
 
 							fadeCoord.c = val ? maskAlpha : fade;
 
@@ -479,13 +482,13 @@
 
 						// GUI
 
-						addGui( 'blurX', blurScreen.radius.x, function( val ) {
+						addGui( 'blurX', blurScreen.radius.x, function ( val ) {
 
 							blurScreen.radius.x = val;
 
 						}, false, 0, 15 );
 
-						addGui( 'blurY', blurScreen.radius.y, function( val ) {
+						addGui( 'blurY', blurScreen.radius.y, function ( val ) {
 
 							blurScreen.radius.y = val;
 
@@ -497,6 +500,17 @@
 
 				nodepass.build();
 
+				// test serialization
+/*
+				var library = {};
+				library[ lensflare2.uuid ] = lensflare2;
+				library[ decalNormal.uuid ] = decalNormal;
+
+				var json = nodepass.toJSON();
+
+				nodepass.value = new THREE.NodeMaterialLoader( null, library ).parse( json ).value;
+				nodepass.build();
+*/
 			}
 
 			function init() {

+ 41 - 4
examples/webgl_sprites_nodes.html

@@ -97,13 +97,18 @@
 		<!-- Sprite Material -->
 		<script src="js/nodes/materials/SpriteNode.js"></script>
 		<script src="js/nodes/materials/SpriteNodeMaterial.js"></script>
-		
+
+		<!-- NodeMaterial Loader -->
+		<script src="js/loaders/NodeMaterialLoader.js"></script>
+
 		<script>
 
 		var container = document.getElementById( 'container' );
 
 		var renderer, scene, camera, clock = new THREE.Clock(), fov = 50;
 		var plane, sprite1, sprite2, sprite3;
+		var walkingManTexture, walkingManTextureURL;
+		var library = {};
 		var controls;
 
 		window.addEventListener( 'load', init );
@@ -138,9 +143,13 @@
 			plane = new THREE.PlaneBufferGeometry( 1, 1 );
 
 			// https://openclipart.org/detail/239883/walking-man-sprite-sheet
-			var walkingManTexture = new THREE.TextureLoader().load( "textures/WalkingManSpriteSheet.png" );
+			walkingManTextureURL = "textures/WalkingManSpriteSheet.png";
+
+			walkingManTexture = new THREE.TextureLoader().load( walkingManTextureURL );
 			walkingManTexture.wrapS = walkingManTexture.wrapT = THREE.RepeatWrapping;
 
+			library[ walkingManTextureURL ] = walkingManTexture;
+
 			// horizontal sprite-sheet animator
 
 			function createHorizontalSpriteSheetNode( hCount, speed ) {
@@ -203,7 +212,7 @@
 			sprite2.material.spherical = false; // look at camera horizontally only, very used to vegetation
 			// horizontal zigzag sprite
 			sprite2.material.transform = new THREE.OperatorNode(
-				new THREE.OperatorNode( 
+				new THREE.OperatorNode(
 					new THREE.Math1Node( new THREE.TimerNode( 0, 3 ), THREE.Math1Node.SIN ), // 3 is speed (time scale)
 					new THREE.Vector2Node( .3, 0 ), // horizontal scale (position)
 					THREE.OperatorNode.MUL
@@ -227,14 +236,22 @@
 			sprite3.position.x = - 30;
 			sprite3.scale.x = spriteWidth;
 			sprite3.scale.y = spriteHeight;
-			sprite3.material.fog = true;
 			sprite3.material.color = new THREE.TextureNode( walkingManTexture );
 			sprite3.material.color.coord = new THREE.FunctionCallNode( sineWaveFunction, {
 				"uv": createHorizontalSpriteSheetNode( 8, 0 ),
 				"phase": new THREE.TimerNode()
 			} );
+			sprite3.material.fog = true;
 			sprite3.material.build();
 
+			//
+			//	Test serialization
+			//
+
+			spriteToJSON( sprite1 );
+			spriteToJSON( sprite2 );
+			spriteToJSON( sprite3 );
+
 			//
 			// Events
 			//
@@ -246,6 +263,26 @@
 
 		}
 
+		function spriteToJSON( sprite ) {
+
+			// serialize
+
+			var json = sprite.material.toJSON();
+
+			// replace uuid to url (facilitates the load of textures using url otherside uuid)
+
+			THREE.NodeMaterialLoaderUtils.replaceUUID( json, walkingManTexture, walkingManTextureURL );
+
+			// unserialize
+
+			var material = new THREE.NodeMaterialLoader( null, library ).parse( json );
+
+			// replace material
+
+			sprite.material = material;
+
+		}
+
 		function onWindowResize() {
 
 			var width = window.innerWidth, height = window.innerHeight;

部分文件因为文件数量过多而无法显示