three
Version:
JavaScript 3D library
638 lines (372 loc) • 12.9 kB
JavaScript
/**
* @author sunag / http://www.sunag.com.br/
*/
THREE.NodeMaterial = function ( vertex, fragment ) {
THREE.ShaderMaterial.call( this );
this.vertex = vertex || new THREE.RawNode( new THREE.PositionNode( THREE.PositionNode.PROJECTION ) );
this.fragment = fragment || new THREE.RawNode( new THREE.ColorNode( 0xFF0000 ) );
this.updaters = [];
};
THREE.NodeMaterial.types = {
t: 'sampler2D',
tc: 'samplerCube',
bv1: 'bool',
iv1: 'int',
fv1: 'float',
c: 'vec3',
v2: 'vec2',
v3: 'vec3',
v4: 'vec4',
m3: 'mat3',
m4: 'mat4'
};
THREE.NodeMaterial.addShortcuts = function ( proto, prop, list ) {
function applyShortcut( prop, name ) {
return {
get: function () {
return this[ prop ][ name ];
},
set: function ( val ) {
this[ prop ][ name ] = val;
}
};
}
return ( function () {
var shortcuts = {};
for ( var i = 0; i < list.length; ++ i ) {
var name = list[ i ];
shortcuts[ name ] = applyShortcut( prop, name );
}
Object.defineProperties( proto, shortcuts );
} )();
};
THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
THREE.NodeMaterial.prototype.type = "NodeMaterial";
THREE.NodeMaterial.prototype.updateFrame = function ( frame ) {
for ( var i = 0; i < this.updaters.length; ++ i ) {
frame.updateNode( this.updaters[ i ] );
}
};
THREE.NodeMaterial.prototype.build = function () {
var vertex, fragment;
this.nodes = [];
this.defines = {};
this.uniforms = {};
this.attributes = {};
this.extensions = {};
this.nodeData = {};
this.vertexUniform = [];
this.fragmentUniform = [];
this.vars = [];
this.vertexTemps = [];
this.fragmentTemps = [];
this.uniformList = [];
this.consts = [];
this.functions = [];
this.updaters = [];
this.requires = {
uv: [],
color: [],
lights: this.lights,
fog: this.fog
};
this.vertexPars = '';
this.fragmentPars = '';
this.vertexCode = '';
this.fragmentCode = '';
this.vertexNode = '';
this.fragmentNode = '';
this.prefixCode = [
"#ifdef GL_EXT_shader_texture_lod",
" #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)",
"#else",
" #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)",
"#endif",
"#include <packing>"
].join( "\n" );
var builder = new THREE.NodeBuilder( this );
vertex = this.vertex.build( builder.setShader( 'vertex' ), 'v4' );
fragment = this.fragment.build( builder.setShader( 'fragment' ), 'v4' );
if ( this.requires.uv[ 0 ] ) {
this.addVertexPars( 'varying vec2 vUv;' );
this.addFragmentPars( 'varying vec2 vUv;' );
this.addVertexCode( 'vUv = uv;' );
}
if ( this.requires.uv[ 1 ] ) {
this.addVertexPars( 'varying vec2 vUv2; attribute vec2 uv2;' );
this.addFragmentPars( 'varying vec2 vUv2;' );
this.addVertexCode( 'vUv2 = uv2;' );
}
if ( this.requires.color[ 0 ] ) {
this.addVertexPars( 'varying vec4 vColor; attribute vec4 color;' );
this.addFragmentPars( 'varying vec4 vColor;' );
this.addVertexCode( 'vColor = color;' );
}
if ( this.requires.color[ 1 ] ) {
this.addVertexPars( 'varying vec4 vColor2; attribute vec4 color2;' );
this.addFragmentPars( 'varying vec4 vColor2;' );
this.addVertexCode( 'vColor2 = color2;' );
}
if ( this.requires.position ) {
this.addVertexPars( 'varying vec3 vPosition;' );
this.addFragmentPars( 'varying vec3 vPosition;' );
this.addVertexCode( 'vPosition = transformed;' );
}
if ( this.requires.worldPosition ) {
this.addVertexPars( 'varying vec3 vWPosition;' );
this.addFragmentPars( 'varying vec3 vWPosition;' );
this.addVertexCode( 'vWPosition = ( modelMatrix * vec4( transformed, 1.0 ) ).xyz;' );
}
if ( this.requires.normal ) {
this.addVertexPars( 'varying vec3 vObjectNormal;' );
this.addFragmentPars( 'varying vec3 vObjectNormal;' );
this.addVertexCode( 'vObjectNormal = normal;' );
}
if ( this.requires.worldNormal ) {
this.addVertexPars( 'varying vec3 vWNormal;' );
this.addFragmentPars( 'varying vec3 vWNormal;' );
this.addVertexCode( 'vWNormal = ( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz;' );
}
this.fog = this.requires.fog;
this.lights = this.requires.lights;
this.transparent = this.requires.transparent || this.blending > THREE.NormalBlending;
this.vertexShader = [
this.prefixCode,
this.vertexPars,
this.getCodePars( this.vertexUniform, 'uniform' ),
this.getIncludes( this.consts[ 'vertex' ] ),
this.getIncludes( this.functions[ 'vertex' ] ),
'void main(){',
this.getCodePars( this.vertexTemps ),
vertex,
this.vertexCode,
'}'
].join( "\n" );
this.fragmentShader = [
this.prefixCode,
this.fragmentPars,
this.getCodePars( this.fragmentUniform, 'uniform' ),
this.getIncludes( this.consts[ 'fragment' ] ),
this.getIncludes( this.functions[ 'fragment' ] ),
'void main(){',
this.getCodePars( this.fragmentTemps ),
this.fragmentCode,
fragment,
'}'
].join( "\n" );
this.needsUpdate = true;
this.dispose(); // force update
return this;
};
THREE.NodeMaterial.prototype.define = function ( name, value ) {
this.defines[ name ] = value == undefined ? 1 : value;
};
THREE.NodeMaterial.prototype.isDefined = function ( name ) {
return this.defines[ name ] != undefined;
};
THREE.NodeMaterial.prototype.mergeUniform = function ( uniforms ) {
for ( var name in uniforms ) {
this.uniforms[ name ] = uniforms[ name ];
}
};
THREE.NodeMaterial.prototype.createUniform = function ( type, value, ns, needsUpdate ) {
var index = this.uniformList.length;
var uniform = {
type: type,
value: value,
name: ns ? ns : 'nVu' + index,
needsUpdate: needsUpdate
};
this.uniformList.push( uniform );
return uniform;
};
THREE.NodeMaterial.prototype.getVertexTemp = function ( uuid, type, ns ) {
var data = this.vertexTemps[ uuid ];
if ( ! data ) {
var index = this.vertexTemps.length,
name = ns ? ns : 'nVt' + index;
data = { name: name, type: type };
this.vertexTemps.push( data );
this.vertexTemps[ uuid ] = data;
}
return data;
};
THREE.NodeMaterial.prototype.getFragmentTemp = function ( uuid, type, ns ) {
var data = this.fragmentTemps[ uuid ];
if ( ! data ) {
var index = this.fragmentTemps.length,
name = ns ? ns : 'nVt' + index;
data = { name: name, type: type };
this.fragmentTemps.push( data );
this.fragmentTemps[ uuid ] = data;
}
return data;
};
THREE.NodeMaterial.prototype.getVar = function ( uuid, type, ns ) {
var data = this.vars[ uuid ];
if ( ! data ) {
var index = this.vars.length,
name = ns ? ns : 'nVv' + index;
data = { name: name, type: type };
this.vars.push( data );
this.vars[ uuid ] = data;
this.addVertexPars( 'varying ' + type + ' ' + name + ';' );
this.addFragmentPars( 'varying ' + type + ' ' + name + ';' );
}
return data;
};
THREE.NodeMaterial.prototype.getAttribute = function ( name, type ) {
if ( ! this.attributes[ name ] ) {
var varying = this.getVar( name, type );
this.addVertexPars( 'attribute ' + type + ' ' + name + ';' );
this.addVertexCode( varying.name + ' = ' + name + ';' );
this.attributes[ name ] = { varying: varying, name: name, type: type };
}
return this.attributes[ name ];
};
THREE.NodeMaterial.prototype.getIncludes = function () {
function sortByPosition( a, b ) {
return a.deps.length - b.deps.length;
}
return function ( incs ) {
if ( ! incs ) return '';
var code = '', incs = incs.sort( sortByPosition );
for ( var i = 0; i < incs.length; i ++ ) {
if ( incs[ i ].src ) code += incs[ i ].src + '\n';
}
return code;
};
}();
THREE.NodeMaterial.prototype.addVertexPars = function ( code ) {
this.vertexPars += code + '\n';
};
THREE.NodeMaterial.prototype.addFragmentPars = function ( code ) {
this.fragmentPars += code + '\n';
};
THREE.NodeMaterial.prototype.addVertexCode = function ( code ) {
this.vertexCode += code + '\n';
};
THREE.NodeMaterial.prototype.addFragmentCode = function ( code ) {
this.fragmentCode += code + '\n';
};
THREE.NodeMaterial.prototype.addVertexNode = function ( code ) {
this.vertexNode += code + '\n';
};
THREE.NodeMaterial.prototype.clearVertexNode = function () {
var code = this.vertexNode;
this.vertexNode = '';
return code;
};
THREE.NodeMaterial.prototype.addFragmentNode = function ( code ) {
this.fragmentNode += code + '\n';
};
THREE.NodeMaterial.prototype.clearFragmentNode = function () {
var code = this.fragmentNode;
this.fragmentNode = '';
return code;
};
THREE.NodeMaterial.prototype.getCodePars = function ( pars, prefix ) {
prefix = prefix || '';
var code = '';
for ( var i = 0, l = pars.length; i < l; ++ i ) {
var parsType = pars[ i ].type;
var parsName = pars[ i ].name;
var parsValue = pars[ i ].value;
if ( parsType == 't' && parsValue instanceof THREE.CubeTexture ) parsType = 'tc';
var type = THREE.NodeMaterial.types[ parsType ];
if ( type == undefined ) throw new Error( "Node pars " + parsType + " not found." );
code += prefix + ' ' + type + ' ' + parsName + ';\n';
}
return code;
};
THREE.NodeMaterial.prototype.createVertexUniform = function ( type, value, ns, needsUpdate ) {
var uniform = this.createUniform( type, value, ns, needsUpdate );
this.vertexUniform.push( uniform );
this.vertexUniform[ uniform.name ] = uniform;
this.uniforms[ uniform.name ] = uniform;
return uniform;
};
THREE.NodeMaterial.prototype.createFragmentUniform = function ( type, value, ns, needsUpdate ) {
var uniform = this.createUniform( type, value, ns, needsUpdate );
this.fragmentUniform.push( uniform );
this.fragmentUniform[ uniform.name ] = uniform;
this.uniforms[ uniform.name ] = uniform;
return uniform;
};
THREE.NodeMaterial.prototype.getDataNode = function ( uuid ) {
return this.nodeData[ uuid ] = this.nodeData[ uuid ] || {};
};
THREE.NodeMaterial.prototype.include = function ( builder, node, parent, source ) {
var includes;
node = typeof node === 'string' ? THREE.NodeLib.get( node ) : node;
if ( node instanceof THREE.FunctionNode ) {
includes = this.functions[ builder.shader ] = this.functions[ builder.shader ] || [];
} else if ( node instanceof THREE.ConstNode ) {
includes = this.consts[ builder.shader ] = this.consts[ builder.shader ] || [];
}
var included = includes[ node.name ];
if ( ! included ) {
included = includes[ node.name ] = {
node: node,
deps: []
};
includes.push( included );
included.src = node.build( builder, 'source' );
}
if ( node instanceof THREE.FunctionNode && parent && includes[ parent.name ] && includes[ parent.name ].deps.indexOf( node ) == - 1 ) {
includes[ parent.name ].deps.push( node );
if ( node.includes && node.includes.length ) {
var i = 0;
do {
this.include( builder, node.includes[ i ++ ], parent );
} while ( i < node.includes.length );
}
}
if ( source ) {
included.src = 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;
};