NodeMaterial.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538
  1. /**
  2. * @author sunag / http://www.sunag.com.br/
  3. */
  4. THREE.NodeMaterial = function( vertex, fragment ) {
  5. THREE.ShaderMaterial.call( this );
  6. this.vertex = vertex || new THREE.RawNode( new THREE.PositionNode( THREE.PositionNode.PROJECTION ) );
  7. this.fragment = fragment || new THREE.RawNode( new THREE.ColorNode( 0xFF0000 ) );
  8. };
  9. THREE.NodeMaterial.types = {
  10. t : 'sampler2D',
  11. tc : 'samplerCube',
  12. bv1 : 'bool',
  13. iv1 : 'int',
  14. fv1 : 'float',
  15. c : 'vec3',
  16. v2 : 'vec2',
  17. v3 : 'vec3',
  18. v4 : 'vec4',
  19. m4 : 'mat4'
  20. };
  21. THREE.NodeMaterial.addShortcuts = function( proto, prop, list ) {
  22. function applyShortcut( prop, name ) {
  23. return {
  24. get: function() {
  25. return this[ prop ][ name ];
  26. },
  27. set: function( val ) {
  28. this[ prop ][ name ] = val;
  29. }
  30. };
  31. };
  32. return ( function() {
  33. var shortcuts = {};
  34. for ( var i = 0; i < list.length; ++ i ) {
  35. var name = list[ i ];
  36. shortcuts[ name ] = applyShortcut( prop, name );
  37. }
  38. Object.defineProperties( proto, shortcuts );
  39. } )();
  40. };
  41. THREE.NodeMaterial.prototype = Object.create( THREE.ShaderMaterial.prototype );
  42. THREE.NodeMaterial.prototype.constructor = THREE.NodeMaterial;
  43. THREE.NodeMaterial.prototype.updateFrame = function( delta ) {
  44. for ( var i = 0; i < this.requestUpdate.length; ++ i ) {
  45. this.requestUpdate[ i ].updateFrame( delta );
  46. }
  47. };
  48. THREE.NodeMaterial.prototype.build = function() {
  49. var vertex, fragment;
  50. this.defines = {};
  51. this.uniforms = {};
  52. this.attributes = {};
  53. this.nodeData = {};
  54. this.vertexUniform = [];
  55. this.fragmentUniform = [];
  56. this.vertexTemps = [];
  57. this.fragmentTemps = [];
  58. this.uniformList = [];
  59. this.consts = [];
  60. this.functions = [];
  61. this.requestUpdate = [];
  62. this.requestAttribs = {
  63. uv: [],
  64. color: []
  65. };
  66. this.vertexPars = '';
  67. this.fragmentPars = '';
  68. this.vertexCode = '';
  69. this.fragmentCode = '';
  70. this.vertexNode = '';
  71. this.fragmentNode = '';
  72. this.prefixCode = [
  73. "#ifdef GL_EXT_shader_texture_lod",
  74. "#define texCube(a, b) textureCube(a, b)",
  75. "#define texCubeBias(a, b, c) textureCubeLodEXT(a, b, c)",
  76. "#define tex2D(a, b) texture2D(a, b)",
  77. "#define tex2DBias(a, b, c) texture2DLodEXT(a, b, c)",
  78. "#else",
  79. "#define texCube(a, b) textureCube(a, b)",
  80. "#define texCubeBias(a, b, c) textureCube(a, b, c)",
  81. "#define tex2D(a, b) texture2D(a, b)",
  82. "#define tex2DBias(a, b, c) texture2D(a, b, c)",
  83. "#endif"
  84. ].join( "\n" );
  85. var builder = new THREE.NodeBuilder( this );
  86. vertex = this.vertex.build( builder.setShader( 'vertex' ), 'v4' );
  87. fragment = this.fragment.build( builder.setShader( 'fragment' ), 'v4' );
  88. if ( this.requestAttribs.uv[ 0 ] ) {
  89. this.addVertexPars( 'varying vec2 vUv;' );
  90. this.addFragmentPars( 'varying vec2 vUv;' );
  91. this.addVertexCode( 'vUv = uv;' );
  92. }
  93. if ( this.requestAttribs.uv[ 1 ] ) {
  94. this.addVertexPars( 'varying vec2 vUv2; attribute vec2 uv2;' );
  95. this.addFragmentPars( 'varying vec2 vUv2;' );
  96. this.addVertexCode( 'vUv2 = uv2;' );
  97. }
  98. if ( this.requestAttribs.color[ 0 ] ) {
  99. this.addVertexPars( 'varying vec4 vColor; attribute vec4 color;' );
  100. this.addFragmentPars( 'varying vec4 vColor;' );
  101. this.addVertexCode( 'vColor = color;' );
  102. }
  103. if ( this.requestAttribs.color[ 1 ] ) {
  104. this.addVertexPars( 'varying vec4 vColor2; attribute vec4 color2;' );
  105. this.addFragmentPars( 'varying vec4 vColor2;' );
  106. this.addVertexCode( 'vColor2 = color2;' );
  107. }
  108. if ( this.requestAttribs.position ) {
  109. this.addVertexPars( 'varying vec3 vPosition;' );
  110. this.addFragmentPars( 'varying vec3 vPosition;' );
  111. this.addVertexCode( 'vPosition = transformed;' );
  112. }
  113. if ( this.requestAttribs.worldPosition ) {
  114. // for future update replace from the native "varying vec3 vWorldPosition" for optimization
  115. this.addVertexPars( 'varying vec3 vWPosition;' );
  116. this.addFragmentPars( 'varying vec3 vWPosition;' );
  117. this.addVertexCode( 'vWPosition = worldPosition.xyz;' );
  118. }
  119. if ( this.requestAttribs.normal ) {
  120. this.addVertexPars( 'varying vec3 vObjectNormal;' );
  121. this.addFragmentPars( 'varying vec3 vObjectNormal;' );
  122. this.addVertexCode( 'vObjectNormal = normal;' );
  123. }
  124. if ( this.requestAttribs.worldNormal ) {
  125. this.addVertexPars( 'varying vec3 vWNormal;' );
  126. this.addFragmentPars( 'varying vec3 vWNormal;' );
  127. this.addVertexCode( 'vWNormal = ( modelMatrix * vec4( objectNormal, 0.0 ) ).xyz;' );
  128. }
  129. this.lights = this.requestAttribs.light;
  130. this.transparent = this.requestAttribs.transparent || this.blendMode > THREE.NormalBlending;
  131. this.vertexShader = [
  132. this.prefixCode,
  133. this.vertexPars,
  134. this.getCodePars( this.vertexUniform, 'uniform' ),
  135. this.getIncludes( this.consts[ 'vertex' ] ),
  136. this.getIncludes( this.functions[ 'vertex' ] ),
  137. 'void main(){',
  138. this.getCodePars( this.vertexTemps ),
  139. vertex,
  140. this.vertexCode,
  141. '}'
  142. ].join( "\n" );
  143. this.fragmentShader = [
  144. this.prefixCode,
  145. this.fragmentPars,
  146. this.getCodePars( this.fragmentUniform, 'uniform' ),
  147. this.getIncludes( this.consts[ 'fragment' ] ),
  148. this.getIncludes( this.functions[ 'fragment' ] ),
  149. 'void main(){',
  150. this.getCodePars( this.fragmentTemps ),
  151. this.fragmentCode,
  152. fragment,
  153. '}'
  154. ].join( "\n" );
  155. this.needsUpdate = true;
  156. this.dispose(); // force update
  157. return this;
  158. };
  159. THREE.NodeMaterial.prototype.define = function( name, value ) {
  160. this.defines[ name ] = value == undefined ? 1 : value;
  161. };
  162. THREE.NodeMaterial.prototype.isDefined = function( name ) {
  163. return this.defines[ name ] != undefined;
  164. };
  165. THREE.NodeMaterial.prototype.mergeUniform = function( uniforms ) {
  166. for ( var name in uniforms ) {
  167. this.uniforms[ name ] = uniforms[ name ];
  168. }
  169. };
  170. THREE.NodeMaterial.prototype.createUniform = function( type, value, ns, needsUpdate ) {
  171. var index = this.uniformList.length;
  172. var uniform = {
  173. type : type,
  174. value : value,
  175. name : ns ? ns : 'nVu' + index,
  176. needsUpdate : needsUpdate
  177. };
  178. this.uniformList.push( uniform );
  179. return uniform;
  180. };
  181. THREE.NodeMaterial.prototype.getVertexTemp = function( uuid, type, ns ) {
  182. if ( ! this.vertexTemps[ uuid ] ) {
  183. var index = this.vertexTemps.length,
  184. name = ns ? ns : 'nVt' + index,
  185. data = { name : name, type : type };
  186. this.vertexTemps.push( data );
  187. this.vertexTemps[ uuid ] = data;
  188. }
  189. return this.vertexTemps[ uuid ];
  190. };
  191. THREE.NodeMaterial.prototype.getFragmentTemp = function( uuid, type, ns ) {
  192. if ( ! this.fragmentTemps[ uuid ] ) {
  193. var index = this.fragmentTemps.length,
  194. name = ns ? ns : 'nVt' + index,
  195. data = { name : name, type : type };
  196. this.fragmentTemps.push( data );
  197. this.fragmentTemps[ uuid ] = data;
  198. }
  199. return this.fragmentTemps[ uuid ];
  200. };
  201. THREE.NodeMaterial.prototype.getAttribute = function( name, type ) {
  202. if ( ! this.attributes[ name ] ) {
  203. var varName = 'nV' + name;
  204. this.addVertexPars( 'varying ' + type + ' ' + varName + ';' );
  205. this.addFragmentPars( 'varying ' + type + ' ' + varName + ';' );
  206. this.addVertexPars( 'attribute ' + type + ' ' + name + ';' );
  207. this.addVertexCode( varName + ' = ' + name + ';' );
  208. this.attributes[ name ] = { varName : varName, name : name, type : type };
  209. }
  210. return this.attributes[ name ];
  211. };
  212. THREE.NodeMaterial.prototype.getIncludes = function() {
  213. function sortByPosition( a, b ) {
  214. return a.deps.length - b.deps.length;
  215. }
  216. return function( incs ) {
  217. if ( ! incs ) return '';
  218. var code = '', incs = incs.sort( sortByPosition );
  219. for ( var i = 0; i < incs.length; i ++ ) {
  220. if ( incs[ i ].src ) code += incs[ i ].src + '\n';
  221. }
  222. return code;
  223. }
  224. }();
  225. THREE.NodeMaterial.prototype.addVertexPars = function( code ) {
  226. this.vertexPars += code + '\n';
  227. };
  228. THREE.NodeMaterial.prototype.addFragmentPars = function( code ) {
  229. this.fragmentPars += code + '\n';
  230. };
  231. THREE.NodeMaterial.prototype.addVertexCode = function( code ) {
  232. this.vertexCode += code + '\n';
  233. };
  234. THREE.NodeMaterial.prototype.addFragmentCode = function( code ) {
  235. this.fragmentCode += code + '\n';
  236. };
  237. THREE.NodeMaterial.prototype.addVertexNode = function( code ) {
  238. this.vertexNode += code + '\n';
  239. };
  240. THREE.NodeMaterial.prototype.clearVertexNode = function() {
  241. var code = this.vertexNode;
  242. this.vertexNode = '';
  243. return code;
  244. };
  245. THREE.NodeMaterial.prototype.addFragmentNode = function( code ) {
  246. this.fragmentNode += code + '\n';
  247. };
  248. THREE.NodeMaterial.prototype.clearFragmentNode = function() {
  249. var code = this.fragmentNode;
  250. this.fragmentNode = '';
  251. return code;
  252. };
  253. THREE.NodeMaterial.prototype.getCodePars = function( pars, prefix ) {
  254. prefix = prefix || '';
  255. var code = '';
  256. for ( var i = 0, l = pars.length; i < l; ++ i ) {
  257. var parsType = pars[ i ].type;
  258. var parsName = pars[ i ].name;
  259. var parsValue = pars[ i ].value;
  260. if ( parsType == 't' && parsValue instanceof THREE.CubeTexture ) parsType = 'tc';
  261. var type = THREE.NodeMaterial.types[ parsType ];
  262. if ( type == undefined ) throw new Error( "Node pars " + parsType + " not found." );
  263. code += prefix + ' ' + type + ' ' + parsName + ';\n';
  264. }
  265. return code;
  266. };
  267. THREE.NodeMaterial.prototype.createVertexUniform = function( type, value, ns, needsUpdate ) {
  268. var uniform = this.createUniform( type, value, ns, needsUpdate );
  269. this.vertexUniform.push( uniform );
  270. this.vertexUniform[ uniform.name ] = uniform;
  271. this.uniforms[ uniform.name ] = uniform;
  272. return uniform;
  273. };
  274. THREE.NodeMaterial.prototype.createFragmentUniform = function( type, value, ns, needsUpdate ) {
  275. var uniform = this.createUniform( type, value, ns, needsUpdate );
  276. this.fragmentUniform.push( uniform );
  277. this.fragmentUniform[ uniform.name ] = uniform;
  278. this.uniforms[ uniform.name ] = uniform;
  279. return uniform;
  280. };
  281. THREE.NodeMaterial.prototype.getDataNode = function( uuid ) {
  282. return this.nodeData[ uuid ] = this.nodeData[ uuid ] || {};
  283. };
  284. THREE.NodeMaterial.prototype.include = function( builder, node, parent, source ) {
  285. var includes;
  286. node = typeof node === 'string' ? THREE.NodeLib.get( node ) : node;
  287. if ( node instanceof THREE.FunctionNode ) {
  288. includes = this.functions[ builder.shader ] = this.functions[ builder.shader ] || [];
  289. } else if ( node instanceof THREE.ConstNode ) {
  290. includes = this.consts[ builder.shader ] = this.consts[ builder.shader ] || [];
  291. }
  292. var included = includes[ node.name ];
  293. if ( ! included ) {
  294. included = includes[ node.name ] = {
  295. node : node,
  296. deps : []
  297. };
  298. includes.push( included );
  299. included.src = node.build( builder, 'source' );
  300. }
  301. if ( node instanceof THREE.FunctionNode && parent && includes[ parent.name ].deps.indexOf( node ) == - 1 ) {
  302. includes[ parent.name ].deps.push( node );
  303. if ( node.includes && node.includes.length ) {
  304. var i = 0;
  305. do {
  306. this.include( builder, node.includes[ i ++ ], parent );
  307. } while ( i < node.includes.length );
  308. }
  309. }
  310. if ( source ) {
  311. included.src = source;
  312. }
  313. };