Browse Source

NodeMaterial: serialize/deserialize and Material.fromType() (#23314)

* Node serializer (draft)

* add Material.fromType()

* NodeObjectLoader: serialize/deserialize

* update default values

* fix analytic light

* force refresh material uniforms using .uniformsNeedUpdate

* Revert "force refresh material uniforms using .uniformsNeedUpdate"

This reverts commit b083fb44296941b0724ef3529bdcfa30b9dbda53.

* mrdoob code style

* cleanup

* cleanup (2)

* Update NodeMaterial.js

Co-authored-by: mrdoob <[email protected]>
sunag 3 years ago
parent
commit
98985923af
34 changed files with 1033 additions and 39 deletions
  1. 102 2
      examples/jsm/renderers/nodes/Nodes.js
  2. 16 0
      examples/jsm/renderers/nodes/accessors/NormalNode.js
  3. 16 0
      examples/jsm/renderers/nodes/accessors/Object3DNode.js
  4. 16 0
      examples/jsm/renderers/nodes/accessors/PositionNode.js
  5. 16 0
      examples/jsm/renderers/nodes/accessors/UVNode.js
  6. 113 1
      examples/jsm/renderers/nodes/core/Node.js
  7. 19 0
      examples/jsm/renderers/nodes/core/NodeUtils.js
  8. 25 0
      examples/jsm/renderers/nodes/inputs/ColorNode.js
  9. 16 0
      examples/jsm/renderers/nodes/inputs/FloatNode.js
  10. 16 0
      examples/jsm/renderers/nodes/inputs/IntNode.js
  11. 16 0
      examples/jsm/renderers/nodes/inputs/TextureNode.js
  12. 23 0
      examples/jsm/renderers/nodes/inputs/Vector2Node.js
  13. 25 0
      examples/jsm/renderers/nodes/inputs/Vector3Node.js
  14. 27 0
      examples/jsm/renderers/nodes/inputs/Vector4Node.js
  15. 107 0
      examples/jsm/renderers/nodes/loaders/NodeLoader.js
  16. 42 0
      examples/jsm/renderers/nodes/loaders/NodeMaterialLoader.js
  17. 70 0
      examples/jsm/renderers/nodes/loaders/NodeObjectLoader.js
  18. 9 2
      examples/jsm/renderers/nodes/materials/LineBasicNodeMaterial.js
  19. 22 0
      examples/jsm/renderers/nodes/materials/Materials.js
  20. 9 2
      examples/jsm/renderers/nodes/materials/MeshBasicNodeMaterial.js
  21. 9 4
      examples/jsm/renderers/nodes/materials/MeshStandardNodeMaterial.js
  22. 98 0
      examples/jsm/renderers/nodes/materials/NodeMaterial.js
  23. 9 2
      examples/jsm/renderers/nodes/materials/PointsNodeMaterial.js
  24. 16 0
      examples/jsm/renderers/nodes/math/MathNode.js
  25. 16 0
      examples/jsm/renderers/nodes/math/OperatorNode.js
  26. 16 0
      examples/jsm/renderers/nodes/utils/OscNode.js
  27. 16 0
      examples/jsm/renderers/nodes/utils/SplitNode.js
  28. 18 1
      examples/jsm/renderers/nodes/utils/TimerNode.js
  29. 23 1
      examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js
  30. 27 3
      examples/webgl_materials_standard_nodes.html
  31. 4 1
      src/core/Object3D.js
  32. 2 2
      src/loaders/MaterialLoader.js
  33. 8 0
      src/materials/Material.js
  34. 66 18
      src/materials/Materials.js

+ 102 - 2
examples/jsm/renderers/nodes/Nodes.js

@@ -71,6 +71,11 @@ import SpriteSheetUVNode from './utils/SpriteSheetUVNode.js';
 import OscNode from './utils/OscNode.js';
 import OscNode from './utils/OscNode.js';
 import TimerNode from './utils/TimerNode.js';
 import TimerNode from './utils/TimerNode.js';
 
 
+// loaders
+import NodeLoader from './loaders/NodeLoader.js';
+import NodeObjectLoader from './loaders/NodeObjectLoader.js';
+import NodeMaterialLoader from './loaders/NodeMaterialLoader.js';
+
 // procedural
 // procedural
 import CheckerNode from './procedural/CheckerNode.js';
 import CheckerNode from './procedural/CheckerNode.js';
 
 
@@ -86,7 +91,7 @@ export * from './materials/Materials.js';
 // shader node
 // shader node
 export * from './ShaderNode.js';
 export * from './ShaderNode.js';
 
 
-export {
+const nodeLib = {
 	// core
 	// core
 	ArrayInputNode,
 	ArrayInputNode,
 	AttributeNode,
 	AttributeNode,
@@ -161,6 +166,101 @@ export {
 	TimerNode,
 	TimerNode,
 
 
 	// procedural
 	// procedural
-	CheckerNode
+	CheckerNode,
+
+	// loaders
+	NodeLoader,
+	NodeObjectLoader,
+	NodeMaterialLoader
+
 };
 };
 
 
+export const fromType = ( type ) => {
+
+	return new nodeLib[ type ]();
+
+};
+
+export {
+	// core
+	ArrayInputNode,
+	AttributeNode,
+	BypassNode,
+	CodeNode,
+	ContextNode,
+	ExpressionNode,
+	FunctionCallNode,
+	FunctionNode,
+	InputNode,
+	Node,
+	NodeAttribute,
+	NodeBuilder,
+	NodeCode,
+	NodeFrame,
+	NodeFunctionInput,
+	NodeKeywords,
+	NodeUniform,
+	NodeVar,
+	NodeVary,
+	PropertyNode,
+	TempNode,
+	VarNode,
+	VaryNode,
+
+	// accessors
+	CameraNode,
+	MaterialNode,
+	MaterialReferenceNode,
+	ModelNode,
+	ModelViewProjectionNode,
+	NormalNode,
+	Object3DNode,
+	PointUVNode,
+	PositionNode,
+	ReferenceNode,
+	SkinningNode,
+	UVNode,
+
+	// inputs
+	ColorNode,
+	FloatNode,
+	IntNode,
+	Matrix3Node,
+	Matrix4Node,
+	TextureNode,
+	Vector2Node,
+	Vector3Node,
+	Vector4Node,
+
+	// display
+	ColorSpaceNode,
+	NormalMapNode,
+
+	// math
+	MathNode,
+	OperatorNode,
+	CondNode,
+
+	// lights
+	LightContextNode,
+	LightNode,
+	LightsNode,
+
+	// utils
+	ArrayElementNode,
+	ConvertNode,
+	JoinNode,
+	SplitNode,
+	SpriteSheetUVNode,
+	OscNode,
+	TimerNode,
+
+	// procedural
+	CheckerNode,
+
+	// loaders
+	NodeLoader,
+	NodeObjectLoader,
+	NodeMaterialLoader
+
+};

+ 16 - 0
examples/jsm/renderers/nodes/accessors/NormalNode.js

@@ -58,6 +58,22 @@ class NormalNode extends Node {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.scope = this.scope;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.scope = data.scope;
+
+	}
+
 }
 }
 
 
 export default NormalNode;
 export default NormalNode;

+ 16 - 0
examples/jsm/renderers/nodes/accessors/Object3DNode.js

@@ -100,6 +100,22 @@ class Object3DNode extends Node {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.scope = this.scope;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.scope = data.scope;
+
+	}
+
 }
 }
 
 
 export default Object3DNode;
 export default Object3DNode;

+ 16 - 0
examples/jsm/renderers/nodes/accessors/PositionNode.js

@@ -62,6 +62,22 @@ class PositionNode extends Node {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.scope = this.scope;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.scope = data.scope;
+
+	}
+
 }
 }
 
 
 export default PositionNode;
 export default PositionNode;

+ 16 - 0
examples/jsm/renderers/nodes/accessors/UVNode.js

@@ -18,6 +18,22 @@ class UVNode extends AttributeNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.index = this.index;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.index = data.index;
+
+	}
+
 }
 }
 
 
 UVNode.prototype.isUVNode = true;
 UVNode.prototype.isUVNode = true;

+ 113 - 1
examples/jsm/renderers/nodes/core/Node.js

@@ -1,5 +1,5 @@
 import { NodeUpdateType } from './constants.js';
 import { NodeUpdateType } from './constants.js';
-
+import { getNodesKeys } from './NodeUtils.js';
 import { MathUtils } from 'three';
 import { MathUtils } from 'three';
 
 
 class Node {
 class Node {
@@ -97,6 +97,118 @@ class Node {
 
 
 	}
 	}
 
 
+	serialize( json ) {
+
+		const nodeKeys = getNodesKeys( this );
+
+		if ( nodeKeys.length > 0 ) {
+
+			const inputNodes = {};
+
+			for ( const property of nodeKeys ) {
+
+				inputNodes[ property ] = this[ property ].toJSON( json.meta ).uuid;
+
+			}
+
+			json.inputNodes = inputNodes;
+
+		}
+
+	}
+
+	deserialize( json ) {
+
+		if ( json.inputNodes !== undefined ) {
+
+			const nodes = json.meta.nodes;
+
+			for ( const property in json.inputNodes ) {
+
+				const uuid = json.inputNodes[ property ];
+
+				this[ property ] = nodes[ uuid ];
+
+			}
+
+		}
+
+	}
+
+	toJSON( meta ) {
+
+		const { uuid, type } = this;
+		const isRoot = ( meta === undefined || typeof meta === 'string' );
+
+		if ( isRoot ) {
+
+			meta = {
+				textures: {},
+				images: {},
+				nodes: {}
+			};
+
+		}
+
+		// serialize
+
+		let data = meta.nodes[ uuid ];
+
+		if ( data === undefined ) {
+
+			data = {
+				uuid,
+				type,
+				meta,
+				metadata: {
+					version: 4.5,
+					type: 'Node',
+					generator: 'Node.toJSON'
+				}
+			};
+
+			meta.nodes[ data.uuid ] = data;
+
+			this.serialize( data );
+
+			delete data.meta;
+
+		}
+
+		// TODO: Copied from Object3D.toJSON
+
+		function extractFromCache( cache ) {
+
+			const values = [];
+
+			for ( const key in cache ) {
+
+				const data = cache[ key ];
+				delete data.metadata;
+				values.push( data );
+
+			}
+
+			return values;
+
+		}
+
+		if ( isRoot ) {
+
+			const textures = extractFromCache( meta.textures );
+			const images = extractFromCache( meta.images );
+			const nodes = extractFromCache( meta.nodes );
+
+			if ( textures.length > 0 ) data.textures = textures;
+			if ( images.length > 0 ) data.images = images;
+			if ( nodes.length > 0 ) data.nodes = nodes;
+
+		}
+
+		return data;
+
+	}
+
 }
 }
 
 
 Node.prototype.isNode = true;
 Node.prototype.isNode = true;

+ 19 - 0
examples/jsm/renderers/nodes/core/NodeUtils.js

@@ -0,0 +1,19 @@
+export const getNodesKeys = ( object ) => {
+
+	const props = [];
+
+	for ( const name in object ) {
+
+		const value = object[ name ];
+
+		if ( value && value.isNode === true ) {
+
+			props.push( name );
+
+		}
+
+	}
+
+	return props;
+
+};

+ 25 - 0
examples/jsm/renderers/nodes/inputs/ColorNode.js

@@ -11,6 +11,31 @@ class ColorNode extends InputNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		const { r, g, b } = this.value;
+
+		data.r = r;
+		data.g = g;
+		data.b = b;
+
+	}
+
+	deserialize( data ) {
+
+		super.serialize( data );
+
+		const { r, g, b } = data;
+		const value = this.value;
+
+		value.r = r;
+		value.g = g;
+		value.b = b;
+
+	}
+
 }
 }
 
 
 ColorNode.prototype.isColorNode = true;
 ColorNode.prototype.isColorNode = true;

+ 16 - 0
examples/jsm/renderers/nodes/inputs/FloatNode.js

@@ -10,6 +10,22 @@ class FloatNode extends InputNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.value = this.value;
+
+	}
+
+	deserialize( data ) {
+
+		super.serialize( data );
+
+		data.value = this.value;
+
+	}
+
 }
 }
 
 
 FloatNode.prototype.isFloatNode = true;
 FloatNode.prototype.isFloatNode = true;

+ 16 - 0
examples/jsm/renderers/nodes/inputs/IntNode.js

@@ -10,6 +10,22 @@ class IntNode extends InputNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.value = this.value;
+
+	}
+
+	deserialize( data ) {
+
+		super.serialize( data );
+
+		this.value = data.value;
+
+	}
+
 }
 }
 
 
 IntNode.prototype.isIntNode = true;
 IntNode.prototype.isIntNode = true;

+ 16 - 0
examples/jsm/renderers/nodes/inputs/TextureNode.js

@@ -66,6 +66,22 @@ class TextureNode extends InputNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.value = this.value.toJSON( data.meta ).uuid;
+
+	}
+
+	deserialize( data ) {
+
+		super.serialize( data );
+
+		this.value = data.meta.textures[ data.value ];
+
+	}
+
 }
 }
 
 
 TextureNode.prototype.isTextureNode = true;
 TextureNode.prototype.isTextureNode = true;

+ 23 - 0
examples/jsm/renderers/nodes/inputs/Vector2Node.js

@@ -11,6 +11,29 @@ class Vector2Node extends InputNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		const { x, y } = this.value;
+
+		data.x = x;
+		data.y = y;
+
+	}
+
+	deserialize( data ) {
+
+		super.serialize( data );
+
+		const { x, y } = data;
+		const value = this.value;
+
+		value.x = x;
+		value.y = y;
+
+	}
+
 }
 }
 
 
 Vector2Node.prototype.isVector2Node = true;
 Vector2Node.prototype.isVector2Node = true;

+ 25 - 0
examples/jsm/renderers/nodes/inputs/Vector3Node.js

@@ -11,6 +11,31 @@ class Vector3Node extends InputNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		const { x, y, z } = this.value;
+
+		data.x = x;
+		data.y = y;
+		data.z = z;
+
+	}
+
+	deserialize( data ) {
+
+		super.serialize( data );
+
+		const { x, y, z } = data;
+		const value = this.value;
+
+		value.x = x;
+		value.y = y;
+		value.z = z;
+
+	}
+
 }
 }
 
 
 Vector3Node.prototype.isVector3Node = true;
 Vector3Node.prototype.isVector3Node = true;

+ 27 - 0
examples/jsm/renderers/nodes/inputs/Vector4Node.js

@@ -11,6 +11,33 @@ class Vector4Node extends InputNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		const { x, y, z, w } = this.value;
+
+		data.x = x;
+		data.y = y;
+		data.z = z;
+		data.w = w;
+
+	}
+
+	deserialize( data ) {
+
+		super.serialize( data );
+
+		const { x, y, z, w } = data;
+		const value = this.value;
+
+		value.x = x;
+		value.y = y;
+		value.z = z;
+		value.w = w;
+
+	}
+
 }
 }
 
 
 Vector4Node.prototype.isVector4Node = true;
 Vector4Node.prototype.isVector4Node = true;

+ 107 - 0
examples/jsm/renderers/nodes/loaders/NodeLoader.js

@@ -0,0 +1,107 @@
+import * as Nodes from '../Nodes.js';
+import { Loader } from 'three';
+
+class NodeLoader extends Loader {
+
+	constructor( manager ) {
+
+		super( manager );
+
+		this.textures = {};
+
+	}
+
+	load( url, onLoad, onProgress, onError ) {
+
+		const loader = new FileLoader( this.manager );
+		loader.setPath( this.path );
+		loader.setRequestHeader( this.requestHeader );
+		loader.setWithCredentials( this.withCredentials );
+		loader.load( url, ( text ) => {
+
+			try {
+
+				onLoad( this.parse( JSON.parse( text ) ) );
+
+			} catch ( e ) {
+
+				if ( onError ) {
+
+					onError( e );
+
+				} else {
+
+					console.error( e );
+
+				}
+
+				this.manager.itemError( url );
+
+			}
+
+		}, onProgress, onError );
+
+	}
+
+	parseNodes( json ) {
+
+		const nodes = {};
+
+		if ( json !== undefined ) {
+
+			for ( const nodeJSON of json ) {
+
+				const { uuid, type } = nodeJSON;
+
+				nodes[ uuid ] = Nodes.fromType( type );
+				nodes[ uuid ].uuid = uuid;
+
+			}
+
+			const meta = { nodes, textures: this.textures };
+
+			for ( const nodeJSON of json ) {
+
+				nodeJSON.meta = meta;
+
+				const node = nodes[ nodeJSON.uuid ];
+				node.deserialize( nodeJSON );
+
+				delete nodeJSON.meta;
+
+			}
+
+		}
+
+		return nodes;
+
+	}
+
+	parse( json ) {
+
+		const node = Nodes.fromType( type );
+		node.uuid = json.uuid;
+
+		const nodes = this.parseNodes( json.inputNodes );
+		const meta = { nodes, textures: this.textures };
+
+		json.meta = meta;
+
+		node.deserialize( json );
+
+		delete json.meta;
+
+		return node;
+
+	}
+
+	setTextures( value ) {
+
+		this.textures = value;
+		return this;
+
+	}
+
+}
+
+export default NodeLoader;

+ 42 - 0
examples/jsm/renderers/nodes/loaders/NodeMaterialLoader.js

@@ -0,0 +1,42 @@
+import { MaterialLoader } from 'three';
+
+class NodeMaterialLoader extends MaterialLoader {
+
+	constructor( manager ) {
+
+		super( manager );
+
+		this.nodes = {};
+
+	}
+
+	parse( json ) {
+
+		const material = super.parse( json );
+
+		const nodes = this.nodes;
+		const inputNodes = json.inputNodes;
+
+		for ( const property in inputNodes ) {
+
+			const uuid = inputNodes[ property ];
+
+			material[ property ] = nodes[ uuid ];
+
+		}
+
+		return material;
+
+	}
+
+	setNodes( value ) {
+
+		this.nodes = value;
+
+		return this;
+
+	}
+
+}
+
+export default NodeMaterialLoader;

+ 70 - 0
examples/jsm/renderers/nodes/loaders/NodeObjectLoader.js

@@ -0,0 +1,70 @@
+import NodeLoader from './NodeLoader.js';
+import NodeMaterialLoader from './NodeMaterialLoader.js';
+import { ObjectLoader } from 'three';
+
+class NodeObjectLoader extends ObjectLoader {
+
+	constructor( manager ) {
+
+		super( manager );
+
+		this._nodesJSON = null;
+
+	}
+
+	parse( json, onLoad ) {
+
+		this._nodesJSON = json.nodes;
+
+		const data = super.parse( json, onLoad );
+
+		this._nodesJSON = null; // dispose
+
+		return data;
+
+	}
+
+	parseNodes( json, textures ) {
+
+		if ( json !== undefined ) {
+
+			const loader = new NodeLoader();
+			loader.setTextures( textures );
+
+			return loader.parseNodes( json );
+
+		}
+
+		return {};
+
+	}
+
+	parseMaterials( json, textures ) {
+
+		const materials = {};
+
+		if ( json !== undefined ) {
+
+			const nodes = this.parseNodes( this._nodesJSON, textures );
+
+			const loader = new NodeMaterialLoader();
+			loader.setTextures( textures );
+			loader.setNodes( nodes );
+
+			for ( let i = 0, l = json.length; i < l; i ++ ) {
+
+				const data = json[ i ];
+
+				materials[ data.uuid ] = loader.parse( data );
+
+			}
+
+		}
+
+		return materials;
+
+	}
+
+}
+
+export default NodeObjectLoader;

+ 9 - 2
examples/jsm/renderers/nodes/materials/LineBasicNodeMaterial.js

@@ -1,10 +1,13 @@
+import NodeMaterial from './NodeMaterial.js';
 import { LineBasicMaterial } from 'three';
 import { LineBasicMaterial } from 'three';
 
 
-class LineBasicNodeMaterial extends LineBasicMaterial {
+const defaultValues = new LineBasicMaterial();
+
+class LineBasicNodeMaterial extends NodeMaterial {
 
 
 	constructor( parameters ) {
 	constructor( parameters ) {
 
 
-		super( parameters );
+		super();
 
 
 		this.colorNode = null;
 		this.colorNode = null;
 		this.opacityNode = null;
 		this.opacityNode = null;
@@ -15,6 +18,10 @@ class LineBasicNodeMaterial extends LineBasicMaterial {
 
 
 		this.positionNode = null;
 		this.positionNode = null;
 
 
+		this.setDefaultValues( defaultValues );
+
+		this.setValues( parameters );
+
 	}
 	}
 
 
 	copy( source ) {
 	copy( source ) {

+ 22 - 0
examples/jsm/renderers/nodes/materials/Materials.js

@@ -2,6 +2,7 @@ import LineBasicNodeMaterial from './LineBasicNodeMaterial.js';
 import MeshBasicNodeMaterial from './MeshBasicNodeMaterial.js';
 import MeshBasicNodeMaterial from './MeshBasicNodeMaterial.js';
 import MeshStandardNodeMaterial from './MeshStandardNodeMaterial.js';
 import MeshStandardNodeMaterial from './MeshStandardNodeMaterial.js';
 import PointsNodeMaterial from './PointsNodeMaterial.js';
 import PointsNodeMaterial from './PointsNodeMaterial.js';
+import { Material } from 'three';
 
 
 export {
 export {
 	LineBasicNodeMaterial,
 	LineBasicNodeMaterial,
@@ -9,3 +10,24 @@ export {
 	MeshStandardNodeMaterial,
 	MeshStandardNodeMaterial,
 	PointsNodeMaterial
 	PointsNodeMaterial
 };
 };
+
+const materialLib = {
+	LineBasicNodeMaterial,
+	MeshBasicNodeMaterial,
+	MeshStandardNodeMaterial,
+	PointsNodeMaterial
+};
+
+const fromTypeFunction = Material.fromType;
+
+Material.fromType = function ( type ) {
+
+	if ( materialLib[ type ] !== undefined ) {
+
+		return new materialLib[ type ]();
+
+	}
+
+	return fromTypeFunction.call( this, type );
+
+};

+ 9 - 2
examples/jsm/renderers/nodes/materials/MeshBasicNodeMaterial.js

@@ -1,10 +1,13 @@
+import NodeMaterial from './NodeMaterial.js';
 import { MeshBasicMaterial } from 'three';
 import { MeshBasicMaterial } from 'three';
 
 
-class MeshBasicNodeMaterial extends MeshBasicMaterial {
+const defaultValues = new MeshBasicMaterial();
+
+class MeshBasicNodeMaterial extends NodeMaterial {
 
 
 	constructor( parameters ) {
 	constructor( parameters ) {
 
 
-		super( parameters );
+		super();
 
 
 		this.colorNode = null;
 		this.colorNode = null;
 		this.opacityNode = null;
 		this.opacityNode = null;
@@ -15,6 +18,10 @@ class MeshBasicNodeMaterial extends MeshBasicMaterial {
 
 
 		this.positionNode = null;
 		this.positionNode = null;
 
 
+		this.setDefaultValues( defaultValues );
+
+		this.setValues( parameters );
+
 	}
 	}
 
 
 	copy( source ) {
 	copy( source ) {

+ 9 - 4
examples/jsm/renderers/nodes/materials/MeshStandardNodeMaterial.js

@@ -1,10 +1,13 @@
+import NodeMaterial from './NodeMaterial.js';
 import { MeshStandardMaterial } from 'three';
 import { MeshStandardMaterial } from 'three';
 
 
-class MeshStandardNodeMaterial extends MeshStandardMaterial {
+const defaultValues = new MeshStandardMaterial();
+
+export default class MeshStandardNodeMaterial extends NodeMaterial {
 
 
 	constructor( parameters ) {
 	constructor( parameters ) {
 
 
-		super( parameters );
+		super();
 
 
 		this.colorNode = null;
 		this.colorNode = null;
 		this.opacityNode = null;
 		this.opacityNode = null;
@@ -27,6 +30,10 @@ class MeshStandardNodeMaterial extends MeshStandardMaterial {
 
 
 		this.positionNode = null;
 		this.positionNode = null;
 
 
+		this.setDefaultValues( defaultValues );
+
+		this.setValues( parameters );
+
 	}
 	}
 
 
 	copy( source ) {
 	copy( source ) {
@@ -59,5 +66,3 @@ class MeshStandardNodeMaterial extends MeshStandardMaterial {
 }
 }
 
 
 MeshStandardNodeMaterial.prototype.isNodeMaterial = true;
 MeshStandardNodeMaterial.prototype.isNodeMaterial = true;
-
-export default MeshStandardNodeMaterial;

+ 98 - 0
examples/jsm/renderers/nodes/materials/NodeMaterial.js

@@ -0,0 +1,98 @@
+import { Material, ShaderMaterial } from 'three';
+import { getNodesKeys } from '../core/NodeUtils.js';
+
+class NodeMaterial extends ShaderMaterial {
+
+	constructor() {
+
+		super();
+
+		this.type = this.constructor.name;
+
+		this.lights = true;
+
+	}
+
+	setDefaultValues( values ) {
+
+		// This approach is to reuse the native refreshUniforms*
+		// and turn available the use of features like transmission and environment in core
+
+		for ( const property in values ) {
+
+			if ( this[ property ] === undefined ) {
+
+				this[ property ] = values[ property ];
+
+			}
+
+		}
+
+		Object.assign( this.defines, values.defines );
+
+	}
+
+	toJSON( meta ) {
+
+		const isRoot = ( meta === undefined || typeof meta === 'string' );
+
+		if ( isRoot ) {
+
+			meta = {
+				textures: {},
+				images: {},
+				nodes: {}
+			};
+
+		}
+
+		const data = Material.prototype.toJSON.call( this, meta );
+		const nodeKeys = getNodesKeys( this );
+
+		data.inputNodes = {};
+
+		for ( const name of nodeKeys ) {
+
+			data.inputNodes[ name ] = this[ name ].toJSON( meta ).uuid;
+
+		}
+
+		// TODO: Copied from Object3D.toJSON
+
+		function extractFromCache( cache ) {
+
+			const values = [];
+
+			for ( const key in cache ) {
+
+				const data = cache[ key ];
+				delete data.metadata;
+				values.push( data );
+
+			}
+
+			return values;
+
+		}
+
+		if ( isRoot ) {
+
+			const textures = extractFromCache( meta.textures );
+			const images = extractFromCache( meta.images );
+			const nodes = extractFromCache( meta.nodes );
+
+			if ( textures.length > 0 ) data.textures = textures;
+			if ( images.length > 0 ) data.images = images;
+			if ( nodes.length > 0 ) data.nodes = nodes;
+
+		}
+
+		return data;
+
+	}
+
+}
+
+NodeMaterial.prototype.isNodeMaterial = true;
+
+export default NodeMaterial;

+ 9 - 2
examples/jsm/renderers/nodes/materials/PointsNodeMaterial.js

@@ -1,10 +1,13 @@
+import NodeMaterial from './NodeMaterial.js';
 import { PointsMaterial } from 'three';
 import { PointsMaterial } from 'three';
 
 
-class PointsNodeMaterial extends PointsMaterial {
+const defaultValues = new PointsMaterial();
+
+class PointsNodeMaterial extends NodeMaterial {
 
 
 	constructor( parameters ) {
 	constructor( parameters ) {
 
 
-		super( parameters );
+		super();
 
 
 		this.colorNode = null;
 		this.colorNode = null;
 		this.opacityNode = null;
 		this.opacityNode = null;
@@ -17,6 +20,10 @@ class PointsNodeMaterial extends PointsMaterial {
 
 
 		this.positionNode = null;
 		this.positionNode = null;
 
 
+		this.setDefaultValues( defaultValues );
+
+		this.setValues( parameters );
+
 	}
 	}
 
 
 	copy( source ) {
 	copy( source ) {

+ 16 - 0
examples/jsm/renderers/nodes/math/MathNode.js

@@ -237,6 +237,22 @@ class MathNode extends TempNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.method = this.method;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.method = data.method;
+
+	}
+
 }
 }
 
 
 export default MathNode;
 export default MathNode;

+ 16 - 0
examples/jsm/renderers/nodes/math/OperatorNode.js

@@ -169,6 +169,22 @@ class OperatorNode extends TempNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.op = this.op;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.op = data.op;
+
+	}
+
 }
 }
 
 
 export default OperatorNode;
 export default OperatorNode;

+ 16 - 0
examples/jsm/renderers/nodes/utils/OscNode.js

@@ -53,6 +53,22 @@ class OscNode extends Node {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.method = this.method;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.method = data.method;
+
+	}
+
 }
 }
 
 
 export default OscNode;
 export default OscNode;

+ 16 - 0
examples/jsm/renderers/nodes/utils/SplitNode.js

@@ -65,6 +65,22 @@ class SplitNode extends Node {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.components = this.components;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.components = data.components;
+
+	}
+
 }
 }
 
 
 export default SplitNode;
 export default SplitNode;

+ 18 - 1
examples/jsm/renderers/nodes/utils/TimerNode.js

@@ -12,7 +12,6 @@ class TimerNode extends FloatNode {
 		super();
 		super();
 
 
 		this.scope = scope;
 		this.scope = scope;
-
 		this.scale = 1;
 		this.scale = 1;
 
 
 		this.updateType = NodeUpdateType.Frame;
 		this.updateType = NodeUpdateType.Frame;
@@ -42,6 +41,24 @@ class TimerNode extends FloatNode {
 
 
 	}
 	}
 
 
+	serialize( data ) {
+
+		super.serialize( data );
+
+		data.scope = this.scope;
+		data.scale = this.scale;
+
+	}
+
+	deserialize( data ) {
+
+		super.deserialize( data );
+
+		this.scope = data.scope;
+		this.scale = data.scale;
+
+	}
+
 }
 }
 
 
 export default TimerNode;
 export default TimerNode;

+ 23 - 1
examples/jsm/renderers/webgl/nodes/WebGLNodeBuilder.js

@@ -3,10 +3,18 @@ import SlotNode from './SlotNode.js';
 import GLSLNodeParser from '../../nodes/parsers/GLSLNodeParser.js';
 import GLSLNodeParser from '../../nodes/parsers/GLSLNodeParser.js';
 import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
 import WebGLPhysicalContextNode from './WebGLPhysicalContextNode.js';
 
 
-import { ShaderChunk, LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
+import { ShaderChunk, ShaderLib, UniformsUtils, UniformsLib,
+		LinearEncoding, RGBAFormat, UnsignedByteType, sRGBEncoding } from 'three';
 
 
 const shaderStages = [ 'vertex', 'fragment' ];
 const shaderStages = [ 'vertex', 'fragment' ];
 
 
+const nodeShaderLib = {
+	LineBasicNodeMaterial: ShaderLib.basic,
+	MeshBasicNodeMaterial: ShaderLib.basic,
+	PointsNodeMaterial: ShaderLib.points,
+	MeshStandardNodeMaterial: ShaderLib.standard
+};
+
 function getIncludeSnippet( name ) {
 function getIncludeSnippet( name ) {
 
 
 	return `#include <${name}>`;
 	return `#include <${name}>`;
@@ -55,6 +63,20 @@ class WebGLNodeBuilder extends NodeBuilder {
 	_parseObject() {
 	_parseObject() {
 
 
 		const material = this.material;
 		const material = this.material;
+		const type = material.type;
+
+		// shader lib
+
+		if ( nodeShaderLib[ type ] !== undefined ) {
+
+			const shaderLib = nodeShaderLib[ type ];
+			const shader = this.shader;
+
+			shader.vertexShader = shaderLib.vertexShader;
+			shader.fragmentShader = shaderLib.fragmentShader;
+			shader.uniforms = UniformsUtils.merge( [ shaderLib.uniforms, UniformsLib.lights ] );
+
+		}
 
 
 		// parse inputs
 		// parse inputs
 
 

+ 27 - 3
examples/webgl_materials_standard_nodes.html

@@ -82,7 +82,9 @@
 					.setPath( 'models/obj/cerberus/' )
 					.setPath( 'models/obj/cerberus/' )
 					.load( 'Cerberus.obj', function ( group ) {
 					.load( 'Cerberus.obj', function ( group ) {
 
 
-						const loader = new THREE.TextureLoader()
+						const loaderManager = new THREE.LoadingManager();
+
+						const loader = new THREE.TextureLoader( loaderManager )
 							.setPath( 'models/obj/cerberus/' );
 							.setPath( 'models/obj/cerberus/' );
 
 
 						const diffuseMap = loader.load( 'Cerberus_A.jpg' );
 						const diffuseMap = loader.load( 'Cerberus_A.jpg' );
@@ -97,7 +99,7 @@
 
 
 						const mpMapNode = new Nodes.TextureNode( rmMap );
 						const mpMapNode = new Nodes.TextureNode( rmMap );
 
 
-						material.colorNode = new Nodes.OperatorNode( '*', new Nodes.TextureNode( diffuseMap ), new Nodes.Vector3Node( material.color ) );
+						material.colorNode = new Nodes.OperatorNode( '*', new Nodes.TextureNode( diffuseMap ), new Nodes.ColorNode( material.color ) );
 
 
 						// roughness is in G channel, metalness is in B channel
 						// roughness is in G channel, metalness is in B channel
 						material.roughnessNode = new Nodes.SplitNode( mpMapNode, 'g' );
 						material.roughnessNode = new Nodes.SplitNode( mpMapNode, 'g' );
@@ -117,7 +119,29 @@
 
 
 						group.position.x = - 0.45;
 						group.position.x = - 0.45;
 						group.rotation.y = - Math.PI / 2;
 						group.rotation.y = - Math.PI / 2;
-						scene.add( group );
+						//scene.add( group );
+
+						// TODO: Serialization test
+
+						loaderManager.onLoad = () => {
+
+							const groupJSON = JSON.stringify( group.toJSON() );
+
+							const objectLoader = new Nodes.NodeObjectLoader();
+							objectLoader.parse( JSON.parse( groupJSON ), ( newGroup ) => {
+
+								//scene.remove( group );
+
+								newGroup.position.copy( group.position );
+								newGroup.rotation.copy( group.rotation );
+
+								scene.add( newGroup );
+
+								console.log( 'Serialized!' );
+
+							} );
+
+						};
 
 
 					} );
 					} );
 
 

+ 4 - 1
src/core/Object3D.js

@@ -646,7 +646,8 @@ class Object3D extends EventDispatcher {
 				images: {},
 				images: {},
 				shapes: {},
 				shapes: {},
 				skeletons: {},
 				skeletons: {},
-				animations: {}
+				animations: {},
+				nodes: {}
 			};
 			};
 
 
 			output.metadata = {
 			output.metadata = {
@@ -830,6 +831,7 @@ class Object3D extends EventDispatcher {
 			const shapes = extractFromCache( meta.shapes );
 			const shapes = extractFromCache( meta.shapes );
 			const skeletons = extractFromCache( meta.skeletons );
 			const skeletons = extractFromCache( meta.skeletons );
 			const animations = extractFromCache( meta.animations );
 			const animations = extractFromCache( meta.animations );
+			const nodes = extractFromCache( meta.nodes );
 
 
 			if ( geometries.length > 0 ) output.geometries = geometries;
 			if ( geometries.length > 0 ) output.geometries = geometries;
 			if ( materials.length > 0 ) output.materials = materials;
 			if ( materials.length > 0 ) output.materials = materials;
@@ -838,6 +840,7 @@ class Object3D extends EventDispatcher {
 			if ( shapes.length > 0 ) output.shapes = shapes;
 			if ( shapes.length > 0 ) output.shapes = shapes;
 			if ( skeletons.length > 0 ) output.skeletons = skeletons;
 			if ( skeletons.length > 0 ) output.skeletons = skeletons;
 			if ( animations.length > 0 ) output.animations = animations;
 			if ( animations.length > 0 ) output.animations = animations;
+			if ( nodes.length > 0 ) output.nodes = nodes;
 
 
 		}
 		}
 
 

+ 2 - 2
src/loaders/MaterialLoader.js

@@ -6,7 +6,7 @@ import { Matrix3 } from '../math/Matrix3.js';
 import { Matrix4 } from '../math/Matrix4.js';
 import { Matrix4 } from '../math/Matrix4.js';
 import { FileLoader } from './FileLoader.js';
 import { FileLoader } from './FileLoader.js';
 import { Loader } from './Loader.js';
 import { Loader } from './Loader.js';
-import * as Materials from '../materials/Materials.js';
+import { Material } from '../materials/Material.js';
 
 
 class MaterialLoader extends Loader {
 class MaterialLoader extends Loader {
 
 
@@ -67,7 +67,7 @@ class MaterialLoader extends Loader {
 
 
 		}
 		}
 
 
-		const material = new Materials[ json.type ]();
+		const material = Material.fromType( json.type );
 
 
 		if ( json.uuid !== undefined ) material.uuid = json.uuid;
 		if ( json.uuid !== undefined ) material.uuid = json.uuid;
 		if ( json.name !== undefined ) material.name = json.name;
 		if ( json.name !== undefined ) material.name = json.name;

+ 8 - 0
src/materials/Material.js

@@ -485,4 +485,12 @@ class Material extends EventDispatcher {
 
 
 Material.prototype.isMaterial = true;
 Material.prototype.isMaterial = true;
 
 
+Material.fromType = function ( /*type*/ ) {
+
+	// TODO: Behavior added in Materials.js
+
+	return null;
+
+};
+
 export { Material };
 export { Material };

+ 66 - 18
src/materials/Materials.js

@@ -1,18 +1,66 @@
-export { ShadowMaterial } from './ShadowMaterial.js';
-export { SpriteMaterial } from './SpriteMaterial.js';
-export { RawShaderMaterial } from './RawShaderMaterial.js';
-export { ShaderMaterial } from './ShaderMaterial.js';
-export { PointsMaterial } from './PointsMaterial.js';
-export { MeshPhysicalMaterial } from './MeshPhysicalMaterial.js';
-export { MeshStandardMaterial } from './MeshStandardMaterial.js';
-export { MeshPhongMaterial } from './MeshPhongMaterial.js';
-export { MeshToonMaterial } from './MeshToonMaterial.js';
-export { MeshNormalMaterial } from './MeshNormalMaterial.js';
-export { MeshLambertMaterial } from './MeshLambertMaterial.js';
-export { MeshDepthMaterial } from './MeshDepthMaterial.js';
-export { MeshDistanceMaterial } from './MeshDistanceMaterial.js';
-export { MeshBasicMaterial } from './MeshBasicMaterial.js';
-export { MeshMatcapMaterial } from './MeshMatcapMaterial.js';
-export { LineDashedMaterial } from './LineDashedMaterial.js';
-export { LineBasicMaterial } from './LineBasicMaterial.js';
-export { Material } from './Material.js';
+import { ShadowMaterial } from './ShadowMaterial.js';
+import { SpriteMaterial } from './SpriteMaterial.js';
+import { RawShaderMaterial } from './RawShaderMaterial.js';
+import { ShaderMaterial } from './ShaderMaterial.js';
+import { PointsMaterial } from './PointsMaterial.js';
+import { MeshPhysicalMaterial } from './MeshPhysicalMaterial.js';
+import { MeshStandardMaterial } from './MeshStandardMaterial.js';
+import { MeshPhongMaterial } from './MeshPhongMaterial.js';
+import { MeshToonMaterial } from './MeshToonMaterial.js';
+import { MeshNormalMaterial } from './MeshNormalMaterial.js';
+import { MeshLambertMaterial } from './MeshLambertMaterial.js';
+import { MeshDepthMaterial } from './MeshDepthMaterial.js';
+import { MeshDistanceMaterial } from './MeshDistanceMaterial.js';
+import { MeshBasicMaterial } from './MeshBasicMaterial.js';
+import { MeshMatcapMaterial } from './MeshMatcapMaterial.js';
+import { LineDashedMaterial } from './LineDashedMaterial.js';
+import { LineBasicMaterial } from './LineBasicMaterial.js';
+import { Material } from './Material.js';
+
+export {
+	ShadowMaterial,
+	SpriteMaterial,
+	RawShaderMaterial,
+	ShaderMaterial,
+	PointsMaterial,
+	MeshPhysicalMaterial,
+	MeshStandardMaterial,
+	MeshPhongMaterial,
+	MeshToonMaterial,
+	MeshNormalMaterial,
+	MeshLambertMaterial,
+	MeshDepthMaterial,
+	MeshDistanceMaterial,
+	MeshBasicMaterial,
+	MeshMatcapMaterial,
+	LineDashedMaterial,
+	LineBasicMaterial,
+	Material
+};
+
+const materialLib = {
+	ShadowMaterial,
+	SpriteMaterial,
+	RawShaderMaterial,
+	ShaderMaterial,
+	PointsMaterial,
+	MeshPhysicalMaterial,
+	MeshStandardMaterial,
+	MeshPhongMaterial,
+	MeshToonMaterial,
+	MeshNormalMaterial,
+	MeshLambertMaterial,
+	MeshDepthMaterial,
+	MeshDistanceMaterial,
+	MeshBasicMaterial,
+	MeshMatcapMaterial,
+	LineDashedMaterial,
+	LineBasicMaterial,
+	Material
+};
+
+Material.fromType = function ( type ) {
+
+	return new materialLib[ type ]();
+
+};