614 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
		
		
			
		
	
	
			614 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 
								 | 
							
								import { Material, ShaderMaterial } from 'three';
							 | 
						||
| 
								 | 
							
								import { getNodeChildren, getCacheKey } from '../core/NodeUtils.js';
							 | 
						||
| 
								 | 
							
								import { attribute } from '../core/AttributeNode.js';
							 | 
						||
| 
								 | 
							
								import { output, diffuseColor, varyingProperty } from '../core/PropertyNode.js';
							 | 
						||
| 
								 | 
							
								import { materialAlphaTest, materialColor, materialOpacity, materialEmissive, materialNormal } from '../accessors/MaterialNode.js';
							 | 
						||
| 
								 | 
							
								import { modelViewProjection } from '../accessors/ModelViewProjectionNode.js';
							 | 
						||
| 
								 | 
							
								import { transformedNormalView } from '../accessors/NormalNode.js';
							 | 
						||
| 
								 | 
							
								import { instance } from '../accessors/InstanceNode.js';
							 | 
						||
| 
								 | 
							
								import { batch } from '../accessors/BatchNode.js';
							 | 
						||
| 
								 | 
							
								import { materialReference } from '../accessors/MaterialReferenceNode.js';
							 | 
						||
| 
								 | 
							
								import { positionLocal, positionView } from '../accessors/PositionNode.js';
							 | 
						||
| 
								 | 
							
								import { skinningReference } from '../accessors/SkinningNode.js';
							 | 
						||
| 
								 | 
							
								import { morphReference } from '../accessors/MorphNode.js';
							 | 
						||
| 
								 | 
							
								import { texture } from '../accessors/TextureNode.js';
							 | 
						||
| 
								 | 
							
								import { cubeTexture } from '../accessors/CubeTextureNode.js';
							 | 
						||
| 
								 | 
							
								import { lightsNode } from '../lighting/LightsNode.js';
							 | 
						||
| 
								 | 
							
								import { mix } from '../math/MathNode.js';
							 | 
						||
| 
								 | 
							
								import { float, vec3, vec4 } from '../shadernode/ShaderNode.js';
							 | 
						||
| 
								 | 
							
								import AONode from '../lighting/AONode.js';
							 | 
						||
| 
								 | 
							
								import { lightingContext } from '../lighting/LightingContextNode.js';
							 | 
						||
| 
								 | 
							
								import EnvironmentNode from '../lighting/EnvironmentNode.js';
							 | 
						||
| 
								 | 
							
								import IrradianceNode from '../lighting/IrradianceNode.js';
							 | 
						||
| 
								 | 
							
								import { depthPixel } from '../display/ViewportDepthNode.js';
							 | 
						||
| 
								 | 
							
								import { cameraLogDepth } from '../accessors/CameraNode.js';
							 | 
						||
| 
								 | 
							
								import { clipping, clippingAlpha } from '../accessors/ClippingNode.js';
							 | 
						||
| 
								 | 
							
								import { faceDirection } from '../display/FrontFacingNode.js';
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								const NodeMaterials = new Map();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								class NodeMaterial extends ShaderMaterial {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									constructor() {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										super();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.isNodeMaterial = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.type = this.constructor.type;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.forceSinglePass = false;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.fog = true;
							 | 
						||
| 
								 | 
							
										this.lights = true;
							 | 
						||
| 
								 | 
							
										this.normals = true;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.lightsNode = null;
							 | 
						||
| 
								 | 
							
										this.envNode = null;
							 | 
						||
| 
								 | 
							
										this.aoNode = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.colorNode = null;
							 | 
						||
| 
								 | 
							
										this.normalNode = null;
							 | 
						||
| 
								 | 
							
										this.opacityNode = null;
							 | 
						||
| 
								 | 
							
										this.backdropNode = null;
							 | 
						||
| 
								 | 
							
										this.backdropAlphaNode = null;
							 | 
						||
| 
								 | 
							
										this.alphaTestNode = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.positionNode = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.depthNode = null;
							 | 
						||
| 
								 | 
							
										this.shadowNode = null;
							 | 
						||
| 
								 | 
							
										this.shadowPositionNode = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.outputNode = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.fragmentNode = null;
							 | 
						||
| 
								 | 
							
										this.vertexNode = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									customProgramCacheKey() {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return this.type + getCacheKey( this );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									build( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.setup( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setup( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// < VERTEX STAGE >
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										builder.addStack();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										builder.stack.outputNode = this.vertexNode || this.setupPosition( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										builder.addFlow( 'vertex', builder.removeStack() );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// < FRAGMENT STAGE >
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										builder.addStack();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let resultNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const clippingNode = this.setupClipping( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( this.depthWrite === true ) this.setupDepth( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( this.fragmentNode === null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( this.normals === true ) this.setupNormal( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											this.setupDiffuseColor( builder );
							 | 
						||
| 
								 | 
							
											this.setupVariants( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const outgoingLightNode = this.setupLighting( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( clippingNode !== null ) builder.stack.add( clippingNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// force unsigned floats - useful for RenderTargets
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const basicOutput = vec4( outgoingLightNode, diffuseColor.a ).max( 0 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											resultNode = this.setupOutput( builder, basicOutput );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											// OUTPUT NODE
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											output.assign( resultNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( this.outputNode !== null ) resultNode = this.outputNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											let fragmentNode = this.fragmentNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( fragmentNode.isOutputStructNode !== true ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												fragmentNode = vec4( fragmentNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											resultNode = this.setupOutput( builder, fragmentNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										builder.stack.outputNode = resultNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										builder.addFlow( 'fragment', builder.removeStack() );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupClipping( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( builder.clippingContext === null ) return null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const { globalClippingCount, localClippingCount } = builder.clippingContext;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let result = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( globalClippingCount || localClippingCount ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( this.alphaToCoverage ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												// to be added to flow when the color/alpha value has been determined
							 | 
						||
| 
								 | 
							
												result = clippingAlpha();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												builder.stack.add( clipping() );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return result;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupDepth( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const { renderer } = builder;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Depth
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let depthNode = this.depthNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( depthNode === null && renderer.logarithmicDepthBuffer === true ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const fragDepth = modelViewProjection().w.add( 1 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											depthNode = fragDepth.log2().mul( cameraLogDepth ).mul( 0.5 );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( depthNode !== null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											depthPixel.assign( depthNode ).append();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupPosition( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const { object } = builder;
							 | 
						||
| 
								 | 
							
										const geometry = object.geometry;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										builder.addStack();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Vertex
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											morphReference( object ).append();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( object.isSkinnedMesh === true ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											skinningReference( object ).append();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( object.isBatchedMesh ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											batch( object ).append();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( ( object.instanceMatrix && object.instanceMatrix.isInstancedBufferAttribute === true ) && builder.isAvailable( 'instance' ) === true ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											instance( object ).append();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( this.positionNode !== null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											positionLocal.assign( this.positionNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const mvp = modelViewProjection();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										builder.context.vertex = builder.removeStack();
							 | 
						||
| 
								 | 
							
										builder.context.mvp = mvp;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return mvp;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupDiffuseColor( { object, geometry } ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let colorNode = this.colorNode ? vec4( this.colorNode ) : materialColor;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// VERTEX COLORS
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( this.vertexColors === true && geometry.hasAttribute( 'color' ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											colorNode = vec4( colorNode.xyz.mul( attribute( 'color', 'vec3' ) ), colorNode.a );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Instanced colors
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( object.instanceColor ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const instanceColor = varyingProperty( 'vec3', 'vInstanceColor' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											colorNode = instanceColor.mul( colorNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// COLOR
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										diffuseColor.assign( colorNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// OPACITY
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const opacityNode = this.opacityNode ? float( this.opacityNode ) : materialOpacity;
							 | 
						||
| 
								 | 
							
										diffuseColor.a.assign( diffuseColor.a.mul( opacityNode ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// ALPHA TEST
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( this.alphaTestNode !== null || this.alphaTest > 0 ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const alphaTestNode = this.alphaTestNode !== null ? float( this.alphaTestNode ) : materialAlphaTest;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											diffuseColor.a.lessThanEqual( alphaTestNode ).discard();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupVariants( /*builder*/ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Interface function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupNormal() {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// NORMAL VIEW
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( this.flatShading === true ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const normalNode = positionView.dFdx().cross( positionView.dFdy() ).normalize();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											transformedNormalView.assign( normalNode.mul( faceDirection ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} else {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const normalNode = this.normalNode ? vec3( this.normalNode ) : materialNormal;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											transformedNormalView.assign( normalNode.mul( faceDirection ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									getEnvNode( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let node = null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( this.envNode ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											node = this.envNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} else if ( this.envMap ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											node = this.envMap.isCubeTexture ? cubeTexture( this.envMap ) : texture( this.envMap );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} else if ( builder.environmentNode ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											node = builder.environmentNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return node;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupLights( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const envNode = this.getEnvNode( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										//
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const materialLightsNode = [];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( envNode ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											materialLightsNode.push( new EnvironmentNode( envNode ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( builder.material.lightMap ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											materialLightsNode.push( new IrradianceNode( materialReference( 'lightMap', 'texture' ) ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( this.aoNode !== null || builder.material.aoMap ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const aoNode = this.aoNode !== null ? this.aoNode : texture( builder.material.aoMap );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											materialLightsNode.push( new AONode( aoNode ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let lightsN = this.lightsNode || builder.lightsNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( materialLightsNode.length > 0 ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											lightsN = lightsNode( [ ...lightsN.lightNodes, ...materialLightsNode ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return lightsN;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupLightingModel( /*builder*/ ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// Interface function.
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupLighting( builder ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const { material } = builder;
							 | 
						||
| 
								 | 
							
										const { backdropNode, backdropAlphaNode, emissiveNode } = this;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// OUTGOING LIGHT
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const lights = this.lights === true || this.lightsNode !== null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const lightsNode = lights ? this.setupLights( builder ) : null;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										let outgoingLightNode = diffuseColor.rgb;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( lightsNode && lightsNode.hasLight !== false ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const lightingModel = this.setupLightingModel( builder );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											outgoingLightNode = lightingContext( lightsNode, lightingModel, backdropNode, backdropAlphaNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										} else if ( backdropNode !== null ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											outgoingLightNode = vec3( backdropAlphaNode !== null ? mix( outgoingLightNode, backdropNode, backdropAlphaNode ) : backdropNode );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// EMISSIVE
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( ( emissiveNode && emissiveNode.isNode === true ) || ( material.emissive && material.emissive.isColor === true ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											outgoingLightNode = outgoingLightNode.add( vec3( emissiveNode ? emissiveNode : materialEmissive ) );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return outgoingLightNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setupOutput( builder, outputNode ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// FOG
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const fogNode = builder.fogNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( fogNode ) outputNode = vec4( fogNode.mix( outputNode.rgb, fogNode.colorNode ), outputNode.a );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return outputNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									setDefaultValues( material ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										// 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 material ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											const value = material[ property ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( this[ property ] === undefined ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												this[ property ] = value;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												if ( value && value.clone ) this[ property ] = value.clone();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										Object.assign( this.defines, material.defines );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const descriptors = Object.getOwnPropertyDescriptors( material.constructor.prototype );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( const key in descriptors ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											if ( Object.getOwnPropertyDescriptor( this.constructor.prototype, key ) === undefined &&
							 | 
						||
| 
								 | 
							
											     descriptors[ key ].get !== undefined ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
												Object.defineProperty( this.constructor.prototype, key, descriptors[ key ] );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									toJSON( meta ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const isRoot = ( meta === undefined || typeof meta === 'string' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( isRoot ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											meta = {
							 | 
						||
| 
								 | 
							
												textures: {},
							 | 
						||
| 
								 | 
							
												images: {},
							 | 
						||
| 
								 | 
							
												nodes: {}
							 | 
						||
| 
								 | 
							
											};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const data = Material.prototype.toJSON.call( this, meta );
							 | 
						||
| 
								 | 
							
										const nodeChildren = getNodeChildren( this );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										data.inputNodes = {};
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( const { property, childNode } of nodeChildren ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											data.inputNodes[ property ] = childNode.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;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									copy( source ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.lightsNode = source.lightsNode;
							 | 
						||
| 
								 | 
							
										this.envNode = source.envNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.colorNode = source.colorNode;
							 | 
						||
| 
								 | 
							
										this.normalNode = source.normalNode;
							 | 
						||
| 
								 | 
							
										this.opacityNode = source.opacityNode;
							 | 
						||
| 
								 | 
							
										this.backdropNode = source.backdropNode;
							 | 
						||
| 
								 | 
							
										this.backdropAlphaNode = source.backdropAlphaNode;
							 | 
						||
| 
								 | 
							
										this.alphaTestNode = source.alphaTestNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.positionNode = source.positionNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.depthNode = source.depthNode;
							 | 
						||
| 
								 | 
							
										this.shadowNode = source.shadowNode;
							 | 
						||
| 
								 | 
							
										this.shadowPositionNode = source.shadowPositionNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.outputNode = source.outputNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										this.fragmentNode = source.fragmentNode;
							 | 
						||
| 
								 | 
							
										this.vertexNode = source.vertexNode;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return super.copy( source );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									static fromMaterial( material ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( material.isNodeMaterial === true ) { // is already a node material
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											return material;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const type = material.type.replace( 'Material', 'NodeMaterial' );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										const nodeMaterial = createNodeMaterialFromType( type );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										if ( nodeMaterial === undefined ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											throw new Error( `NodeMaterial: Material "${ material.type }" is not compatible.` );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										for ( const key in material ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
											nodeMaterial[ key ] = material[ key ];
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return nodeMaterial;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export default NodeMaterial;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function addNodeMaterial( type, nodeMaterial ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( typeof nodeMaterial !== 'function' || ! type ) throw new Error( `Node material ${ type } is not a class` );
							 | 
						||
| 
								 | 
							
									if ( NodeMaterials.has( type ) ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										console.warn( `Redefinition of node material ${ type }` );
							 | 
						||
| 
								 | 
							
										return;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									NodeMaterials.set( type, nodeMaterial );
							 | 
						||
| 
								 | 
							
									nodeMaterial.type = type;
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								export function createNodeMaterialFromType( type ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									const Material = NodeMaterials.get( type );
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									if ( Material !== undefined ) {
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
										return new Material();
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
									}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								}
							 | 
						||
| 
								 | 
							
								
							 | 
						||
| 
								 | 
							
								addNodeMaterial( 'NodeMaterial', NodeMaterial );
							 |